faiss 0.6.4 → 0.6.5

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/ext/faiss/extconf.rb +1 -1
  4. data/lib/faiss/version.rb +1 -1
  5. data/vendor/faiss/faiss/AutoTune.cpp +175 -0
  6. data/vendor/faiss/faiss/Clustering.cpp +150 -60
  7. data/vendor/faiss/faiss/Clustering.h +29 -0
  8. data/vendor/faiss/faiss/IVFlib.cpp +99 -0
  9. data/vendor/faiss/faiss/IVFlib.h +15 -0
  10. data/vendor/faiss/faiss/Index.cpp +44 -0
  11. data/vendor/faiss/faiss/Index.h +19 -14
  12. data/vendor/faiss/faiss/IndexBinary.cpp +8 -0
  13. data/vendor/faiss/faiss/IndexBinary.h +8 -0
  14. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +82 -4
  15. data/vendor/faiss/faiss/IndexBinaryHNSW.h +19 -0
  16. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +34 -0
  17. data/vendor/faiss/faiss/IndexFastScan.cpp +33 -17
  18. data/vendor/faiss/faiss/IndexFastScan.h +6 -3
  19. data/vendor/faiss/faiss/IndexFlat.cpp +56 -0
  20. data/vendor/faiss/faiss/IndexFlat.h +31 -0
  21. data/vendor/faiss/faiss/IndexFlatCodes.cpp +6 -1
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +456 -10
  23. data/vendor/faiss/faiss/IndexHNSW.h +38 -0
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +32 -0
  25. data/vendor/faiss/faiss/IndexIDMap.h +7 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +281 -31
  27. data/vendor/faiss/faiss/IndexIVF.h +53 -1
  28. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +46 -14
  29. data/vendor/faiss/faiss/IndexIVFFastScan.h +4 -2
  30. data/vendor/faiss/faiss/IndexIVFFlat.cpp +33 -0
  31. data/vendor/faiss/faiss/IndexIVFFlat.h +16 -0
  32. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +1 -0
  33. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +3 -20
  34. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +50 -14
  35. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -2
  36. data/vendor/faiss/faiss/IndexIVFSQFastScan.cpp +855 -0
  37. data/vendor/faiss/faiss/IndexIVFSQFastScan.h +145 -0
  38. data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -3
  39. data/vendor/faiss/faiss/IndexPiPNN.cpp +201 -0
  40. data/vendor/faiss/faiss/IndexPiPNN.h +83 -0
  41. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +6 -3
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +10 -4
  43. data/vendor/faiss/faiss/IndexReplicas.cpp +20 -0
  44. data/vendor/faiss/faiss/IndexReplicas.h +9 -0
  45. data/vendor/faiss/faiss/IndexSQFastScan.cpp +648 -0
  46. data/vendor/faiss/faiss/IndexSQFastScan.h +129 -0
  47. data/vendor/faiss/faiss/IndexShards.cpp +10 -0
  48. data/vendor/faiss/faiss/IndexShards.h +5 -0
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +57 -15
  50. data/vendor/faiss/faiss/SuperKMeans.h +9 -4
  51. data/vendor/faiss/faiss/VectorTransform.cpp +3 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +24 -0
  53. data/vendor/faiss/faiss/gpu/Dataset.h +112 -0
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +16 -13
  55. data/vendor/faiss/faiss/gpu/GpuCloner.h +4 -0
  56. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -13
  58. data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +26 -13
  59. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +45 -22
  60. data/vendor/faiss/faiss/gpu/GpuIndexFlat.h +11 -0
  61. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
  62. data/vendor/faiss/faiss/gpu/test/TestGpuDatasetHeader.cpp +18 -0
  63. data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +215 -0
  64. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +98 -0
  65. data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +11 -0
  66. data/vendor/faiss/faiss/gpu/utils/StackDeviceMemory.cpp +1 -1
  67. data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +11 -0
  68. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +92 -0
  69. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +4 -0
  70. data/vendor/faiss/faiss/gpu_metal/MetalIndexBinaryFlat.h +69 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +38 -0
  72. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +106 -0
  73. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +10 -0
  74. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -0
  75. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -6
  76. data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +134 -45
  77. data/vendor/faiss/faiss/impl/ClusteringHelpers.h +36 -0
  78. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +286 -136
  79. data/vendor/faiss/faiss/impl/DistanceComputer.cpp +63 -0
  80. data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -29
  81. data/vendor/faiss/faiss/impl/HNSW.cpp +46 -10
  82. data/vendor/faiss/faiss/impl/HNSW.h +72 -0
  83. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +6 -19
  84. data/vendor/faiss/faiss/impl/NNDescent.cpp +2 -1
  85. data/vendor/faiss/faiss/impl/NSG.cpp +3 -2
  86. data/vendor/faiss/faiss/impl/Panorama.h +3 -2
  87. data/vendor/faiss/faiss/impl/PiPNN.cpp +822 -0
  88. data/vendor/faiss/faiss/impl/PiPNN.h +122 -0
  89. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +54 -11
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.h +1 -1
  91. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +81 -57
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +15 -0
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +29 -0
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -6
  95. data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +0 -4
  96. data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +0 -4
  97. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +5 -2
  98. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +0 -4
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +0 -4
  100. data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +0 -4
  101. data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +0 -4
  102. data/vendor/faiss/faiss/impl/expanded_scanners.h +88 -0
  103. data/vendor/faiss/faiss/impl/fast_scan/FastScanDistancePostProcessing.h +4 -0
  104. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +61 -64
  105. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +13 -14
  106. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +17 -129
  107. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +25 -19
  108. data/vendor/faiss/faiss/impl/fast_scan/fast_scan.cpp +5 -0
  109. data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +0 -4
  110. data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +0 -4
  111. data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +0 -88
  112. data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +0 -4
  113. data/vendor/faiss/faiss/impl/fast_scan/kernels_neon.h +155 -0
  114. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +22 -1
  115. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd512.h +3 -2
  116. data/vendor/faiss/faiss/impl/fast_scan/simd_result_handlers.h +41 -23
  117. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +0 -4
  118. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +0 -4
  119. data/vendor/faiss/faiss/impl/index_read.cpp +172 -0
  120. data/vendor/faiss/faiss/impl/index_write.cpp +95 -0
  121. data/vendor/faiss/faiss/impl/pipnn/HashPrune.cpp +159 -0
  122. data/vendor/faiss/faiss/impl/pipnn/HashPrune.h +69 -0
  123. data/vendor/faiss/faiss/impl/pipnn/Partition.cpp +587 -0
  124. data/vendor/faiss/faiss/impl/pipnn/Partition.h +93 -0
  125. data/vendor/faiss/faiss/impl/pipnn/kernels.cpp +326 -0
  126. data/vendor/faiss/faiss/impl/pipnn/kernels.h +92 -0
  127. data/vendor/faiss/faiss/impl/platform_macros.h +45 -0
  128. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +0 -4
  129. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +79 -4
  130. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +6 -0
  131. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +0 -4
  132. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +0 -4
  133. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +0 -4
  134. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx2.h +4 -0
  135. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx512.h +4 -1
  136. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +19 -0
  137. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +6 -6
  138. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +131 -6
  139. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +0 -4
  140. data/vendor/faiss/faiss/impl/pq_code_distance/pq_rvv_kernel.h +89 -0
  141. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +21 -10
  142. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +45 -18
  143. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +2 -1
  144. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +0 -4
  145. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +0 -4
  146. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +0 -4
  147. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +0 -4
  148. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +5 -1
  149. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +28 -52
  150. data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +59 -11
  151. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +226 -77
  152. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +482 -103
  153. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +2 -149
  154. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +3 -698
  155. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +66 -57
  156. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +101 -96
  157. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +3185 -144
  158. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_coefficients.h +48 -0
  159. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_rvv_kernel.h +546 -0
  160. data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +2 -2
  161. data/vendor/faiss/faiss/impl/simd_dispatch.h +7 -0
  162. data/vendor/faiss/faiss/impl/simdlib/simdlib_avx2.h +14 -0
  163. data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +7 -8
  164. data/vendor/faiss/faiss/impl/sq_fastscan_utils.h +232 -0
  165. data/vendor/faiss/faiss/index_factory.cpp +76 -0
  166. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +3 -1
  167. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +3 -2
  168. data/vendor/faiss/faiss/utils/AlignedTable.h +6 -1
  169. data/vendor/faiss/faiss/utils/Heap.cpp +4 -3
  170. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +16 -5
  171. data/vendor/faiss/faiss/utils/bf16.h +36 -0
  172. data/vendor/faiss/faiss/utils/distances.cpp +226 -15
  173. data/vendor/faiss/faiss/utils/distances.h +34 -0
  174. data/vendor/faiss/faiss/utils/distances_dispatch.h +6 -4
  175. data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +2 -5
  176. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +0 -14
  177. data/vendor/faiss/faiss/utils/distances_fused/rvv.cpp +25 -0
  178. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +0 -4
  179. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +0 -4
  180. data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +2 -1
  181. data/vendor/faiss/faiss/utils/distances_simd.cpp +9 -0
  182. data/vendor/faiss/faiss/utils/fp16-fp16c.h +4 -0
  183. data/vendor/faiss/faiss/utils/fp16-inl.h +26 -52
  184. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +0 -4
  185. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +0 -4
  186. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +0 -4
  187. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +33 -5
  188. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h +15 -30
  189. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +0 -4
  190. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +2 -1
  191. data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +0 -4
  192. data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +0 -4
  193. data/vendor/faiss/faiss/utils/pq_code_distance.h +8 -4
  194. data/vendor/faiss/faiss/utils/prefetch.h +1 -26
  195. data/vendor/faiss/faiss/utils/quantize_lut.cpp +26 -27
  196. data/vendor/faiss/faiss/utils/quantize_lut.h +11 -0
  197. data/vendor/faiss/faiss/utils/rabitq_simd.h +51 -16
  198. data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +9 -4
  199. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +37 -5
  200. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +64 -5
  201. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +64 -5
  202. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +353 -71
  203. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv_kernel.h +68 -0
  204. data/vendor/faiss/faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h +93 -0
  205. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels.h +76 -0
  206. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx2.cpp +52 -0
  207. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx512.cpp +52 -0
  208. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_neon.cpp +48 -0
  209. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx2.h +44 -0
  210. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx512.h +43 -0
  211. data/vendor/faiss/faiss/utils/simd_impl/masked_sum.h +38 -0
  212. data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +0 -4
  213. data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +0 -4
  214. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +54 -15
  215. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +38 -21
  216. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_vpopcnt.cpp +51 -14
  217. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +297 -10
  218. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +0 -4
  219. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +0 -4
  220. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +0 -4
  221. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +0 -4
  222. data/vendor/faiss/faiss/utils/simd_levels.cpp +19 -3
  223. data/vendor/faiss/faiss/utils/simd_levels.h +74 -1
  224. data/vendor/faiss/faiss/utils/transpose/transpose-avx2-inl.h +0 -4
  225. data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +0 -4
  226. data/vendor/faiss/faiss/utils/utils.cpp +80 -0
  227. data/vendor/faiss/faiss/utils/utils.h +24 -0
  228. metadata +33 -1
