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
@@ -45,6 +45,7 @@
45
45
  #include <faiss/IndexIVFPQR.h>
46
46
  #include <faiss/IndexIVFRaBitQ.h>
47
47
  #include <faiss/IndexIVFRaBitQFastScan.h>
48
+ #include <faiss/IndexIVFSQFastScan.h>
48
49
  #include <faiss/IndexIVFSpectralHash.h>
49
50
  #include <faiss/IndexLSH.h>
50
51
  #include <faiss/IndexLattice.h>
@@ -52,11 +53,13 @@
52
53
  #include <faiss/IndexNSG.h>
53
54
  #include <faiss/IndexPQ.h>
54
55
  #include <faiss/IndexPQFastScan.h>
56
+ #include <faiss/IndexPiPNN.h>
55
57
  #include <faiss/IndexPreTransform.h>
56
58
  #include <faiss/IndexRaBitQ.h>
57
59
  #include <faiss/IndexRaBitQFastScan.h>
58
60
  #include <faiss/IndexRefine.h>
59
61
  #include <faiss/IndexRowwiseMinMax.h>
62
+ #include <faiss/IndexSQFastScan.h>
60
63
  #ifdef FAISS_ENABLE_SVS
61
64
  #include <faiss/impl/svs_io.h>
62
65
  #include <faiss/svs/IndexSVSFlat.h>
@@ -765,6 +768,10 @@ void read_ProductQuantizer(ProductQuantizer* pq, IOReader* f) {
765
768
  pq->d * pq->ksub);
766
769
  }
767
770
 
771
+ static void validate_codebooks_size(
772
+ const AdditiveQuantizer& aq,
773
+ size_t effective_d);
774
+
768
775
  static void read_ResidualQuantizer_old(ResidualQuantizer& rq, IOReader* f) {
769
776
  READ1(rq.d);
770
777
  FAISS_THROW_IF_NOT_FMT(
@@ -786,6 +793,7 @@ static void read_ResidualQuantizer_old(ResidualQuantizer& rq, IOReader* f) {
786
793
  READ1(rq.norm_min);
787
794
  READ1(rq.norm_max);
788
795
  rq.set_derived_values();
796
+ validate_codebooks_size(rq, rq.d);
789
797
  }
790
798
 
791
799
  static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
@@ -847,6 +855,9 @@ static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
847
855
  static void validate_codebooks_size(
848
856
  const AdditiveQuantizer& aq,
849
857
  size_t effective_d) {
858
+ if (!aq.is_trained && aq.codebooks.empty()) {
859
+ return;
860
+ }
850
861
  size_t required = mul_no_overflow(
851
862
  effective_d, aq.total_codebook_size, "codebooks validation");
852
863
  FAISS_THROW_IF_NOT_FMT(
@@ -1359,6 +1370,59 @@ static void read_NSG(NSG& nsg, IOReader* f) {
1359
1370
  FAISS_CHECK_RANGE(nsg.enterpoint, 0, N);
1360
1371
  }
1361
1372
 
1373
+ static void read_PiPNN(PiPNN& pipnn, idx_t index_ntotal, IOReader* f) {
1374
+ READ1(pipnn.R);
1375
+ READ1(pipnn.leaf_k);
1376
+ READ1(pipnn.hash_bits);
1377
+ READ1(pipnn.reservoir_size);
1378
+ READ1(pipnn.c_max);
1379
+ READ1(pipnn.c_min);
1380
+ READ1(pipnn.leader_cap);
1381
+ // Former replicas field: build metadata that search does not need.
1382
+ int replicas;
1383
+ READ1(replicas);
1384
+ READ1(pipnn.leader_fraction);
1385
+ READ1(pipnn.alpha);
1386
+ READVECTOR(pipnn.fanout);
1387
+ READ1(pipnn.seed);
1388
+ READ1_BOOL(pipnn.ip_partition_by_angle);
1389
+ READ1(pipnn.search_L);
1390
+ pipnn.check_params();
1391
+ READ1(pipnn.ntotal);
1392
+ READ1(pipnn.entry_point);
1393
+ // Checked before the graph is allocated; ntotal == 0 means unbuilt.
1394
+ if (pipnn.ntotal != 0) {
1395
+ FAISS_THROW_IF_NOT_FMT(
1396
+ pipnn.ntotal == index_ntotal,
1397
+ "PiPNN ntotal %" PRId64 " != index ntotal %" PRId64,
1398
+ pipnn.ntotal,
1399
+ index_ntotal);
1400
+ FAISS_THROW_IF_NOT_FMT(
1401
+ pipnn.ntotal <= INT32_MAX,
1402
+ "built PiPNN ntotal %" PRId64 " exceeds INT32_MAX",
1403
+ pipnn.ntotal);
1404
+ FAISS_CHECK_RANGE(pipnn.entry_point, 0, pipnn.ntotal);
1405
+ }
1406
+ const size_t graph_size = mul_no_overflow(
1407
+ (size_t)pipnn.ntotal, (size_t)pipnn.R, "PiPNN graph");
1408
+ // Views (IO_FLAG_MMAP_IFC, zero-copy) skip the scan: it would fault in
1409
+ // the whole graph. Search bounds-checks every neighbour id instead.
1410
+ if (!read_vector_base(pipnn.graph, f, graph_size, std::nullopt)) {
1411
+ FAISS_THROW_IF_NOT(
1412
+ graph_size <
1413
+ get_deserialization_vector_byte_limit() / sizeof(int32_t));
1414
+ pipnn.graph.resize(graph_size);
1415
+ READANDCHECK(pipnn.graph.data(), graph_size);
1416
+ for (int32_t id : pipnn.graph) {
1417
+ FAISS_CHECK_RANGE(id, -1, pipnn.ntotal);
1418
+ }
1419
+ }
1420
+ read_vector(pipnn.next_copy, f);
1421
+ FAISS_THROW_IF_NOT(
1422
+ pipnn.next_copy.size() == 0 ||
1423
+ pipnn.next_copy.size() == size_t(pipnn.ntotal));
1424
+ }
1425
+
1362
1426
  static void read_NNDescent(NNDescent& nnd, IOReader* f) {
1363
1427
  READ1(nnd.ntotal);
1364
1428
  READ1(nnd.d);
@@ -1841,6 +1905,14 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1841
1905
  } else {
1842
1906
  read_ResidualQuantizer(idxr->rq, f, io_flags);
1843
1907
  }
1908
+ FAISS_THROW_IF_NOT_MSG(
1909
+ idxr->rq.is_trained == idxr->is_trained,
1910
+ "IndexResidualQuantizer is_trained does not match "
1911
+ "ResidualQuantizer is_trained");
1912
+ FAISS_THROW_IF_NOT_MSG(
1913
+ idxr->ntotal == 0 || idxr->rq.is_trained,
1914
+ "IndexResidualQuantizer with ntotal > 0 must have trained "
1915
+ "ResidualQuantizer");
1844
1916
  validate_aq_dimension_match(
1845
1917
  idxr->rq, idxr->d, "IndexResidualQuantizer");
1846
1918
  READ1(idxr->code_size);
@@ -2760,6 +2832,66 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2760
2832
  idxnnd->d);
2761
2833
  }
2762
2834
  idx = std::move(idxnnd);
2835
+ } else if (h == fourcc("IPNf") || h == fourcc("IPNs")) {
2836
+ std::unique_ptr<IndexPiPNN> idxpipnn;
2837
+ if (h == fourcc("IPNf")) {
2838
+ idxpipnn = std::make_unique<IndexPiPNNFlat>();
2839
+ } else {
2840
+ idxpipnn = std::make_unique<IndexPiPNNSQ>();
2841
+ }
2842
+ read_index_header(*idxpipnn, f);
2843
+ READ1_BOOL(idxpipnn->defer_build);
2844
+ read_PiPNN(idxpipnn->pipnn, idxpipnn->ntotal, f);
2845
+ idxpipnn->storage = read_index(f, io_flags);
2846
+ idxpipnn->own_fields = true;
2847
+ FAISS_THROW_IF_NOT_MSG(
2848
+ idxpipnn->storage, "IndexPiPNN requires non-null storage");
2849
+ IndexPiPNN::check_storage_supported(*idxpipnn->storage);
2850
+ FAISS_THROW_IF_NOT_FMT(
2851
+ idxpipnn->storage->ntotal == idxpipnn->ntotal,
2852
+ "PiPNN storage ntotal %" PRId64 " != index ntotal %" PRId64,
2853
+ idxpipnn->storage->ntotal,
2854
+ idxpipnn->ntotal);
2855
+ FAISS_THROW_IF_NOT_FMT(
2856
+ idxpipnn->storage->d == idxpipnn->d,
2857
+ "PiPNN storage d %d != index d %d",
2858
+ idxpipnn->storage->d,
2859
+ idxpipnn->d);
2860
+ FAISS_THROW_IF_NOT_FMT(
2861
+ idxpipnn->storage->metric_type == idxpipnn->metric_type,
2862
+ "PiPNN storage metric %d != index metric %d",
2863
+ (int)idxpipnn->storage->metric_type,
2864
+ (int)idxpipnn->metric_type);
2865
+ idx = std::move(idxpipnn);
2866
+ } else if (h == fourcc("ISfs")) {
2867
+ auto idxsqfs = std::make_unique<IndexSQFastScan>();
2868
+ read_index_header(*idxsqfs, f);
2869
+ read_ScalarQuantizer(&idxsqfs->sq, f, *idxsqfs);
2870
+ READ1(idxsqfs->implem);
2871
+ READ1(idxsqfs->bbs);
2872
+ READ1(idxsqfs->qbs);
2873
+ FAISS_THROW_IF_NOT_MSG(idxsqfs->qbs >= 0, "qbs must be non-negative");
2874
+ READ1(idxsqfs->ntotal2);
2875
+ READ1(idxsqfs->M2);
2876
+ READVECTOR(idxsqfs->codes);
2877
+ READ1(idxsqfs->rerank_factor);
2878
+ READVECTOR(idxsqfs->lo_codes);
2879
+
2880
+ // Restore FastScan base-class fields from the SQ
2881
+ idxsqfs->M = idxsqfs->sq.d;
2882
+ idxsqfs->nbits = 4;
2883
+ idxsqfs->ksub = 16;
2884
+ idxsqfs->code_size = idxsqfs->M2 / 2;
2885
+
2886
+ validate_fastscan_fields(
2887
+ idxsqfs->M,
2888
+ idxsqfs->M2,
2889
+ idxsqfs->ksub,
2890
+ idxsqfs->bbs,
2891
+ "IndexSQFastScan");
2892
+
2893
+ idx = std::move(idxsqfs);
2894
+
2763
2895
  } else if (h == fourcc("IPfs")) {
2764
2896
  auto idxpqfs = std::make_unique<IndexPQFastScan>();
2765
2897
  read_index_header(*idxpqfs, f);
@@ -2787,6 +2919,46 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2787
2919
 
2788
2920
  idx = std::move(idxpqfs);
2789
2921
 
2922
+ } else if (h == fourcc("IwSf")) {
2923
+ auto ivfsqfs = std::make_unique<IndexIVFSQFastScan>();
2924
+ read_ivf_header(ivfsqfs.get(), f);
2925
+ READ1_BOOL(ivfsqfs->by_residual);
2926
+ READ1(ivfsqfs->code_size);
2927
+ READ1(ivfsqfs->bbs);
2928
+ READ1(ivfsqfs->M2);
2929
+ READ1(ivfsqfs->implem);
2930
+ READ1(ivfsqfs->rerank_factor);
2931
+ read_ScalarQuantizer(&ivfsqfs->sq, f, *ivfsqfs);
2932
+ read_InvertedLists(*ivfsqfs, f, io_flags);
2933
+
2934
+ ivfsqfs->M = ivfsqfs->d;
2935
+ ivfsqfs->nbits = 4;
2936
+ ivfsqfs->ksub = 16;
2937
+ ivfsqfs->init_code_packer();
2938
+
2939
+ bool has_orig;
2940
+ READ1(has_orig);
2941
+ if (has_orig) {
2942
+ ivfsqfs->lo_codes_invlists = read_InvertedLists(f, io_flags);
2943
+ // Rebuild direct_map from lo_codes_invlists for reranking
2944
+ ivfsqfs->direct_map.set_type(
2945
+ DirectMap::Hashtable, ivfsqfs->invlists, 0);
2946
+ for (size_t list_no = 0; list_no < ivfsqfs->nlist; list_no++) {
2947
+ size_t list_size =
2948
+ ivfsqfs->lo_codes_invlists->list_size(list_no);
2949
+ if (list_size == 0) {
2950
+ continue;
2951
+ }
2952
+ InvertedLists::ScopedIds ids(
2953
+ ivfsqfs->lo_codes_invlists, list_no);
2954
+ for (size_t j = 0; j < list_size; j++) {
2955
+ ivfsqfs->direct_map.add_single_id(ids[j], list_no, j);
2956
+ }
2957
+ }
2958
+ }
2959
+
2960
+ idx = std::move(ivfsqfs);
2961
+
2790
2962
  } else if (h == fourcc("IwPf")) {
2791
2963
  auto ivpq = std::make_unique<IndexIVFPQFastScan>();
2792
2964
  read_ivf_header(ivpq.get(), f);
@@ -40,6 +40,7 @@
40
40
  #include <faiss/IndexIVFPQR.h>
41
41
  #include <faiss/IndexIVFRaBitQ.h>
42
42
  #include <faiss/IndexIVFRaBitQFastScan.h>
43
+ #include <faiss/IndexIVFSQFastScan.h>
43
44
  #include <faiss/IndexIVFSpectralHash.h>
44
45
  #include <faiss/IndexLSH.h>
45
46
  #include <faiss/IndexLattice.h>
@@ -47,11 +48,13 @@
47
48
  #include <faiss/IndexNSG.h>
48
49
  #include <faiss/IndexPQ.h>
49
50
  #include <faiss/IndexPQFastScan.h>
51
+ #include <faiss/IndexPiPNN.h>
50
52
  #include <faiss/IndexPreTransform.h>
51
53
  #include <faiss/IndexRaBitQ.h>
52
54
  #include <faiss/IndexRaBitQFastScan.h>
53
55
  #include <faiss/IndexRefine.h>
54
56
  #include <faiss/IndexRowwiseMinMax.h>
57
+ #include <faiss/IndexSQFastScan.h>
55
58
  #ifdef FAISS_ENABLE_SVS
56
59
  #include <faiss/impl/svs_io.h>
57
60
  #include <faiss/svs/IndexSVSFlat.h>
@@ -421,6 +424,29 @@ static void write_NNDescent(const NNDescent* nnd, IOWriter* f) {
421
424
  WRITEVECTOR(nnd->final_graph);
422
425
  }
423
426
 
427
+ static void write_PiPNN(const PiPNN& pipnn, IOWriter* f) {
428
+ WRITE1(pipnn.R);
429
+ WRITE1(pipnn.leaf_k);
430
+ WRITE1(pipnn.hash_bits);
431
+ WRITE1(pipnn.reservoir_size);
432
+ WRITE1(pipnn.c_max);
433
+ WRITE1(pipnn.c_min);
434
+ WRITE1(pipnn.leader_cap);
435
+ // Former replicas field, kept so that the format does not change.
436
+ const int replicas = 1;
437
+ WRITE1(replicas);
438
+ WRITE1(pipnn.leader_fraction);
439
+ WRITE1(pipnn.alpha);
440
+ WRITEVECTOR(pipnn.fanout);
441
+ WRITE1(pipnn.seed);
442
+ WRITE1(pipnn.ip_partition_by_angle);
443
+ WRITE1(pipnn.search_L);
444
+ WRITE1(pipnn.ntotal);
445
+ WRITE1(pipnn.entry_point);
446
+ WRITEANDCHECK(pipnn.graph.data(), pipnn.graph.size());
447
+ WRITEVECTOR(pipnn.next_copy);
448
+ }
449
+
424
450
  // Write RaBitQuantizer for 1-bit format (backward compatible)
425
451
  static void write_RaBitQuantizer(
426
452
  const RaBitQuantizer* rabitq,
@@ -938,6 +964,40 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
938
964
  WRITE1(idxnsg->nndescent_iter);
939
965
  write_NSG(&idxnsg->nsg, f);
940
966
  write_index(idxnsg->storage, f);
967
+ } else if (
968
+ const IndexPiPNN* idxpipnn = dynamic_cast<const IndexPiPNN*>(idx)) {
969
+ FAISS_THROW_IF_NOT_FMT(
970
+ typeid(*idx) == typeid(IndexPiPNN) ||
971
+ typeid(*idx) == typeid(IndexPiPNNFlat) ||
972
+ typeid(*idx) == typeid(IndexPiPNNSQ),
973
+ "don't know how to serialize this IndexPiPNN subtype: %s",
974
+ typeid(*idx).name());
975
+ const Index* storage = idxpipnn->storage;
976
+ const PiPNN& state = idxpipnn->pipnn;
977
+ // The reader rejects these states, so they are not written either.
978
+ FAISS_THROW_IF_NOT_MSG(
979
+ storage, "cannot serialize an IndexPiPNN without storage");
980
+ IndexPiPNN::check_storage_supported(*storage);
981
+ state.check_params();
982
+ FAISS_THROW_IF_NOT_MSG(
983
+ storage->ntotal == idxpipnn->ntotal &&
984
+ storage->d == idxpipnn->d &&
985
+ storage->metric_type == idxpipnn->metric_type,
986
+ "cannot serialize IndexPiPNN: the storage is inconsistent "
987
+ "with the index (was the storage modified directly?)");
988
+ FAISS_THROW_IF_NOT_MSG(
989
+ state.graph.size() == size_t(state.ntotal) * size_t(state.R),
990
+ "cannot serialize IndexPiPNN: graph size != ntotal * R");
991
+ // The subclasses differ only by their constructors: the fourcc names
992
+ // the storage.
993
+ const uint32_t h = dynamic_cast<const IndexScalarQuantizer*>(storage)
994
+ ? fourcc("IPNs")
995
+ : fourcc("IPNf");
996
+ WRITE1(h);
997
+ write_index_header(idxpipnn, f);
998
+ WRITE1(idxpipnn->defer_build);
999
+ write_PiPNN(state, f);
1000
+ write_index(storage, f);
941
1001
  } else if (
942
1002
  const IndexNNDescent* idxnnd =
943
1003
  dynamic_cast<const IndexNNDescent*>(idx)) {
@@ -951,6 +1011,21 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
951
1011
  write_index_header(idxnnd, f);
952
1012
  write_NNDescent(&idxnnd->nndescent, f);
953
1013
  write_index(idxnnd->storage, f);
1014
+ } else if (
1015
+ const IndexSQFastScan* idxsqfs =
1016
+ dynamic_cast<const IndexSQFastScan*>(idx)) {
1017
+ uint32_t h = fourcc("ISfs");
1018
+ WRITE1(h);
1019
+ write_index_header(idxsqfs, f);
1020
+ write_ScalarQuantizer(&idxsqfs->sq, f);
1021
+ WRITE1(idxsqfs->implem);
1022
+ WRITE1(idxsqfs->bbs);
1023
+ WRITE1(idxsqfs->qbs);
1024
+ WRITE1(idxsqfs->ntotal2);
1025
+ WRITE1(idxsqfs->M2);
1026
+ WRITEVECTOR(idxsqfs->codes);
1027
+ WRITE1(idxsqfs->rerank_factor);
1028
+ WRITEVECTOR(idxsqfs->lo_codes);
954
1029
  } else if (
955
1030
  const IndexPQFastScan* idxpqfs =
956
1031
  dynamic_cast<const IndexPQFastScan*>(idx)) {
@@ -964,6 +1039,26 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
964
1039
  WRITE1(idxpqfs->ntotal2);
965
1040
  WRITE1(idxpqfs->M2);
966
1041
  WRITEVECTOR(idxpqfs->codes);
1042
+ } else if (
1043
+ const IndexIVFSQFastScan* ivfsqfs =
1044
+ dynamic_cast<const IndexIVFSQFastScan*>(idx)) {
1045
+ uint32_t h = fourcc("IwSf");
1046
+ WRITE1(h);
1047
+ write_ivf_header(ivfsqfs, f);
1048
+ WRITE1(ivfsqfs->by_residual);
1049
+ WRITE1(ivfsqfs->code_size);
1050
+ WRITE1(ivfsqfs->bbs);
1051
+ WRITE1(ivfsqfs->M2);
1052
+ WRITE1(ivfsqfs->implem);
1053
+ WRITE1(ivfsqfs->rerank_factor);
1054
+ write_ScalarQuantizer(&ivfsqfs->sq, f);
1055
+ write_InvertedLists(ivfsqfs->invlists, f);
1056
+ // Write lo_codes_invlists if present
1057
+ bool has_orig = (ivfsqfs->lo_codes_invlists != nullptr);
1058
+ WRITE1(has_orig);
1059
+ if (has_orig) {
1060
+ write_InvertedLists(ivfsqfs->lo_codes_invlists, f);
1061
+ }
967
1062
  } else if (
968
1063
  const IndexIVFPQFastScan* ivpq_2 =
969
1064
  dynamic_cast<const IndexIVFPQFastScan*>(idx)) {
@@ -0,0 +1,159 @@
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
+ #include <faiss/impl/pipnn/HashPrune.h>
9
+
10
+ #include <cstring>
11
+
12
+ #include <faiss/impl/FaissAssert.h>
13
+ #include <faiss/impl/platform_macros.h>
14
+ #include <faiss/utils/bf16.h>
15
+ #include <faiss/utils/random.h>
16
+
17
+ namespace faiss {
18
+ namespace pipnn {
19
+
20
+ uint16_t distance_key(float v) {
21
+ // Normalized on the bit pattern, so -ffast-math cannot remove the checks.
22
+ uint32_t u;
23
+ std::memcpy(&u, &v, sizeof(u));
24
+ if ((u & 0x7fffffffu) == 0) {
25
+ u = 0; // -0 -> +0
26
+ } else if ((u & 0x7f800000u) == 0x7f800000u && (u & 0x007fffffu) != 0) {
27
+ u = 0x7f800000u; // NaN -> +inf
28
+ }
29
+ std::memcpy(&v, &u, sizeof(v));
30
+ // Scalar `encode_bf16` only: the SIMD path rounds differently, so keys
31
+ // would depend on the CPU.
32
+ const uint16_t b = encode_bf16(v);
33
+ return (b & 0x8000) ? static_cast<uint16_t>(~b)
34
+ : static_cast<uint16_t>(b | 0x8000);
35
+ }
36
+
37
+ bool ReservoirRef::offer(int32_t id, uint16_t hash, uint16_t key) {
38
+ const int n = *count;
39
+ // A full reservoir rejects anything not below its maximum (rules 1 and 3).
40
+ if (n == capacity && *farthest != kNoFarthest) {
41
+ const Slot& f = slots[*farthest];
42
+ if (!slot_less(key, id, f.key, f.id)) {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ int lo = 0;
48
+ int hi = n;
49
+ while (lo < hi) {
50
+ const int mid = (lo + hi) / 2;
51
+ if (slots[mid].hash < hash) {
52
+ lo = mid + 1;
53
+ } else {
54
+ hi = mid;
55
+ }
56
+ }
57
+
58
+ // Rule 1: bucket hit. The cached farthest stays valid only when a
59
+ // non-farthest slot is lowered.
60
+ if (lo < n && slots[lo].hash == hash) {
61
+ Slot& s = slots[lo];
62
+ if (!slot_less(key, id, s.key, s.id)) {
63
+ return false;
64
+ }
65
+ s.id = id;
66
+ s.key = key;
67
+ if (*farthest == lo) {
68
+ *farthest = kNoFarthest;
69
+ }
70
+ return true;
71
+ }
72
+
73
+ // Rule 2: miss with room.
74
+ if (n < capacity) {
75
+ std::memmove(
76
+ slots + lo + 1,
77
+ slots + lo,
78
+ sizeof(Slot) * static_cast<size_t>(n - lo));
79
+ slots[lo] = Slot{id, hash, key};
80
+ *count = static_cast<uint8_t>(n + 1);
81
+ *farthest = kNoFarthest;
82
+ return true;
83
+ }
84
+
85
+ // Rule 3: miss, full. Compare against the slot_less-maximum.
86
+ int z = *farthest;
87
+ if (z == kNoFarthest) {
88
+ z = 0;
89
+ for (int i = 1; i < n; i++) {
90
+ if (slot_less(
91
+ slots[z].key, slots[z].id, slots[i].key, slots[i].id)) {
92
+ z = i;
93
+ }
94
+ }
95
+ *farthest = static_cast<uint8_t>(z);
96
+ }
97
+ const Slot& zs = slots[z];
98
+ if (!slot_less(key, id, zs.key, zs.id)) {
99
+ return false;
100
+ }
101
+ if (z < lo) {
102
+ std::memmove(
103
+ slots + z,
104
+ slots + z + 1,
105
+ sizeof(Slot) * static_cast<size_t>(lo - 1 - z));
106
+ slots[lo - 1] = Slot{id, hash, key};
107
+ } else {
108
+ std::memmove(
109
+ slots + lo + 1,
110
+ slots + lo,
111
+ sizeof(Slot) * static_cast<size_t>(z - lo));
112
+ slots[lo] = Slot{id, hash, key};
113
+ }
114
+ *farthest = kNoFarthest;
115
+ return true;
116
+ }
117
+
118
+ Hyperplanes::Hyperplanes(int d, int m, int64_t seed) : d(d), m(m) {
119
+ FAISS_THROW_IF_NOT_FMT(d >= 1, "Hyperplanes: d must be >= 1, got %d", d);
120
+ FAISS_THROW_IF_NOT_FMT(
121
+ m >= 1 && m <= 16, "Hyperplanes: m must be in [1, 16], got %d", m);
122
+ std::vector<float> h(static_cast<size_t>(m) * d);
123
+ float_randn(h.data(), h.size(), seed);
124
+ ht.assign(static_cast<size_t>(d) * 16, 0.0f);
125
+ for (int i = 0; i < m; i++) {
126
+ for (int j = 0; j < d; j++) {
127
+ ht[static_cast<size_t>(j) * 16 + i] =
128
+ h[static_cast<size_t>(i) * d + j];
129
+ }
130
+ }
131
+ }
132
+
133
+ FAISS_NOINLINE void Hyperplanes::sketch(const float* x, float* out16) const {
134
+ // Local accumulators, j ascending: the result depends only on the values
135
+ // of x, never on its buffer, alignment or caller.
136
+ float acc[16] = {};
137
+ const float* h = ht.data();
138
+ for (int j = 0; j < d; j++) {
139
+ const float xj = x[j];
140
+ const float* hj = h + static_cast<size_t>(j) * 16;
141
+ for (int i = 0; i < 16; i++) {
142
+ acc[i] += xj * hj[i];
143
+ }
144
+ }
145
+ std::memcpy(out16, acc, sizeof(acc));
146
+ }
147
+
148
+ uint16_t residual_hash(const float* sketch_p, const float* sketch_c, int m) {
149
+ uint16_t h = 0;
150
+ for (int i = 0; i < m; i++) {
151
+ if (sketch_c[i] - sketch_p[i] >= 0) {
152
+ h = static_cast<uint16_t>(h | (1u << i));
153
+ }
154
+ }
155
+ return h;
156
+ }
157
+
158
+ } // namespace pipnn
159
+ } // namespace faiss
@@ -0,0 +1,69 @@
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 <cstdint>
11
+ #include <vector>
12
+
13
+ namespace faiss {
14
+ namespace pipnn {
15
+
16
+ /// One reservoir slot: candidate id, residual-LSH bucket, order-preserving key.
17
+ struct Slot {
18
+ int32_t id;
19
+ uint16_t hash;
20
+ uint16_t key;
21
+ };
22
+ static_assert(sizeof(Slot) == 8, "PiPNN reservoir slots must be 8 bytes");
23
+
24
+ /// "No cached farthest slot"; never a slot index since capacity <= 255.
25
+ constexpr uint8_t kNoFarthest = 0xFF;
26
+
27
+ /// Monotone 16-bit key of a distance, which may be negative, from its scalar
28
+ /// bf16 encoding. NaN maps to +inf and -0 to +0.
29
+ uint16_t distance_key(float v);
30
+
31
+ /// Strict (key, id) order. Deciding key ties by id makes the reservoir
32
+ /// independent of the offer order.
33
+ inline bool slot_less(uint16_t ka, int32_t ia, uint16_t kb, int32_t ib) {
34
+ return ka < kb || (ka == kb && ia < ib);
35
+ }
36
+
37
+ /// One point's reservoir in caller-owned storage; not thread-safe. Slots are
38
+ /// sorted by distinct hash, and *farthest caches the slot_less-maximum.
39
+ struct ReservoirRef {
40
+ Slot* slots;
41
+ uint8_t* count; // 0 when fresh
42
+ uint8_t* farthest; // kNoFarthest when fresh or unknown
43
+ int capacity; // 1..255
44
+
45
+ /// Keeps the smaller offer on a bucket hit, else inserts or evicts the
46
+ /// maximum. An id must always come with the same hash. True iff changed.
47
+ bool offer(int32_t id, uint16_t hash, uint16_t key);
48
+ };
49
+
50
+ /// m <= 16 Gaussian hyperplanes from float_randn(seed), stored transposed
51
+ /// and zero-padded: ht[j * 16 + i].
52
+ struct Hyperplanes {
53
+ int d = 0;
54
+ int m = 0;
55
+ std::vector<float> ht; // d * 16
56
+
57
+ Hyperplanes() = default;
58
+ Hyperplanes(int d, int m, int64_t seed);
59
+
60
+ /// out16[i] = sum over ascending j of x[j] * ht[j * 16 + i]. Not inlined,
61
+ /// so all call sites produce bitwise-identical sketches.
62
+ void sketch(const float* x, float* out16) const;
63
+ };
64
+
65
+ /// Bit i (i < m <= 16) is set iff sketch_c[i] - sketch_p[i] >= 0.
66
+ uint16_t residual_hash(const float* sketch_p, const float* sketch_c, int m);
67
+
68
+ } // namespace pipnn
69
+ } // namespace faiss