@@ -21,6 +21,7 @@
21
21
  #include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
22
22
  #include <faiss/impl/fast_scan/fast_scan.h>
23
23
  #include <faiss/impl/fast_scan/simd_result_handlers.h>
24
+ #include <faiss/impl/platform_macros.h>
24
25
  #include <faiss/invlists/BlockInvertedLists.h>
25
26
  #include <faiss/utils/hamming.h>
26
27
  #include <faiss/utils/quantize_lut.h>
@@ -646,8 +647,23 @@ void IndexIVFFastScan::search_dispatch_implem(
646
647
  search_implem_14(
647
648
  n, x, k, distances, labels, cq, impl, context, params);
648
649
  } else {
650
+ // Size each thread's LUT here, on the calling thread: a
651
+ // heap-profiler sample taken on an OpenMP worker unwinds
652
+ // through libomp, which is very slow on aarch64. The tables
653
+ // keep their capacity, so smaller slices do not reallocate.
654
+ std::vector<AlignedTable<uint8_t>> thread_dis_tables(
655
+ omp_get_max_threads());
656
+ const size_t max_slice_n = (n + nslice - 1) / nslice;
657
+ const size_t lut_per_query =
658
+ ksub * M2 * (lookup_table_is_3d() ? cur_nprobe : 1);
659
+ for (auto& t : thread_dis_tables) {
660
+ t.keep_capacity = true;
661
+ t.resize(max_slice_n * lut_per_query);
662
+ }
649
663
  #pragma omp parallel for reduction(+ : ndis, nlist_visited)
650
664
  for (int slice = 0; slice < nslice; slice++) {
665
+ AlignedTable<uint8_t>* dis_tables_buf =
666
+ &thread_dis_tables.at(omp_get_thread_num());
651
667
  idx_t i0 = n * slice / nslice;
652
668
  idx_t i1 = n * (slice + 1) / nslice;
653
669
  float* dis_i = distances + i0 * k;
@@ -663,6 +679,9 @@ void IndexIVFFastScan::search_dispatch_implem(
663
679
  if (thread_context.query_factors != nullptr) {
664
680
  thread_context.query_factors += i0 * cur_nprobe;
665
681
  }
682
+ if (thread_context.rotated_q != nullptr) {
683
+ thread_context.rotated_q += i0 * cur_nprobe * d;
684
+ }
666
685
 
667
686
  auto scanner = make_knn_scanner(
668
687
  is_max,
@@ -684,7 +703,8 @@ void IndexIVFFastScan::search_dispatch_implem(
684
703
  &nlist_visited,
685
704
  thread_context,
686
705
  params,
687
- *scanner);
706
+ *scanner,
707
+ dis_tables_buf);
688
708
  } else {
689
709
  search_implem_10(
690
710
  i1 - i0,
@@ -696,7 +716,8 @@ void IndexIVFFastScan::search_dispatch_implem(
696
716
  &nlist_visited,
697
717
  thread_context,
698
718
  params,
699
- *scanner);
719
+ *scanner,
720
+ dis_tables_buf);
700
721
  }
701
722
  }
702
723
  }
@@ -1020,9 +1041,12 @@ void IndexIVFFastScan::search_implem_10(
1020
1041
  size_t* nlist_out,
1021
1042
  const FastScanDistancePostProcessing& context,
1022
1043
  const IVFSearchParameters* params,
1023
- FastScanCodeScanner& scanner) const {
1044
+ FastScanCodeScanner& scanner,
1045
+ AlignedTable<uint8_t>* dis_tables_buf) const {
1024
1046
  size_t dim12 = ksub * M2;
1025
- AlignedTable<uint8_t> dis_tables;
1047
+ AlignedTable<uint8_t> local_dis_tables;
1048
+ AlignedTable<uint8_t>& dis_tables =
1049
+ dis_tables_buf ? *dis_tables_buf : local_dis_tables;
1026
1050
  AlignedTable<uint16_t> biases;
1027
1051
  std::unique_ptr<float[]> normalizers(new float[2 * n]);
1028
1052
 
@@ -1060,6 +1084,7 @@ void IndexIVFFastScan::search_implem_10(
1060
1084
  // Allocate probe_map once and reuse it
1061
1085
  std::vector<int> probe_map;
1062
1086
  probe_map.reserve(1);
1087
+ const size_t block_stride = get_block_stride();
1063
1088
 
1064
1089
  for (idx_t i = 0; i < n; i++) {
1065
1090
  const uint8_t* LUT = nullptr;
@@ -1134,7 +1159,7 @@ void IndexIVFFastScan::search_implem_10(
1134
1159
  codes.get(),
1135
1160
  LUT,
1136
1161
  context.pq2x4_scale,
1137
- get_block_stride());
1162
+ block_stride);
1138
1163
 
1139
1164
  ndis += ls;
1140
1165
  nlist_visited++;
@@ -1172,14 +1197,17 @@ void IndexIVFFastScan::search_implem_12(
1172
1197
  size_t* nlist_out,
1173
1198
  const FastScanDistancePostProcessing& context,
1174
1199
  const IVFSearchParameters* /* params */,
1175
- FastScanCodeScanner& scanner) const {
1200
+ FastScanCodeScanner& scanner,
1201
+ AlignedTable<uint8_t>* dis_tables_buf) const {
1176
1202
  if (n == 0) { // does not work well with reservoir
1177
1203
  return;
1178
1204
  }
1179
1205
  FAISS_THROW_IF_NOT(bbs == 32);
1180
1206
 
1181
1207
  size_t dim12 = ksub * M2;
1182
- AlignedTable<uint8_t> dis_tables;
1208
+ AlignedTable<uint8_t> local_dis_tables;
1209
+ AlignedTable<uint8_t>& dis_tables =
1210
+ dis_tables_buf ? *dis_tables_buf : local_dis_tables;
1183
1211
  AlignedTable<uint16_t> biases;
1184
1212
  std::unique_ptr<float[]> normalizers(new float[2 * n]);
1185
1213
 
@@ -1196,6 +1224,7 @@ void IndexIVFFastScan::search_implem_12(
1196
1224
  size_t cur_nprobe = cq.nprobe;
1197
1225
 
1198
1226
  std::vector<QC> qcs;
1227
+ qcs.reserve(n * cur_nprobe);
1199
1228
  {
1200
1229
  size_t ij = 0;
1201
1230
  for (idx_t i = 0; i < n; i++) {
@@ -1226,6 +1255,9 @@ void IndexIVFFastScan::search_implem_12(
1226
1255
  // Allocate vectors once and reuse them
1227
1256
  std::vector<int> probe_map;
1228
1257
  probe_map.reserve(actual_qbs2);
1258
+ std::vector<int> q_map(actual_qbs2), lut_entries(actual_qbs2);
1259
+ AlignedTable<uint8_t> LUT(actual_qbs2 * dim12);
1260
+ const size_t block_stride = get_block_stride();
1229
1261
 
1230
1262
  size_t i0 = 0;
1231
1263
  uint64_t t_copy_pack = 0, t_scan = 0;
@@ -1252,8 +1284,6 @@ void IndexIVFFastScan::search_implem_12(
1252
1284
  // re-organize LUTs and biases into the right order
1253
1285
  int nc = static_cast<int>(i1 - i0);
1254
1286
 
1255
- std::vector<int> q_map(nc), lut_entries(nc);
1256
- AlignedTable<uint8_t> LUT(nc * dim12);
1257
1287
  memset(LUT.get(), -1, nc * dim12);
1258
1288
  int qbs_for_list = pq4_preferred_qbs(nc);
1259
1289
 
@@ -1303,7 +1333,7 @@ void IndexIVFFastScan::search_implem_12(
1303
1333
  codes.get(),
1304
1334
  LUT.get(),
1305
1335
  context.pq2x4_scale,
1306
- get_block_stride());
1336
+ block_stride);
1307
1337
  // prepare for next loop
1308
1338
  i0 = i1;
1309
1339
  }
@@ -1351,6 +1381,7 @@ void IndexIVFFastScan::search_implem_14(
1351
1381
  size_t cur_nprobe = cq.nprobe;
1352
1382
 
1353
1383
  std::vector<QC> qcs;
1384
+ qcs.reserve(n * cur_nprobe);
1354
1385
  {
1355
1386
  size_t ij = 0;
1356
1387
  for (idx_t i = 0; i < n; i++) {
@@ -1461,8 +1492,11 @@ void IndexIVFFastScan::search_implem_14(
1461
1492
  // Allocate probe_map once per thread and reuse it
1462
1493
  std::vector<int> probe_map;
1463
1494
  probe_map.reserve(actual_qbs2);
1495
+ std::vector<int> q_map(actual_qbs2), lut_entries(actual_qbs2);
1496
+ AlignedTable<uint8_t> LUT(actual_qbs2 * dim12);
1497
+ const size_t block_stride = get_block_stride();
1464
1498
 
1465
- #pragma omp for schedule(dynamic)
1499
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
1466
1500
  for (idx_t cluster = 0; cluster < static_cast<idx_t>(ses.size());
1467
1501
  cluster++) {
1468
1502
  size_t i0 = ses[cluster].start;
@@ -1474,8 +1508,6 @@ void IndexIVFFastScan::search_implem_14(
1474
1508
  // re-organize LUTs and biases into the right order
1475
1509
  int nc = static_cast<int>(i1 - i0);
1476
1510
 
1477
- std::vector<int> q_map(nc), lut_entries(nc);
1478
- AlignedTable<uint8_t> LUT(nc * dim12);
1479
1511
  memset(LUT.get(), -1, nc * dim12);
1480
1512
  int qbs_for_list = pq4_preferred_qbs(nc);
1481
1513
 
@@ -1526,7 +1558,7 @@ void IndexIVFFastScan::search_implem_14(
1526
1558
  codes.get(),
1527
1559
  LUT.get(),
1528
1560
  context.pq2x4_scale,
1529
- get_block_stride());
1561
+ block_stride);
1530
1562
  }
1531
1563
 
1532
1564
  // labels is in-place for HeapHC
@@ -303,7 +303,8 @@ struct IndexIVFFastScan : IndexIVF {
303
303
  size_t* nlist_out,
304
304
  const FastScanDistancePostProcessing& context,
305
305
  const IVFSearchParameters* params,
306
- FastScanCodeScanner& scanner) const;
306
+ FastScanCodeScanner& scanner,
307
+ AlignedTable<uint8_t>* dis_tables_buf = nullptr) const;
307
308
 
308
309
  void search_implem_12(
309
310
  idx_t n,
@@ -314,7 +315,8 @@ struct IndexIVFFastScan : IndexIVF {
314
315
  size_t* nlist_out,
315
316
  const FastScanDistancePostProcessing& context,
316
317
  const IVFSearchParameters* params,
317
- FastScanCodeScanner& scanner) const;
318
+ FastScanCodeScanner& scanner,
319
+ AlignedTable<uint8_t>* dis_tables_buf = nullptr) const;
318
320
 
319
321
  // implem 14 is multithreaded internally across nprobes and queries
320
322
  void search_implem_14(
@@ -57,6 +57,25 @@ IndexIVFFlat::IndexIVFFlat() {
57
57
  by_residual = false;
58
58
  }
59
59
 
60
+ void IndexIVFFlat::train_encoded(
61
+ idx_t n,
62
+ const uint8_t* x,
63
+ const Index* codec) {
64
+ FAISS_THROW_IF_NOT_MSG(codec, "encoded training requires a codec");
65
+ train_q1_encoded(n, x, codec, verbose, metric_type);
66
+ is_trained = true;
67
+ }
68
+
69
+ void IndexIVFFlat::train_ex(idx_t n, const void* x, NumericType numeric_type) {
70
+ if (numeric_type != NumericType::Float16) {
71
+ IndexIVF::train_ex(n, x, numeric_type);
72
+ return;
73
+ }
74
+ FAISS_THROW_IF_NOT_MSG(x, "training data must not be null");
75
+ train_q1_ex(n, x, numeric_type, verbose, metric_type);
76
+ is_trained = true;
77
+ }
78
+
60
79
  void IndexIVFFlat::add_core(
61
80
  idx_t n,
62
81
  const float* x,
@@ -186,6 +205,20 @@ IndexIVFFlatDedup::IndexIVFFlatDedup(
186
205
  metric_type_in,
187
206
  own_invlists_in) {}
188
207
 
208
+ void IndexIVFFlatDedup::train_encoded(idx_t, const uint8_t*, const Index*) {
209
+ FAISS_THROW_MSG("encoded training is not supported by IndexIVFFlatDedup");
210
+ }
211
+
212
+ void IndexIVFFlatDedup::train_ex(
213
+ idx_t n,
214
+ const void* x,
215
+ NumericType numeric_type) {
216
+ FAISS_THROW_IF_NOT_MSG(
217
+ numeric_type == NumericType::Float32,
218
+ "only fp32 training is supported by IndexIVFFlatDedup");
219
+ train(n, static_cast<const float*>(x));
220
+ }
221
+
189
222
  void IndexIVFFlatDedup::train(idx_t n, const float* x) {
190
223
  std::unordered_map<uint64_t, idx_t> map;
191
224
  std::unique_ptr<float[]> x2(new float[n * d]);
@@ -29,6 +29,16 @@ struct IndexIVFFlat : IndexIVF {
29
29
  MetricType = METRIC_L2,
30
30
  bool own_invlists = true);
31
31
 
32
+ /** Train the coarse quantizer from vectors decoded on demand by `codec`.
33
+ *
34
+ * IndexIVFFlat has no trainable residual encoder, so this avoids a
35
+ * corpus-sized decoded buffer. The codec must remain alive for this call.
36
+ */
37
+ void train_encoded(idx_t n, const uint8_t* x, const Index* codec) override;
38
+
39
+ /// Float16 trains only the coarse quantizer, natively on the fp16 rows.
40
+ void train_ex(idx_t n, const void* x, NumericType numeric_type) override;
41
+
32
42
  void add_core(
33
43
  idx_t n,
34
44
  const float* x,
@@ -110,6 +120,12 @@ struct IndexIVFFlatDedup : IndexIVFFlat {
110
120
  /// also dedups the training set
111
121
  void train(idx_t n, const float* x) override;
112
122
 
123
+ /// Encoded training cannot preserve the byte-exact deduplication contract.
124
+ void train_encoded(idx_t n, const uint8_t* x, const Index* codec) override;
125
+
126
+ /// Non-fp32 input is rejected for the same reason as train_encoded.
127
+ void train_ex(idx_t n, const void* x, NumericType numeric_type) override;
128
+
113
129
  /// implemented for all IndexIVF* classes
114
130
  void add_with_ids(idx_t n, const float* x, const idx_t* xids) override;
115
131
 
@@ -334,6 +334,7 @@ struct IVFPQFastScanScanner : InvertedListScanner {
334
334
  const IDSelector* sel_in)
335
335
  : InvertedListScanner(store_pairs_in, sel_in), index(index_in) {
336
336
  this->keep_max = is_similarity_metric(index_in.metric_type);
337
+ this->code_size = index_in.code_size;
337
338
  residual.resize(index_in.d);
338
339
  }
339
340
 
@@ -162,8 +162,7 @@ struct RaBitInvertedListScanner : InvertedListScanner {
162
162
  std::vector<float> query_vector;
163
163
 
164
164
  std::unique_ptr<FlatCodesDistanceComputer> dc;
165
- RaBitQDistanceComputer* rabitq_dc =
166
- nullptr; // For multi-bit adaptive filtering
165
+ RaBitQDistanceComputer* rabitq_dc = nullptr;
167
166
 
168
167
  uint8_t qb = 0;
169
168
  bool centered = false;
@@ -205,30 +204,15 @@ struct RaBitInvertedListScanner : InvertedListScanner {
205
204
  return dc->distance_to_code(code);
206
205
  }
207
206
 
208
- // redefiniing the scan_codes allows to inline the distance_to_code
209
- // (this is unlikely to matter because it contains a virtual function call)
210
- size_t scan_codes_1bit(
211
- size_t list_size,
212
- const uint8_t* codes,
213
- const idx_t* ids,
214
- ResultHandler& handler) const {
215
- return run_scan_codes(*this, list_size, codes, ids, handler);
216
- }
217
-
218
- /// Override scan_codes to implement adaptive filtering for multi-bit codes
219
207
  size_t scan_codes(
220
208
  size_t list_size,
221
209
  const uint8_t* codes,
222
210
  const idx_t* ids,
223
211
  ResultHandler& handler) const override {
224
- size_t ex_bits = ivf_rabitq.rabitq.nb_bits - 1;
225
-
226
- // For 1-bit codes, use default implementation
227
- if (ex_bits == 0 || rabitq_dc == nullptr) {
228
- return scan_codes_1bit(list_size, codes, ids, handler);
212
+ if (qb == 0 && ivf_rabitq.rabitq.nb_bits == 1) {
213
+ return run_scan_codes(*this, list_size, codes, ids, handler);
229
214
  }
230
215
 
231
- // Multi-bit: Two-stage search with adaptive filtering
232
216
  return rabitq_dc->scan_codes_multibit(
233
217
  list_size,
234
218
  codes,
@@ -247,7 +231,6 @@ struct RaBitInvertedListScanner : InvertedListScanner {
247
231
  if (!dc) {
248
232
  dc.reset(ivf_rabitq.rabitq.get_distance_computer(
249
233
  qb, nullptr, centered));
250
- // Try to cast to RaBitQDistanceComputer for multi-bit support
251
234
  rabitq_dc = dynamic_cast<RaBitQDistanceComputer*>(dc.get());
252
235
  FAISS_THROW_IF_NOT(rabitq_dc);
253
236
  }
@@ -19,9 +19,11 @@
19
19
  #include <faiss/impl/ResultHandler.h>
20
20
  #include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
21
21
  #include <faiss/impl/fast_scan/fast_scan.h>
22
+ #include <faiss/impl/platform_macros.h>
22
23
  #include <faiss/impl/simd_dispatch.h>
23
24
  #include <faiss/invlists/BlockInvertedLists.h>
24
25
  #include <faiss/utils/distances.h>
26
+ #include <faiss/utils/quantize_lut.h>
25
27
  #include <faiss/utils/rabitq_simd.h>
26
28
  #include <faiss/utils/utils.h>
27
29
 
@@ -401,12 +403,14 @@ void IndexIVFRaBitQFastScan::compute_residual_LUT(
401
403
  uint8_t qb_param,
402
404
  bool centered_param,
403
405
  std::vector<float>& rotated_q,
404
- std::vector<float>& centroid_buf) const {
406
+ std::vector<float>& centroid_buf,
407
+ std::vector<uint8_t>& rotated_qq,
408
+ float* rotated_q_out) const {
405
409
  const size_t d_val = static_cast<size_t>(d);
406
410
  FAISS_THROW_IF_NOT(d_val > 0);
407
411
  rotated_q.resize(d_val);
408
412
  centroid_buf.resize(d_val);
409
- std::vector<uint8_t> rotated_qq(d_val);
413
+ rotated_qq.resize(d_val);
410
414
 
411
415
  // Compute residual
412
416
  quantizer->reconstruct(centroid_id, centroid_buf.data());
@@ -414,7 +418,9 @@ void IndexIVFRaBitQFastScan::compute_residual_LUT(
414
418
  rotated_q[i] = query[i] - centroid_buf[i];
415
419
  }
416
420
 
417
- // Compute query factors using shared utility
421
+ // Keep the caller's rotated_q capacity across the assignment below, so a
422
+ // reused QueryFactorsData does not reallocate on every call.
423
+ std::vector<float> rotated_q_keep = std::move(query_factors.rotated_q);
418
424
  query_factors = rabitq_utils::compute_query_factors(
419
425
  rotated_q.data(),
420
426
  d_val,
@@ -432,7 +438,12 @@ void IndexIVFRaBitQFastScan::compute_residual_LUT(
432
438
  }
433
439
 
434
440
  if (rabitq.nb_bits > 1) {
435
- query_factors.rotated_q = rotated_q;
441
+ if (rotated_q_out != nullptr) {
442
+ std::copy(rotated_q.begin(), rotated_q.end(), rotated_q_out);
443
+ } else {
444
+ rotated_q_keep.assign(rotated_q.begin(), rotated_q.end());
445
+ query_factors.rotated_q = std::move(rotated_q_keep);
446
+ }
436
447
  }
437
448
 
438
449
  // Build LUT using branchless subset-sum construction
@@ -528,8 +539,15 @@ void IndexIVFRaBitQFastScan::search_preassigned(
528
539
  }
529
540
 
530
541
  std::vector<QueryFactorsData> query_factors_storage(n * cur_nprobe);
542
+ // One block for the multi-bit rotated queries, allocated here rather than
543
+ // per (query, probe) on OpenMP workers: under jemalloc heap profiling on
544
+ // aarch64, worker-thread allocations are very expensive to sample.
545
+ // Left uninitialised; every slot read is written by compute_LUT first.
546
+ std::unique_ptr<float[]> rotated_q_storage(
547
+ rabitq.nb_bits > 1 ? new float[n * cur_nprobe * d] : nullptr);
531
548
  FastScanDistancePostProcessing context;
532
549
  context.query_factors = query_factors_storage.data();
550
+ context.rotated_q = rotated_q_storage.get();
533
551
  context.nprobe = cur_nprobe;
534
552
  context.qb = used_qb;
535
553
  context.centered = used_centered;
@@ -570,6 +588,7 @@ void IndexIVFRaBitQFastScan::compute_LUT(
570
588
  {
571
589
  std::vector<float> rotated_q(d);
572
590
  std::vector<float> centroid_buf(d);
591
+ std::vector<uint8_t> rotated_qq(d);
573
592
 
574
593
  #pragma omp for
575
594
  for (idx_t ij = 0; ij < static_cast<idx_t>(n * cq_nprobe); ij++) {
@@ -587,7 +606,10 @@ void IndexIVFRaBitQFastScan::compute_LUT(
587
606
  used_qb,
588
607
  used_centered,
589
608
  rotated_q,
590
- centroid_buf);
609
+ centroid_buf,
610
+ rotated_qq,
611
+ context.rotated_q ? context.rotated_q + ij * d
612
+ : nullptr);
591
613
 
592
614
  if (context.query_factors != nullptr) {
593
615
  context.query_factors[ij] = std::move(query_factors_data);
@@ -628,10 +650,12 @@ void IndexIVFRaBitQFastScan::compute_LUT_uint8(
628
650
  AlignedTable<float> lut_float(cur_nprobe * dim12);
629
651
  std::vector<float> rotated_q(d);
630
652
  std::vector<float> centroid_buf(d);
653
+ std::vector<uint8_t> rotated_qq(d);
631
654
  std::vector<float> all_mins(cur_nprobe * M);
632
655
  std::vector<float> probe_b(cur_nprobe);
656
+ std::vector<float> probe_span(cur_nprobe);
633
657
 
634
- #pragma omp for schedule(dynamic)
658
+ #pragma omp for schedule(FAISS_OMP_DYNAMIC)
635
659
  for (int64_t i = 0; i < static_cast<int64_t>(n); i++) {
636
660
  const float* xi = x + i * d;
637
661
 
@@ -650,10 +674,13 @@ void IndexIVFRaBitQFastScan::compute_LUT_uint8(
650
674
  used_qb,
651
675
  used_centered,
652
676
  rotated_q,
653
- centroid_buf);
677
+ centroid_buf,
678
+ rotated_qq,
679
+ context.rotated_q ? context.rotated_q + ij * d
680
+ : nullptr);
654
681
 
655
682
  if (context.query_factors != nullptr) {
656
- context.query_factors[ij] = qf;
683
+ context.query_factors[ij] = std::move(qf);
657
684
  }
658
685
  } else {
659
686
  memset(lut_float.get() + j * dim12,
@@ -686,13 +713,18 @@ void IndexIVFRaBitQFastScan::compute_LUT_uint8(
686
713
  span_j += span;
687
714
  }
688
715
  probe_b[j2] = b_j;
689
- glob_max_dis = std::max(glob_max_dis, span_j);
716
+ probe_span[j2] = span_j;
690
717
  glob_b = std::min(glob_b, b_j);
691
718
  }
692
719
 
693
- a = std::min(
694
- 255.0f / glob_max_span,
695
- 65535.0f / glob_max_dis);
720
+ for (size_t j2 = 0; j2 < cur_nprobe; j2++) {
721
+ glob_max_dis = std::max(
722
+ glob_max_dis,
723
+ probe_span[j2] + probe_b[j2] - glob_b);
724
+ }
725
+
726
+ a = quantize_lut::fastscan_lut_scale(
727
+ glob_max_span, glob_max_dis, M + 1);
696
728
 
697
729
  // Second pass: quantize LUT and compute biasq.
698
730
  uint8_t* out_base =
@@ -872,6 +904,7 @@ struct IVFRaBitQFastScanScanner : InvertedListScanner {
872
904
  AlignedTable<float> lut_float;
873
905
  std::vector<float> rotated_q;
874
906
  std::vector<float> centroid_buf;
907
+ std::vector<uint8_t> rotated_qq;
875
908
  QueryFactorsData query_factors;
876
909
  FastScanDistancePostProcessing context;
877
910
  std::vector<int> probe_map;
@@ -893,6 +926,7 @@ struct IVFRaBitQFastScanScanner : InvertedListScanner {
893
926
  lut_float(16 * index_in.M),
894
927
  rotated_q(index_in.d),
895
928
  centroid_buf(index_in.d),
929
+ rotated_qq(index_in.d),
896
930
  probe_map({0}),
897
931
  mins_buf(index_in.M) {
898
932
  this->keep_max = is_similarity_metric(index_in.metric_type);
@@ -924,7 +958,9 @@ struct IVFRaBitQFastScanScanner : InvertedListScanner {
924
958
  qb,
925
959
  centered,
926
960
  rotated_q,
927
- centroid_buf);
961
+ centroid_buf,
962
+ rotated_qq,
963
+ nullptr);
928
964
 
929
965
  // Single-probe quantization (simplified inline, no OMP, no 3D)
930
966
  const size_t M = index.M;
@@ -952,7 +988,7 @@ struct IVFRaBitQFastScanScanner : InvertedListScanner {
952
988
  b += mn;
953
989
  }
954
990
 
955
- a = std::min(255.0f / max_span, 65535.0f / max_dis);
991
+ a = quantize_lut::fastscan_lut_scale(max_span, max_dis, M);
956
992
  for (size_t m = 0; m < M; m++) {
957
993
  const float* tab = lut_float.get() + m * ksub;
958
994
  rabitq::lut_quantize_16_to_uint8<SL>(
@@ -131,7 +131,9 @@ struct IndexIVFRaBitQFastScan : IndexIVFFastScan {
131
131
  uint8_t qb_param,
132
132
  bool centered_param,
133
133
  std::vector<float>& rotated_q,
134
- std::vector<float>& centroid_buf) const;
134
+ std::vector<float>& centroid_buf,
135
+ std::vector<uint8_t>& rotated_qq,
136
+ float* rotated_q_out) const;
135
137
 
136
138
  private:
137
139
  /// Decode FastScan code to RaBitQ residual vector with explicit
@@ -399,7 +401,8 @@ float IVFRaBitQHeapHandler<C, SL>::compute_full_multibit_distance(
399
401
  unpack_buf.data(),
400
402
  ex_code,
401
403
  ex_fac,
402
- query_factors.rotated_q.data(),
404
+ context->rotated_q ? context->rotated_q + storage_idx_val * dim
405
+ : query_factors.rotated_q.data(),
403
406
  is_similarity ? query_factors.q_dot_c : query_factors.qr_to_c_L2sqr,
404
407
  dim,
405
408
  ex_bits,