faiss 0.6.0 → 0.6.2

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 (378) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/ext/faiss/extconf.rb +2 -1
  4. data/ext/faiss/{index_rb.cpp → index.cpp} +1 -1
  5. data/ext/faiss/index_binary.cpp +1 -1
  6. data/ext/faiss/kmeans.cpp +1 -1
  7. data/ext/faiss/pca_matrix.cpp +1 -1
  8. data/ext/faiss/product_quantizer.cpp +1 -1
  9. data/ext/faiss/{utils_rb.cpp → utils.cpp} +1 -1
  10. data/lib/faiss/version.rb +1 -1
  11. data/vendor/faiss/faiss/AutoTune.cpp +93 -80
  12. data/vendor/faiss/faiss/Clustering.cpp +39 -240
  13. data/vendor/faiss/faiss/Clustering.h +6 -0
  14. data/vendor/faiss/faiss/IVFlib.cpp +41 -21
  15. data/vendor/faiss/faiss/Index.cpp +6 -5
  16. data/vendor/faiss/faiss/Index.h +5 -5
  17. data/vendor/faiss/faiss/Index2Layer.cpp +37 -53
  18. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +49 -37
  19. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +36 -34
  20. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.h +4 -1
  21. data/vendor/faiss/faiss/IndexBinary.cpp +5 -3
  22. data/vendor/faiss/faiss/IndexBinary.h +4 -4
  23. data/vendor/faiss/faiss/IndexBinaryFlat.cpp +1 -1
  24. data/vendor/faiss/faiss/IndexBinaryFlat.h +1 -1
  25. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +4 -4
  26. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +88 -97
  27. data/vendor/faiss/faiss/IndexBinaryHNSW.h +9 -3
  28. data/vendor/faiss/faiss/IndexBinaryHash.cpp +45 -236
  29. data/vendor/faiss/faiss/IndexBinaryHash.h +6 -6
  30. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +89 -417
  31. data/vendor/faiss/faiss/IndexFastScan.cpp +72 -109
  32. data/vendor/faiss/faiss/IndexFastScan.h +25 -23
  33. data/vendor/faiss/faiss/IndexFlat.cpp +27 -20
  34. data/vendor/faiss/faiss/IndexFlat.h +21 -18
  35. data/vendor/faiss/faiss/IndexFlatCodes.cpp +42 -19
  36. data/vendor/faiss/faiss/IndexHNSW.cpp +374 -206
  37. data/vendor/faiss/faiss/IndexHNSW.h +16 -2
  38. data/vendor/faiss/faiss/IndexIDMap.cpp +25 -21
  39. data/vendor/faiss/faiss/IndexIDMap.h +9 -7
  40. data/vendor/faiss/faiss/IndexIVF.cpp +467 -364
  41. data/vendor/faiss/faiss/IndexIVF.h +33 -12
  42. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +79 -76
  43. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +96 -93
  44. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.h +4 -1
  45. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +357 -238
  46. data/vendor/faiss/faiss/IndexIVFFastScan.h +42 -41
  47. data/vendor/faiss/faiss/IndexIVFFlat.cpp +39 -69
  48. data/vendor/faiss/faiss/IndexIVFFlat.h +32 -0
  49. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +56 -33
  50. data/vendor/faiss/faiss/IndexIVFFlatPanorama.h +3 -1
  51. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +18 -15
  52. data/vendor/faiss/faiss/IndexIVFPQ.cpp +73 -846
  53. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +151 -121
  54. data/vendor/faiss/faiss/IndexIVFPQFastScan.h +3 -0
  55. data/vendor/faiss/faiss/IndexIVFPQR.cpp +23 -20
  56. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +30 -52
  57. data/vendor/faiss/faiss/IndexIVFRaBitQ.h +2 -1
  58. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +475 -476
  59. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +248 -93
  60. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +41 -127
  61. data/vendor/faiss/faiss/IndexIVFSpectralHash.h +1 -1
  62. data/vendor/faiss/faiss/IndexLSH.cpp +36 -19
  63. data/vendor/faiss/faiss/IndexLattice.cpp +13 -13
  64. data/vendor/faiss/faiss/IndexNNDescent.cpp +36 -21
  65. data/vendor/faiss/faiss/IndexNNDescent.h +2 -2
  66. data/vendor/faiss/faiss/IndexNSG.cpp +38 -23
  67. data/vendor/faiss/faiss/IndexNeuralNetCodec.cpp +31 -11
  68. data/vendor/faiss/faiss/IndexPQ.cpp +128 -221
  69. data/vendor/faiss/faiss/IndexPQ.h +3 -2
  70. data/vendor/faiss/faiss/IndexPQFastScan.cpp +20 -14
  71. data/vendor/faiss/faiss/IndexPQFastScan.h +3 -0
  72. data/vendor/faiss/faiss/IndexPreTransform.cpp +25 -18
  73. data/vendor/faiss/faiss/IndexPreTransform.h +1 -1
  74. data/vendor/faiss/faiss/IndexRaBitQ.cpp +11 -36
  75. data/vendor/faiss/faiss/IndexRaBitQ.h +2 -1
  76. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +41 -277
  77. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +183 -27
  78. data/vendor/faiss/faiss/IndexRefine.cpp +30 -25
  79. data/vendor/faiss/faiss/IndexRefine.h +4 -4
  80. data/vendor/faiss/faiss/IndexReplicas.cpp +6 -6
  81. data/vendor/faiss/faiss/IndexRowwiseMinMax.cpp +15 -14
  82. data/vendor/faiss/faiss/IndexRowwiseMinMax.h +1 -1
  83. data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +150 -20
  84. data/vendor/faiss/faiss/IndexScalarQuantizer.h +10 -0
  85. data/vendor/faiss/faiss/IndexShards.cpp +10 -9
  86. data/vendor/faiss/faiss/IndexShardsIVF.cpp +21 -15
  87. data/vendor/faiss/faiss/MatrixStats.cpp +5 -4
  88. data/vendor/faiss/faiss/MetaIndexes.cpp +19 -17
  89. data/vendor/faiss/faiss/MetaIndexes.h +1 -1
  90. data/vendor/faiss/faiss/MetricType.h +14 -7
  91. data/vendor/faiss/faiss/SuperKMeans.cpp +656 -0
  92. data/vendor/faiss/faiss/SuperKMeans.h +97 -0
  93. data/vendor/faiss/faiss/VectorTransform.cpp +237 -149
  94. data/vendor/faiss/faiss/VectorTransform.h +16 -16
  95. data/vendor/faiss/faiss/build.cpp +23 -0
  96. data/vendor/faiss/faiss/build.h +15 -0
  97. data/vendor/faiss/faiss/clone_index.cpp +48 -47
  98. data/vendor/faiss/faiss/cppcontrib/SaDecodeKernels.h +1 -1
  99. data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-avx2-inl.h +47 -47
  100. data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-inl.h +11 -0
  101. data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-neon-inl.h +902 -12
  102. data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-avx2-inl.h +38 -38
  103. data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-inl.h +11 -0
  104. data/vendor/faiss/faiss/cppcontrib/sa_decode/PQ-neon-inl.h +702 -10
  105. data/vendor/faiss/faiss/factory_tools.cpp +9 -0
  106. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +6 -5
  107. data/vendor/faiss/faiss/gpu/GpuResources.h +3 -2
  108. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +15 -16
  109. data/vendor/faiss/faiss/gpu/StandardGpuResources.h +5 -4
  110. data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +46 -0
  111. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +56 -0
  112. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFPQ.cpp +78 -1
  113. data/vendor/faiss/faiss/gpu/test/TestUtils.cpp +72 -0
  114. data/vendor/faiss/faiss/gpu/test/TestUtils.h +23 -0
  115. data/vendor/faiss/faiss/gpu/utils/CuvsFilterConvert.h +1 -1
  116. data/vendor/faiss/faiss/gpu/utils/CuvsUtils.h +21 -10
  117. data/vendor/faiss/faiss/gpu_metal/GpuIndexFlat.h +22 -0
  118. data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +35 -0
  119. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +87 -0
  120. data/vendor/faiss/faiss/gpu_metal/MetalFlatKernels.h +40 -0
  121. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +58 -0
  122. data/vendor/faiss/faiss/gpu_metal/MetalIndexFlat.h +65 -0
  123. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +181 -0
  124. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +111 -0
  125. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +45 -0
  126. data/vendor/faiss/faiss/gpu_metal/MetalResources.h +79 -0
  127. data/vendor/faiss/faiss/gpu_metal/StandardMetalResources.h +35 -0
  128. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFFlat.h +193 -0
  129. data/vendor/faiss/faiss/impl/AdSampling.cpp +103 -0
  130. data/vendor/faiss/faiss/impl/AdSampling.h +35 -0
  131. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +29 -25
  132. data/vendor/faiss/faiss/impl/AdditiveQuantizer.h +1 -0
  133. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +10 -9
  134. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +3 -0
  135. data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +244 -0
  136. data/vendor/faiss/faiss/impl/ClusteringHelpers.h +94 -0
  137. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +16 -16
  138. data/vendor/faiss/faiss/impl/CodePacker.cpp +3 -3
  139. data/vendor/faiss/faiss/impl/CodePackerRaBitQ.cpp +1 -1
  140. data/vendor/faiss/faiss/impl/DistanceComputer.h +8 -8
  141. data/vendor/faiss/faiss/impl/FaissAssert.h +6 -3
  142. data/vendor/faiss/faiss/impl/FaissException.h +50 -3
  143. data/vendor/faiss/faiss/impl/HNSW.cpp +639 -507
  144. data/vendor/faiss/faiss/impl/HNSW.h +61 -44
  145. data/vendor/faiss/faiss/impl/IDSelector.cpp +15 -11
  146. data/vendor/faiss/faiss/impl/IDSelector.h +8 -8
  147. data/vendor/faiss/faiss/impl/InvertedListScannerStats.h +26 -0
  148. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +82 -77
  149. data/vendor/faiss/faiss/impl/NNDescent.cpp +62 -25
  150. data/vendor/faiss/faiss/impl/NNDescent.h +6 -2
  151. data/vendor/faiss/faiss/impl/NSG.cpp +53 -32
  152. data/vendor/faiss/faiss/impl/NSG.h +4 -4
  153. data/vendor/faiss/faiss/impl/Panorama.cpp +23 -6
  154. data/vendor/faiss/faiss/impl/Panorama.h +269 -87
  155. data/vendor/faiss/faiss/impl/PdxLayout.cpp +93 -0
  156. data/vendor/faiss/faiss/impl/PdxLayout.h +41 -0
  157. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +46 -32
  158. data/vendor/faiss/faiss/impl/PolysemousTraining.h +3 -3
  159. data/vendor/faiss/faiss/impl/ProductAdditiveQuantizer.cpp +35 -35
  160. data/vendor/faiss/faiss/impl/ProductQuantizer-inl.h +21 -16
  161. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +55 -25
  162. data/vendor/faiss/faiss/impl/Quantizer.h +2 -2
  163. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +55 -49
  164. data/vendor/faiss/faiss/impl/RaBitQUtils.h +65 -0
  165. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +302 -283
  166. data/vendor/faiss/faiss/impl/ResidualQuantizer.cpp +26 -23
  167. data/vendor/faiss/faiss/impl/ResidualQuantizer.h +1 -1
  168. data/vendor/faiss/faiss/impl/ResultHandler.h +100 -75
  169. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +318 -7
  170. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +77 -1
  171. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +14 -11
  172. data/vendor/faiss/faiss/impl/VisitedTable.cpp +10 -10
  173. data/vendor/faiss/faiss/impl/VisitedTable.h +70 -28
  174. data/vendor/faiss/faiss/impl/approx_topk/approx_topk.h +276 -0
  175. data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +68 -0
  176. data/vendor/faiss/faiss/{utils → impl}/approx_topk/generic.h +15 -8
  177. data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +68 -0
  178. data/vendor/faiss/faiss/impl/approx_topk/rq_beam_search_tab-inl.h +169 -0
  179. data/vendor/faiss/faiss/impl/approx_topk/rq_beam_search_tab.h +117 -0
  180. data/vendor/faiss/faiss/impl/approx_topk/simdlib256-inl.h +146 -0
  181. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHNSW_impl.h +73 -0
  182. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +270 -0
  183. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +460 -0
  184. data/vendor/faiss/faiss/impl/binary_hamming/IndexIVFSpectralHash_impl.h +159 -0
  185. data/vendor/faiss/faiss/impl/binary_hamming/IndexPQ_impl.h +92 -0
  186. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +26 -0
  187. data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +26 -0
  188. data/vendor/faiss/faiss/impl/binary_hamming/dispatch.h +143 -0
  189. data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +26 -0
  190. data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +26 -0
  191. data/vendor/faiss/faiss/impl/expanded_scanners.h +8 -3
  192. data/vendor/faiss/faiss/impl/{FastScanDistancePostProcessing.h → fast_scan/FastScanDistancePostProcessing.h} +13 -6
  193. data/vendor/faiss/faiss/impl/{LookupTableScaler.h → fast_scan/LookupTableScaler.h} +16 -5
  194. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +237 -0
  195. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +185 -0
  196. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +229 -0
  197. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +270 -0
  198. data/vendor/faiss/faiss/impl/{pq4_fast_scan.cpp → fast_scan/fast_scan.cpp} +169 -2
  199. data/vendor/faiss/faiss/impl/fast_scan/fast_scan.h +341 -0
  200. data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +36 -0
  201. data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +40 -0
  202. data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +120 -0
  203. data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +104 -0
  204. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +213 -0
  205. data/vendor/faiss/faiss/impl/{pq4_fast_scan_search_qbs.cpp → fast_scan/kernels_simd512.h} +26 -356
  206. data/vendor/faiss/faiss/impl/fast_scan/rabitq_dispatching.h +90 -0
  207. data/vendor/faiss/faiss/impl/fast_scan/rabitq_result_handler.h +108 -0
  208. data/vendor/faiss/faiss/impl/{simd_result_handlers.h → fast_scan/simd_result_handlers.h} +282 -134
  209. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +54 -0
  210. data/vendor/faiss/faiss/impl/hnsw/LockVector.h +64 -0
  211. data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.cpp +83 -0
  212. data/vendor/faiss/faiss/impl/hnsw/MinimaxHeap.h +113 -0
  213. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +150 -0
  214. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +142 -0
  215. data/vendor/faiss/faiss/impl/index_read.cpp +1227 -79
  216. data/vendor/faiss/faiss/impl/index_read_utils.h +1 -1
  217. data/vendor/faiss/faiss/impl/index_write.cpp +96 -13
  218. data/vendor/faiss/faiss/impl/io.cpp +6 -6
  219. data/vendor/faiss/faiss/impl/io_macros.h +58 -16
  220. data/vendor/faiss/faiss/impl/kmeans1d.cpp +10 -10
  221. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +37 -23
  222. data/vendor/faiss/faiss/impl/lattice_Zn.h +6 -6
  223. data/vendor/faiss/faiss/impl/mapped_io.cpp +6 -6
  224. data/vendor/faiss/faiss/impl/platform_macros.h +15 -4
  225. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +549 -0
  226. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +245 -0
  227. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.h +105 -0
  228. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +106 -0
  229. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +23 -0
  230. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +23 -0
  231. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +23 -0
  232. data/vendor/faiss/faiss/impl/pq_code_distance/{pq_code_distance-avx2.cpp → pq_code_distance-avx2.h} +9 -13
  233. data/vendor/faiss/faiss/impl/pq_code_distance/{pq_code_distance-avx512.cpp → pq_code_distance-avx512.h} +9 -57
  234. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +45 -107
  235. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +96 -0
  236. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +274 -5
  237. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +10 -7
  238. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +105 -0
  239. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +70 -0
  240. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +311 -477
  241. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.h +1 -1
  242. data/vendor/faiss/faiss/impl/scalar_quantizer/codecs.h +1 -1
  243. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +9 -2
  244. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +419 -19
  245. data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +27 -1
  246. data/vendor/faiss/faiss/impl/scalar_quantizer/similarities.h +3 -3
  247. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +387 -2
  248. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +553 -0
  249. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +559 -0
  250. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +341 -2
  251. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +425 -3
  252. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +290 -2
  253. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +337 -0
  254. data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +192 -8
  255. data/vendor/faiss/faiss/impl/scalar_quantizer/training.h +12 -0
  256. data/vendor/faiss/faiss/impl/simd_dispatch.h +157 -66
  257. data/vendor/faiss/faiss/impl/simdlib/simdlib.h +57 -0
  258. data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_avx2.h +264 -172
  259. data/vendor/faiss/faiss/impl/simdlib/simdlib_avx512.h +414 -0
  260. data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +44 -0
  261. data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_emulated.h +231 -166
  262. data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_neon.h +270 -218
  263. data/vendor/faiss/faiss/{utils → impl/simdlib}/simdlib_ppc64.h +201 -160
  264. data/vendor/faiss/faiss/impl/svs_io.cpp +12 -3
  265. data/vendor/faiss/faiss/impl/svs_io.h +8 -2
  266. data/vendor/faiss/faiss/index_factory.cpp +90 -18
  267. data/vendor/faiss/faiss/index_io.h +40 -0
  268. data/vendor/faiss/faiss/invlists/BlockInvertedLists.cpp +66 -16
  269. data/vendor/faiss/faiss/invlists/DirectMap.cpp +28 -15
  270. data/vendor/faiss/faiss/invlists/DirectMap.h +4 -3
  271. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +170 -86
  272. data/vendor/faiss/faiss/invlists/InvertedLists.h +88 -25
  273. data/vendor/faiss/faiss/invlists/InvertedListsIOHook.cpp +4 -4
  274. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +13 -13
  275. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +1 -1
  276. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +1 -1
  277. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +2 -2
  278. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +350 -0
  279. data/vendor/faiss/faiss/svs/IndexSVSIVF.h +128 -0
  280. data/vendor/faiss/faiss/svs/IndexSVSIVFLVQ.cpp +40 -0
  281. data/vendor/faiss/faiss/svs/IndexSVSIVFLVQ.h +43 -0
  282. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +225 -0
  283. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.h +71 -0
  284. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +142 -21
  285. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +33 -7
  286. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +3 -2
  287. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  288. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +77 -27
  289. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +10 -4
  290. data/vendor/faiss/faiss/utils/Heap.cpp +10 -10
  291. data/vendor/faiss/faiss/utils/NeuralNet.cpp +47 -36
  292. data/vendor/faiss/faiss/utils/NeuralNet.h +1 -1
  293. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +10 -4
  294. data/vendor/faiss/faiss/utils/bf16.h +34 -0
  295. data/vendor/faiss/faiss/utils/distances.cpp +390 -560
  296. data/vendor/faiss/faiss/utils/distances.h +20 -1
  297. data/vendor/faiss/faiss/utils/distances_dispatch.h +117 -37
  298. data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +8 -7
  299. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +33 -14
  300. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.h +12 -1
  301. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +16 -293
  302. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +57 -0
  303. data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +290 -0
  304. data/vendor/faiss/faiss/utils/distances_simd.cpp +5 -178
  305. data/vendor/faiss/faiss/utils/extra_distances.cpp +9 -8
  306. data/vendor/faiss/faiss/utils/extra_distances.h +32 -6
  307. data/vendor/faiss/faiss/utils/hamming-inl.h +13 -11
  308. data/vendor/faiss/faiss/utils/hamming.cpp +66 -517
  309. data/vendor/faiss/faiss/utils/hamming.h +92 -2
  310. data/vendor/faiss/faiss/utils/hamming_distance/common.h +287 -10
  311. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +16 -0
  312. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +15 -0
  313. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +15 -0
  314. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx2.h +142 -0
  315. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +210 -0
  316. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_spr.h +171 -0
  317. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-generic.h +368 -0
  318. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +322 -0
  319. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-rvv.h +39 -0
  320. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer.h +146 -0
  321. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +481 -0
  322. data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +15 -0
  323. data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +15 -0
  324. data/vendor/faiss/faiss/utils/partitioning.cpp +66 -989
  325. data/vendor/faiss/faiss/utils/partitioning.h +31 -0
  326. data/vendor/faiss/faiss/utils/popcount.h +29 -0
  327. data/vendor/faiss/faiss/utils/pq_code_distance.h +2 -2
  328. data/vendor/faiss/faiss/utils/prefetch.h +2 -2
  329. data/vendor/faiss/faiss/utils/quantize_lut.cpp +30 -30
  330. data/vendor/faiss/faiss/utils/quantize_lut.h +1 -1
  331. data/vendor/faiss/faiss/utils/rabitq_simd.h +57 -536
  332. data/vendor/faiss/faiss/utils/random.cpp +6 -6
  333. data/vendor/faiss/faiss/utils/simd_impl/IVFFlatScanner-inl.h +51 -0
  334. data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +5 -1
  335. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +213 -4
  336. data/vendor/faiss/faiss/utils/simd_impl/distances_autovec-inl.h +163 -10
  337. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +250 -4
  338. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +7 -4
  339. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +189 -0
  340. data/vendor/faiss/faiss/utils/simd_impl/distances_simdlib256.h +195 -0
  341. data/vendor/faiss/faiss/utils/simd_impl/distances_sse-inl.h +2 -1
  342. data/vendor/faiss/faiss/utils/{distances_fused/simdlib_based.h → simd_impl/exhaustive_L2sqr_blas_cmax.h} +5 -10
  343. data/vendor/faiss/faiss/utils/simd_impl/hamming_impl.h +481 -0
  344. data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +14 -0
  345. data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +14 -0
  346. data/vendor/faiss/faiss/utils/simd_impl/partitioning_simdlib256.h +1031 -0
  347. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +355 -0
  348. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +477 -0
  349. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +343 -0
  350. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +55 -0
  351. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +55 -0
  352. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +32 -0
  353. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +43 -0
  354. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +57 -0
  355. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +45 -0
  356. data/vendor/faiss/faiss/utils/simd_levels.cpp +29 -7
  357. data/vendor/faiss/faiss/utils/simd_levels.h +93 -1
  358. data/vendor/faiss/faiss/utils/sorting.cpp +48 -36
  359. data/vendor/faiss/faiss/utils/utils.cpp +5 -5
  360. data/vendor/faiss/faiss/utils/utils.h +3 -3
  361. metadata +129 -34
  362. data/vendor/faiss/faiss/impl/RaBitQStats.cpp +0 -29
  363. data/vendor/faiss/faiss/impl/RaBitQStats.h +0 -56
  364. data/vendor/faiss/faiss/impl/pq4_fast_scan.h +0 -224
  365. data/vendor/faiss/faiss/impl/pq4_fast_scan_search_1.cpp +0 -230
  366. data/vendor/faiss/faiss/utils/approx_topk/approx_topk.h +0 -84
  367. data/vendor/faiss/faiss/utils/approx_topk/avx2-inl.h +0 -196
  368. data/vendor/faiss/faiss/utils/approx_topk/mode.h +0 -34
  369. data/vendor/faiss/faiss/utils/distances_fused/avx512.h +0 -36
  370. data/vendor/faiss/faiss/utils/extra_distances-inl.h +0 -235
  371. data/vendor/faiss/faiss/utils/hamming_distance/avx2-inl.h +0 -462
  372. data/vendor/faiss/faiss/utils/hamming_distance/avx512-inl.h +0 -490
  373. data/vendor/faiss/faiss/utils/hamming_distance/generic-inl.h +0 -449
  374. data/vendor/faiss/faiss/utils/hamming_distance/hamdis-inl.h +0 -87
  375. data/vendor/faiss/faiss/utils/hamming_distance/neon-inl.h +0 -524
  376. data/vendor/faiss/faiss/utils/simdlib.h +0 -42
  377. data/vendor/faiss/faiss/utils/simdlib_avx512.h +0 -365
  378. /data/ext/faiss/{utils_rb.h → utils.h} +0 -0
@@ -8,6 +8,7 @@
8
8
  #include <faiss/IndexHNSW.h>
9
9
 
10
10
  #include <omp.h>
11
+ #include <atomic>
11
12
  #include <cinttypes>
12
13
  #include <cstdio>
13
14
  #include <cstdlib>
@@ -26,14 +27,15 @@
26
27
  #include <faiss/IndexIVFPQ.h>
27
28
  #include <faiss/impl/AuxIndexStructures.h>
28
29
  #include <faiss/impl/FaissAssert.h>
30
+ #include <faiss/impl/FaissException.h>
29
31
  #include <faiss/impl/ResultHandler.h>
30
32
  #include <faiss/impl/VisitedTable.h>
33
+ #include <faiss/impl/hnsw/MinimaxHeap.h>
31
34
  #include <faiss/utils/random.h>
32
35
  #include <faiss/utils/sorting.h>
33
36
 
34
37
  namespace faiss {
35
38
 
36
- using MinimaxHeap = HNSW::MinimaxHeap;
37
39
  using storage_idx_t = HNSW::storage_idx_t;
38
40
  using NodeDistFarther = HNSW::NodeDistFarther;
39
41
 
@@ -45,12 +47,17 @@ HNSWStats hnsw_stats;
45
47
 
46
48
  namespace {
47
49
 
50
+ // Returns the storage's native distance computer. For similarity metrics
51
+ // (e.g. METRIC_INNER_PRODUCT), distance values are real similarity scores
52
+ // (larger = better); HNSW handles the ordering via `hnsw.is_similarity`.
53
+ //
54
+ // NOTE: callers that drive the legacy max-heap-only code paths (notably
55
+ // `search_from_candidates_2` in the IndexHNSW2Level mixed search) cannot
56
+ // consume similarity scores directly; they assume smaller-is-better.
57
+ // Those paths only fire for the (default) L2 IndexHNSW2Level + Index2Layer
58
+ // configuration today, so passing the native DC is safe in practice.
48
59
  DistanceComputer* storage_distance_computer(const Index* storage) {
49
- if (is_similarity_metric(storage->metric_type)) {
50
- return new NegativeDistanceComputer(storage->get_distance_computer());
51
- } else {
52
- return storage->get_distance_computer();
53
- }
60
+ return storage->get_distance_computer();
54
61
  }
55
62
 
56
63
  void hnsw_add_vertices(
@@ -82,10 +89,8 @@ void hnsw_add_vertices(
82
89
  printf(" max_level = %d\n", max_level);
83
90
  }
84
91
 
85
- std::vector<omp_lock_t> locks(ntotal);
86
- for (int i = 0; i < ntotal; i++) {
87
- omp_init_lock(&locks[i]);
88
- }
92
+ auto& locks = index_hnsw.locks;
93
+ locks.prepare(ntotal);
89
94
 
90
95
  // add vectors from highest to lowest level
91
96
  std::vector<int> hist;
@@ -94,10 +99,10 @@ void hnsw_add_vertices(
94
99
  { // make buckets with vectors of the same level
95
100
 
96
101
  // build histogram
97
- for (int i = 0; i < n; i++) {
98
- storage_idx_t pt_id = i + n0;
102
+ for (size_t i = 0; i < n; i++) {
103
+ storage_idx_t pt_id = static_cast<storage_idx_t>(i + n0);
99
104
  int pt_level = hnsw.levels[pt_id] - 1;
100
- while (pt_level >= hist.size()) {
105
+ while (pt_level >= static_cast<int>(hist.size())) {
101
106
  hist.push_back(0);
102
107
  }
103
108
  hist[pt_level]++;
@@ -105,13 +110,13 @@ void hnsw_add_vertices(
105
110
 
106
111
  // accumulate
107
112
  std::vector<int> offsets(hist.size() + 1, 0);
108
- for (int i = 0; i < hist.size() - 1; i++) {
113
+ for (size_t i = 0; i < hist.size() - 1; i++) {
109
114
  offsets[i + 1] = offsets[i] + hist[i];
110
115
  }
111
116
 
112
117
  // bucket sort
113
- for (int i = 0; i < n; i++) {
114
- storage_idx_t pt_id = i + n0;
118
+ for (size_t i = 0; i < n; i++) {
119
+ storage_idx_t pt_id = static_cast<storage_idx_t>(i + n0);
115
120
  int pt_level = hnsw.levels[pt_id] - 1;
116
121
  order[offsets[pt_level]++] = pt_id;
117
122
  }
@@ -123,39 +128,42 @@ void hnsw_add_vertices(
123
128
  { // perform add
124
129
  RandomGenerator rng2(789);
125
130
 
126
- int i1 = n;
131
+ size_t i1 = static_cast<int>(n);
127
132
 
128
- for (int pt_level = hist.size() - 1;
133
+ for (int pt_level = static_cast<int>(hist.size()) - 1;
129
134
  pt_level >= int(!index_hnsw.init_level0);
130
135
  pt_level--) {
131
- int i0 = i1 - hist[pt_level];
136
+ size_t i0 = i1 - hist[pt_level];
132
137
 
133
138
  if (verbose) {
134
- printf("Adding %d elements at level %d\n", i1 - i0, pt_level);
139
+ printf("Adding %zu elements at level %d\n", i1 - i0, pt_level);
135
140
  }
136
141
 
137
142
  // random permutation to get rid of dataset order bias
138
- for (int j = i0; j < i1; j++) {
139
- std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
143
+ for (size_t j = i0; j < i1; j++) {
144
+ std::swap(
145
+ order[j],
146
+ order[j + rng2.rand_int(static_cast<int>(i1 - j))]);
140
147
  }
141
148
 
142
149
  bool interrupt = false;
143
150
 
144
151
  #pragma omp parallel if (i1 > i0 + 100)
145
152
  {
146
- VisitedTable vt(ntotal, hnsw.use_visited_hashset);
153
+ std::unique_ptr<VisitedTable> vt =
154
+ VisitedTable::create(ntotal, hnsw.use_visited_hashset);
147
155
 
148
156
  std::unique_ptr<DistanceComputer> dis(
149
157
  storage_distance_computer(index_hnsw.storage));
150
- int prev_display =
151
- verbose && omp_get_thread_num() == 0 ? 0 : -1;
158
+ bool do_display = verbose && omp_get_thread_num() == 0;
159
+ size_t prev_display = 0;
152
160
  size_t counter = 0;
153
161
 
154
162
  // here we should do schedule(dynamic) but this segfaults for
155
163
  // some versions of LLVM. The performance impact should not be
156
164
  // too large when (i1 - i0) / num_threads >> 1
157
165
  #pragma omp for schedule(static)
158
- for (int i = i0; i < i1; i++) {
166
+ for (int64_t i = i0; i < i1; i++) {
159
167
  storage_idx_t pt_id = order[i];
160
168
  dis->set_query(x + (pt_id - n0) * d);
161
169
 
@@ -169,12 +177,12 @@ void hnsw_add_vertices(
169
177
  pt_level,
170
178
  pt_id,
171
179
  locks,
172
- vt,
180
+ *vt,
173
181
  index_hnsw.keep_max_size_level0 && (pt_level == 0));
174
182
 
175
- if (prev_display >= 0 && i - i0 > prev_display + 10000) {
183
+ if (do_display && i - i0 > prev_display + 10000) {
176
184
  prev_display = i - i0;
177
- printf(" %d / %d\r", i - i0, i1 - i0);
185
+ printf(" %zu / %zu\r", i - i0, i1 - i0);
178
186
  fflush(stdout);
179
187
  }
180
188
  if (counter % check_period == 0) {
@@ -199,9 +207,8 @@ void hnsw_add_vertices(
199
207
  if (verbose) {
200
208
  printf("Done in %.3f ms\n", getmillisecs() - t0);
201
209
  }
202
-
203
- for (int i = 0; i < ntotal; i++) {
204
- omp_destroy_lock(&locks[i]);
210
+ if (!index_hnsw.retain_locks) {
211
+ locks.clear();
205
212
  }
206
213
  }
207
214
 
@@ -211,12 +218,17 @@ void hnsw_add_vertices(
211
218
  * IndexHNSW implementation
212
219
  **************************************************************/
213
220
 
214
- IndexHNSW::IndexHNSW(int d, int M, MetricType metric)
215
- : Index(d, metric), hnsw(M) {}
221
+ IndexHNSW::IndexHNSW(int d_in, int M, MetricType metric)
222
+ : Index(d_in, metric), hnsw(M) {
223
+ hnsw.is_similarity = is_similarity_metric(metric);
224
+ }
216
225
 
217
- IndexHNSW::IndexHNSW(Index* storage, int M)
218
- : Index(storage->d, storage->metric_type), hnsw(M), storage(storage) {
226
+ IndexHNSW::IndexHNSW(Index* storage_in, int M)
227
+ : Index(storage_in->d, storage_in->metric_type),
228
+ hnsw(M),
229
+ storage(storage_in) {
219
230
  metric_arg = storage->metric_arg;
231
+ hnsw.is_similarity = is_similarity_metric(metric_type);
220
232
  }
221
233
 
222
234
  IndexHNSW::~IndexHNSW() {
@@ -263,28 +275,48 @@ void hnsw_search(
263
275
 
264
276
  for (idx_t i0 = 0; i0 < n; i0 += check_period) {
265
277
  idx_t i1 = std::min(i0 + check_period, n);
278
+ std::exception_ptr ex;
279
+ std::atomic<bool> interrupt{false};
266
280
 
267
281
  #pragma omp parallel if (i1 - i0 > 1)
268
282
  {
269
- VisitedTable vt(index->ntotal, hnsw.use_visited_hashset);
270
- typename BlockResultHandler::SingleResultHandler res(bres);
271
-
272
- std::unique_ptr<DistanceComputer> dis(
273
- storage_distance_computer(index->storage));
283
+ std::unique_ptr<VisitedTable> vt;
284
+ std::unique_ptr<typename BlockResultHandler::SingleResultHandler>
285
+ res;
286
+ std::unique_ptr<DistanceComputer> dis;
287
+ try {
288
+ vt = VisitedTable::create(
289
+ index->ntotal, hnsw.use_visited_hashset);
290
+ res = std::make_unique<
291
+ typename BlockResultHandler::SingleResultHandler>(bres);
292
+ dis.reset(storage_distance_computer(index->storage));
293
+ } catch (...) {
294
+ omp_capture_exception(ex, [&] { interrupt = true; });
295
+ }
274
296
 
275
297
  #pragma omp for reduction(+ : n1, n2, ndis, nhops) schedule(guided)
276
298
  for (idx_t i = i0; i < i1; i++) {
277
- res.begin(i);
278
- dis->set_query(x + i * index->d);
279
-
280
- HNSWStats stats = hnsw.search(*dis, index, res, vt, params);
281
- n1 += stats.n1;
282
- n2 += stats.n2;
283
- ndis += stats.ndis;
284
- nhops += stats.nhops;
285
- res.end();
299
+ if (interrupt.load(std::memory_order_relaxed)) {
300
+ continue;
301
+ }
302
+ try {
303
+ res->begin(i);
304
+ dis->set_query(x + i * index->d);
305
+
306
+ HNSWStats stats =
307
+ hnsw.search(*dis, index, *res, *vt, params);
308
+ n1 += stats.n1;
309
+ n2 += stats.n2;
310
+ ndis += stats.ndis;
311
+ nhops += stats.nhops;
312
+ res->end();
313
+ vt->advance();
314
+ } catch (...) {
315
+ omp_capture_exception(ex, [&] { interrupt = true; });
316
+ }
286
317
  }
287
318
  }
319
+ omp_rethrow_if_exception(ex);
288
320
  InterruptCallback::check();
289
321
  }
290
322
 
@@ -302,16 +334,14 @@ void IndexHNSW::search(
302
334
  const SearchParameters* params) const {
303
335
  FAISS_THROW_IF_NOT(k > 0);
304
336
 
305
- using RH = HeapBlockResultHandler<HNSW::C>;
306
- RH bres(n, distances, labels, k);
307
-
308
- hnsw_search(this, n, x, bres, params);
309
-
310
337
  if (is_similarity_metric(this->metric_type)) {
311
- // we need to revert the negated distances
312
- for (size_t i = 0; i < k * n; i++) {
313
- distances[i] = -distances[i];
314
- }
338
+ using RH = HeapBlockResultHandler<HNSW::C_similarity>;
339
+ RH bres(n, distances, labels, k);
340
+ hnsw_search(this, n, x, bres, params);
341
+ } else {
342
+ using RH = HeapBlockResultHandler<HNSW::C_distance>;
343
+ RH bres(n, distances, labels, k);
344
+ hnsw_search(this, n, x, bres, params);
315
345
  }
316
346
  }
317
347
 
@@ -321,16 +351,14 @@ void IndexHNSW::range_search(
321
351
  float radius,
322
352
  RangeSearchResult* result,
323
353
  const SearchParameters* params) const {
324
- using RH = RangeSearchBlockResultHandler<HNSW::C>;
325
- RH bres(result, is_similarity_metric(metric_type) ? -radius : radius);
326
-
327
- hnsw_search(this, n, x, bres, params);
328
-
329
- if (is_similarity_metric(this->metric_type)) {
330
- // we need to revert the negated distances
331
- for (size_t i = 0; i < result->lims[result->nq]; i++) {
332
- result->distances[i] = -result->distances[i];
333
- }
354
+ if (is_similarity_metric(metric_type)) {
355
+ using RH = RangeSearchBlockResultHandler<HNSW::C_similarity>;
356
+ RH bres(result, radius);
357
+ hnsw_search(this, n, x, bres, params);
358
+ } else {
359
+ using RH = RangeSearchBlockResultHandler<HNSW::C_distance>;
360
+ RH bres(result, radius);
361
+ hnsw_search(this, n, x, bres, params);
334
362
  }
335
363
  }
336
364
 
@@ -338,8 +366,13 @@ void IndexHNSW::search1(
338
366
  const float* x,
339
367
  ResultHandler& handler,
340
368
  SearchParameters* params) const {
341
- SingleQueryBlockResultHandler<HNSW::C, false> bres(handler);
342
- hnsw_search(this, 1, x, bres, params);
369
+ if (is_similarity_metric(metric_type)) {
370
+ SingleQueryBlockResultHandler<HNSW::C_similarity, false> bres(handler);
371
+ hnsw_search(this, 1, x, bres, params);
372
+ } else {
373
+ SingleQueryBlockResultHandler<HNSW::C_distance, false> bres(handler);
374
+ hnsw_search(this, 1, x, bres, params);
375
+ }
343
376
  }
344
377
 
345
378
  void IndexHNSW::add(idx_t n, const float* x) {
@@ -347,15 +380,22 @@ void IndexHNSW::add(idx_t n, const float* x) {
347
380
  storage,
348
381
  "Please use IndexHNSWFlat (or variants) instead of IndexHNSW directly");
349
382
  FAISS_THROW_IF_NOT(is_trained);
350
- int n0 = ntotal;
383
+ size_t n0 = ntotal;
351
384
  storage->add(n, x);
352
385
  ntotal = storage->ntotal;
353
386
 
354
- hnsw_add_vertices(*this, n0, n, x, verbose, hnsw.levels.size() == ntotal);
387
+ hnsw_add_vertices(
388
+ *this,
389
+ n0,
390
+ n,
391
+ x,
392
+ verbose,
393
+ hnsw.levels.size() == static_cast<size_t>(ntotal));
355
394
  }
356
395
 
357
396
  void IndexHNSW::reset() {
358
397
  hnsw.reset();
398
+ locks.clear();
359
399
  storage->reset();
360
400
  ntotal = 0;
361
401
  }
@@ -427,48 +467,66 @@ void IndexHNSW::search_level_0(
427
467
  FAISS_THROW_IF_NOT(k > 0);
428
468
  FAISS_THROW_IF_NOT(nprobe > 0);
429
469
 
430
- storage_idx_t ntotal = hnsw.levels.size();
470
+ size_t hnsw_ntotal = hnsw.levels.size();
431
471
 
432
- using RH = HeapBlockResultHandler<HNSW::C>;
433
- RH bres(n, distances, labels, k);
472
+ auto run = [&]<class C>() {
473
+ using RH = HeapBlockResultHandler<C>;
474
+ RH bres(n, distances, labels, k);
434
475
 
476
+ std::exception_ptr ex;
477
+ std::atomic<bool> interrupt{false};
435
478
  #pragma omp parallel
436
- {
437
- std::unique_ptr<DistanceComputer> qdis(
438
- storage_distance_computer(storage));
439
- HNSWStats search_stats;
440
- VisitedTable vt(ntotal, hnsw.use_visited_hashset);
441
- RH::SingleResultHandler res(bres);
479
+ {
480
+ std::unique_ptr<DistanceComputer> qdis;
481
+ HNSWStats search_stats;
482
+ std::unique_ptr<VisitedTable> vt;
483
+ std::unique_ptr<typename RH::SingleResultHandler> res;
484
+ try {
485
+ qdis.reset(storage_distance_computer(storage));
486
+ vt = VisitedTable::create(
487
+ hnsw_ntotal, hnsw.use_visited_hashset);
488
+ res = std::make_unique<typename RH::SingleResultHandler>(bres);
489
+ } catch (...) {
490
+ omp_capture_exception(ex, [&] { interrupt = true; });
491
+ }
442
492
 
443
493
  #pragma omp for
444
- for (idx_t i = 0; i < n; i++) {
445
- res.begin(i);
446
- qdis->set_query(x + i * d);
447
-
448
- hnsw.search_level_0(
449
- *qdis.get(),
450
- res,
451
- nprobe,
452
- nearest + i * nprobe,
453
- nearest_d + i * nprobe,
454
- search_type,
455
- search_stats,
456
- vt,
457
- params);
458
- res.end();
459
- vt.advance();
460
- }
494
+ for (idx_t i = 0; i < n; i++) {
495
+ if (interrupt.load(std::memory_order_relaxed)) {
496
+ continue;
497
+ }
498
+ try {
499
+ res->begin(i);
500
+ qdis->set_query(x + i * d);
501
+
502
+ hnsw.search_level_0(
503
+ *qdis.get(),
504
+ *res,
505
+ nprobe,
506
+ nearest + i * nprobe,
507
+ nearest_d + i * nprobe,
508
+ search_type,
509
+ search_stats,
510
+ *vt,
511
+ params);
512
+ res->end();
513
+ vt->advance();
514
+ } catch (...) {
515
+ omp_capture_exception(ex, [&] { interrupt = true; });
516
+ }
517
+ }
461
518
  #pragma omp critical
462
- {
463
- hnsw_stats.combine(search_stats);
519
+ {
520
+ hnsw_stats.combine(search_stats);
521
+ }
464
522
  }
465
- }
523
+ omp_rethrow_if_exception(ex);
524
+ };
525
+
466
526
  if (is_similarity_metric(this->metric_type)) {
467
- // we need to revert the negated distances
468
- #pragma omp parallel for
469
- for (int64_t i = 0; i < k * n; i++) {
470
- distances[i] = -distances[i];
471
- }
527
+ run.template operator()<HNSW::C_similarity>();
528
+ } else {
529
+ run.template operator()<HNSW::C_distance>();
472
530
  }
473
531
  }
474
532
 
@@ -487,8 +545,8 @@ void IndexHNSW::init_level_0_from_knngraph(
487
545
 
488
546
  std::priority_queue<NodeDistFarther> initial_list;
489
547
 
490
- for (size_t j = 0; j < k; j++) {
491
- int v1 = I[i * k + j];
548
+ for (int j = 0; j < k; j++) {
549
+ int v1 = static_cast<int>(I[i * k + j]);
492
550
  if (v1 == i) {
493
551
  continue;
494
552
  }
@@ -518,14 +576,12 @@ void IndexHNSW::init_level_0_from_entry_points(
518
576
  int n,
519
577
  const storage_idx_t* points,
520
578
  const storage_idx_t* nearests) {
521
- std::vector<omp_lock_t> locks(ntotal);
522
- for (int i = 0; i < ntotal; i++) {
523
- omp_init_lock(&locks[i]);
524
- }
579
+ locks.prepare(ntotal);
525
580
 
526
581
  #pragma omp parallel
527
582
  {
528
- VisitedTable vt(ntotal, hnsw.use_visited_hashset);
583
+ std::unique_ptr<VisitedTable> vt =
584
+ VisitedTable::create(ntotal, hnsw.use_visited_hashset);
529
585
 
530
586
  std::unique_ptr<DistanceComputer> dis(
531
587
  storage_distance_computer(storage));
@@ -539,7 +595,7 @@ void IndexHNSW::init_level_0_from_entry_points(
539
595
  dis->set_query(vec.data());
540
596
 
541
597
  hnsw.add_links_starting_from(
542
- *dis, pt_id, nearest, (*dis)(nearest), 0, locks.data(), vt);
598
+ *dis, pt_id, nearest, (*dis)(nearest), 0, locks, *vt);
543
599
 
544
600
  if (verbose && i % 10000 == 0) {
545
601
  printf(" %d / %d\r", i, n);
@@ -551,8 +607,8 @@ void IndexHNSW::init_level_0_from_entry_points(
551
607
  printf("\n");
552
608
  }
553
609
 
554
- for (int i = 0; i < ntotal; i++) {
555
- omp_destroy_lock(&locks[i]);
610
+ if (!retain_locks) {
611
+ locks.clear();
556
612
  }
557
613
  }
558
614
 
@@ -595,7 +651,7 @@ void IndexHNSW::link_singletons() {
595
651
 
596
652
  std::vector<bool> seen(ntotal);
597
653
 
598
- for (size_t i = 0; i < ntotal; i++) {
654
+ for (idx_t i = 0; i < ntotal; i++) {
599
655
  size_t begin, end;
600
656
  hnsw.neighbor_range(i, 0, &begin, &end);
601
657
  for (size_t j = begin; j < end; j++) {
@@ -624,7 +680,7 @@ void IndexHNSW::link_singletons() {
624
680
  n_sing_l1);
625
681
 
626
682
  std::vector<float> recons(singletons.size() * d);
627
- for (int i = 0; i < singletons.size(); i++) {
683
+ for (size_t i = 0; i < singletons.size(); i++) {
628
684
  FAISS_ASSERT(false); // not implemented
629
685
  }
630
686
  }
@@ -649,10 +705,10 @@ IndexHNSWFlat::IndexHNSWFlat() {
649
705
  is_trained = true;
650
706
  }
651
707
 
652
- IndexHNSWFlat::IndexHNSWFlat(int d, int M, MetricType metric)
708
+ IndexHNSWFlat::IndexHNSWFlat(int d_in, int M, MetricType metric)
653
709
  : IndexHNSW(
654
- (metric == METRIC_L2) ? new IndexFlatL2(d)
655
- : new IndexFlat(d, metric),
710
+ (metric == METRIC_L2) ? new IndexFlatL2(d_in)
711
+ : new IndexFlat(d_in, metric),
656
712
  M) {
657
713
  own_fields = true;
658
714
  is_trained = true;
@@ -663,17 +719,20 @@ IndexHNSWFlat::IndexHNSWFlat(int d, int M, MetricType metric)
663
719
  **************************************************************/
664
720
 
665
721
  IndexHNSWFlatPanorama::IndexHNSWFlatPanorama()
666
- : IndexHNSWFlat(), cum_sums(), pano(0, 1, 1), num_panorama_levels(0) {}
722
+ : IndexHNSWFlat(),
723
+ cum_sums(),
724
+ pano(sizeof(float), 1, 1),
725
+ num_panorama_levels(0) {}
667
726
 
668
727
  IndexHNSWFlatPanorama::IndexHNSWFlatPanorama(
669
- int d,
728
+ int d_in,
670
729
  int M,
671
- int num_panorama_levels,
730
+ int num_panorama_levels_in,
672
731
  MetricType metric)
673
- : IndexHNSWFlat(d, M, metric),
732
+ : IndexHNSWFlat(d_in, M, metric),
674
733
  cum_sums(),
675
- pano(d * sizeof(float), num_panorama_levels, 1),
676
- num_panorama_levels(num_panorama_levels) {
734
+ pano(d_in * sizeof(float), num_panorama_levels_in, 1),
735
+ num_panorama_levels(num_panorama_levels_in) {
677
736
  // For now, we only support L2 distance.
678
737
  // Supporting dot product and cosine distance is a trivial addition
679
738
  // left for future work.
@@ -718,12 +777,12 @@ void IndexHNSWFlatPanorama::permute_entries(const idx_t* perm) {
718
777
  IndexHNSWPQ::IndexHNSWPQ() = default;
719
778
 
720
779
  IndexHNSWPQ::IndexHNSWPQ(
721
- int d,
780
+ int d_in,
722
781
  int pq_m,
723
782
  int M,
724
783
  int pq_nbits,
725
784
  MetricType metric)
726
- : IndexHNSW(new IndexPQ(d, pq_m, pq_nbits, metric), M) {
785
+ : IndexHNSW(new IndexPQ(d_in, pq_m, pq_nbits, metric), M) {
727
786
  own_fields = true;
728
787
  is_trained = false;
729
788
  }
@@ -738,11 +797,11 @@ void IndexHNSWPQ::train(idx_t n, const float* x) {
738
797
  **************************************************************/
739
798
 
740
799
  IndexHNSWSQ::IndexHNSWSQ(
741
- int d,
800
+ int d_in,
742
801
  ScalarQuantizer::QuantizerType qtype,
743
802
  int M,
744
803
  MetricType metric)
745
- : IndexHNSW(new IndexScalarQuantizer(d, qtype, metric), M) {
804
+ : IndexHNSW(new IndexScalarQuantizer(d_in, qtype, metric), M) {
746
805
  is_trained = this->storage->is_trained;
747
806
  own_fields = true;
748
807
  }
@@ -777,7 +836,7 @@ int search_from_candidates_2(
777
836
  idx_t* I,
778
837
  float* D,
779
838
  MinimaxHeap& candidates,
780
- VisitedTable& vt,
839
+ VisitedTableVector& vt,
781
840
  HNSWStats& stats,
782
841
  int level,
783
842
  int nres_in = 0) {
@@ -855,8 +914,11 @@ void IndexHNSW2Level::search(
855
914
 
856
915
  const IndexIVFPQ* index_ivfpq =
857
916
  dynamic_cast<const IndexIVFPQ*>(storage);
917
+ FAISS_THROW_IF_NOT_MSG(
918
+ index_ivfpq,
919
+ "IndexHNSW2Level mixed search requires IndexIVFPQ storage");
858
920
 
859
- int nprobe = index_ivfpq->nprobe;
921
+ size_t nprobe = index_ivfpq->nprobe;
860
922
 
861
923
  std::unique_ptr<idx_t[]> coarse_assign(new idx_t[n * nprobe]);
862
924
  std::unique_ptr<float[]> coarse_dis(new float[n * nprobe]);
@@ -874,72 +936,87 @@ void IndexHNSW2Level::search(
874
936
  labels,
875
937
  false);
876
938
 
939
+ std::exception_ptr ex;
940
+ std::atomic<bool> interrupt{false};
877
941
  #pragma omp parallel
878
942
  {
879
943
  // visited table (not hash set) for tri-state flags.
880
- VisitedTable vt(ntotal, /*use_hashset=*/false);
881
- std::unique_ptr<DistanceComputer> dis(
882
- storage_distance_computer(storage));
883
-
944
+ std::unique_ptr<VisitedTable> vt;
945
+ std::unique_ptr<DistanceComputer> dis;
884
946
  constexpr int candidates_size = 1;
885
- MinimaxHeap candidates(candidates_size);
947
+ std::unique_ptr<MinimaxHeap> candidates;
948
+ try {
949
+ vt = VisitedTable::create(ntotal, /*use_hashset=*/false);
950
+ dis.reset(storage_distance_computer(storage));
951
+ candidates = std::make_unique<MinimaxHeap>(candidates_size);
952
+ } catch (...) {
953
+ omp_capture_exception(ex, [&] { interrupt = true; });
954
+ }
886
955
 
887
956
  #pragma omp for reduction(+ : n1, n2, ndis, nhops)
888
957
  for (idx_t i = 0; i < n; i++) {
889
- idx_t* idxi = labels + i * k;
890
- float* simi = distances + i * k;
891
- dis->set_query(x + i * d);
892
-
893
- // mark all inverted list elements as visited
958
+ if (interrupt.load(std::memory_order_relaxed)) {
959
+ continue;
960
+ }
961
+ try {
962
+ idx_t* idxi = labels + i * k;
963
+ float* simi = distances + i * k;
964
+ dis->set_query(x + i * d);
965
+
966
+ // mark all inverted list elements as visited
967
+ for (size_t j = 0; j < nprobe; j++) {
968
+ idx_t key = coarse_assign[j + i * nprobe];
969
+ if (key < 0) {
970
+ break;
971
+ }
972
+ size_t list_length = index_ivfpq->get_list_size(key);
973
+ const idx_t* ids = index_ivfpq->invlists->get_ids(key);
894
974
 
895
- for (int j = 0; j < nprobe; j++) {
896
- idx_t key = coarse_assign[j + i * nprobe];
897
- if (key < 0) {
898
- break;
975
+ for (size_t jj = 0; jj < list_length; jj++) {
976
+ vt->set(ids[jj]);
977
+ }
899
978
  }
900
- size_t list_length = index_ivfpq->get_list_size(key);
901
- const idx_t* ids = index_ivfpq->invlists->get_ids(key);
902
979
 
903
- for (int jj = 0; jj < list_length; jj++) {
904
- vt.set(ids[jj]);
980
+ candidates->clear();
981
+
982
+ for (int j = 0; j < k; j++) {
983
+ if (idxi[j] < 0) {
984
+ break;
985
+ }
986
+ candidates->push(
987
+ static_cast<storage_idx_t>(idxi[j]), simi[j]);
905
988
  }
906
- }
907
989
 
908
- candidates.clear();
990
+ // reorder from sorted to heap
991
+ maxheap_heapify(k, simi, idxi, simi, idxi, k);
909
992
 
910
- for (int j = 0; j < k; j++) {
911
- if (idxi[j] < 0) {
912
- break;
913
- }
914
- candidates.push(idxi[j], simi[j]);
993
+ HNSWStats search_stats;
994
+ search_from_candidates_2(
995
+ hnsw,
996
+ *dis,
997
+ k,
998
+ idxi,
999
+ simi,
1000
+ *candidates,
1001
+ static_cast<VisitedTableVector&>(*vt),
1002
+ search_stats,
1003
+ 0,
1004
+ k);
1005
+ n1 += search_stats.n1;
1006
+ n2 += search_stats.n2;
1007
+ ndis += search_stats.ndis;
1008
+ nhops += search_stats.nhops;
1009
+
1010
+ vt->advance();
1011
+ vt->advance();
1012
+
1013
+ maxheap_reorder(k, simi, idxi);
1014
+ } catch (...) {
1015
+ omp_capture_exception(ex, [&] { interrupt = true; });
915
1016
  }
916
-
917
- // reorder from sorted to heap
918
- maxheap_heapify(k, simi, idxi, simi, idxi, k);
919
-
920
- HNSWStats search_stats;
921
- search_from_candidates_2(
922
- hnsw,
923
- *dis,
924
- k,
925
- idxi,
926
- simi,
927
- candidates,
928
- vt,
929
- search_stats,
930
- 0,
931
- k);
932
- n1 += search_stats.n1;
933
- n2 += search_stats.n2;
934
- ndis += search_stats.ndis;
935
- nhops += search_stats.nhops;
936
-
937
- vt.advance();
938
- vt.advance();
939
-
940
- maxheap_reorder(k, simi, idxi);
941
1017
  }
942
1018
  }
1019
+ omp_rethrow_if_exception(ex);
943
1020
 
944
1021
  hnsw_stats.combine({n1, n2, ndis, nhops});
945
1022
  }
@@ -976,11 +1053,11 @@ IndexHNSWCagra::IndexHNSWCagra() {
976
1053
  }
977
1054
 
978
1055
  IndexHNSWCagra::IndexHNSWCagra(
979
- int d,
1056
+ int d_in,
980
1057
  int M,
981
1058
  MetricType metric,
982
1059
  NumericType numeric_type)
983
- : IndexHNSW(d, M, metric) {
1060
+ : IndexHNSW(d_in, M, metric) {
984
1061
  FAISS_THROW_IF_NOT_MSG(
985
1062
  ((metric == METRIC_L2) || (metric == METRIC_INNER_PRODUCT)),
986
1063
  "unsupported metric type for IndexHNSWCagra");
@@ -1024,31 +1101,51 @@ void IndexHNSWCagra::search(
1024
1101
  if (!base_level_only) {
1025
1102
  IndexHNSW::search(n, x, k, distances, labels, params);
1026
1103
  } else {
1104
+ if (ntotal == 0) {
1105
+ std::fill(
1106
+ distances,
1107
+ distances + n * k,
1108
+ std::numeric_limits<float>::max());
1109
+ std::fill(labels, labels + n * k, -1);
1110
+ return;
1111
+ }
1027
1112
  std::vector<storage_idx_t> nearest(n);
1028
1113
  std::vector<float> nearest_d(n);
1029
1114
 
1030
- #pragma omp for
1031
- for (idx_t i = 0; i < n; i++) {
1032
- std::unique_ptr<DistanceComputer> dis(
1033
- storage_distance_computer(this->storage));
1034
- dis->set_query(x + i * d);
1035
- nearest[i] = -1;
1036
- nearest_d[i] = std::numeric_limits<float>::max();
1037
-
1038
- std::random_device rd;
1039
- std::mt19937 gen(rd());
1040
- std::uniform_int_distribution<idx_t> distrib(0, this->ntotal - 1);
1041
-
1042
- for (idx_t j = 0; j < num_base_level_search_entrypoints; j++) {
1043
- auto idx = distrib(gen);
1044
- auto distance = (*dis)(idx);
1045
- if (distance < nearest_d[i]) {
1046
- nearest[i] = idx;
1047
- nearest_d[i] = distance;
1115
+ auto pick_entrypoints = [&]<class C>() {
1116
+ #pragma omp parallel for
1117
+ for (idx_t i = 0; i < n; i++) {
1118
+ std::unique_ptr<DistanceComputer> dis(
1119
+ storage_distance_computer(this->storage));
1120
+ dis->set_query(x + i * d);
1121
+ nearest[i] = -1;
1122
+ // C::neutral() is the "worst possible" value: +inf for
1123
+ // CMax (distance) and -inf for CMin (similarity). The
1124
+ // first real candidate will always be strictly better.
1125
+ nearest_d[i] = C::neutral();
1126
+
1127
+ std::random_device rd;
1128
+ std::mt19937 gen(rd());
1129
+ std::uniform_int_distribution<idx_t> distrib(
1130
+ 0, this->ntotal - 1);
1131
+
1132
+ for (idx_t j = 0; j < num_base_level_search_entrypoints; j++) {
1133
+ auto idx = distrib(gen);
1134
+ auto distance = (*dis)(idx);
1135
+ if (C::cmp(nearest_d[i], distance)) {
1136
+ nearest[i] = static_cast<storage_idx_t>(idx);
1137
+ nearest_d[i] = distance;
1138
+ }
1048
1139
  }
1140
+ FAISS_THROW_IF_NOT_MSG(
1141
+ nearest[i] >= 0, "Could not find a valid entrypoint.");
1049
1142
  }
1050
- FAISS_THROW_IF_NOT_MSG(
1051
- nearest[i] >= 0, "Could not find a valid entrypoint.");
1143
+ };
1144
+
1145
+ if (is_similarity_metric(metric_type)) {
1146
+ pick_entrypoints.template operator()<HNSW::C_similarity>();
1147
+ } else {
1148
+ pick_entrypoints.template operator()<HNSW::C_distance>();
1052
1149
  }
1053
1150
 
1054
1151
  search_level_0(
@@ -1065,6 +1162,77 @@ void IndexHNSWCagra::search(
1065
1162
  }
1066
1163
  }
1067
1164
 
1165
+ void IndexHNSWCagra::range_search(
1166
+ idx_t n,
1167
+ const float* x,
1168
+ float radius,
1169
+ RangeSearchResult* result,
1170
+ const SearchParameters* params) const {
1171
+ if (!base_level_only) {
1172
+ IndexHNSW::range_search(n, x, radius, result, params);
1173
+ return;
1174
+ }
1175
+
1176
+ auto run = [&]<class C>() {
1177
+ const HNSW& hnsw = this->hnsw;
1178
+ size_t n1 = 0, n2 = 0, ndis = 0, nhops = 0;
1179
+ RangeSearchPartialResult pres(result);
1180
+
1181
+ for (idx_t i = 0; i < n; i++) {
1182
+ std::unique_ptr<DistanceComputer> dis(
1183
+ storage_distance_computer(storage));
1184
+ dis->set_query(x + i * d);
1185
+
1186
+ storage_idx_t nearest = -1;
1187
+ // C::neutral() is the "worst possible" value under C: +inf for
1188
+ // CMax (distance) and -inf for CMin (similarity). The first
1189
+ // real candidate will always be strictly better.
1190
+ float nearest_d = C::neutral();
1191
+
1192
+ std::random_device rd;
1193
+ std::mt19937 gen(rd());
1194
+ std::uniform_int_distribution<idx_t> distrib(0, ntotal - 1);
1195
+
1196
+ for (idx_t j = 0; j < num_base_level_search_entrypoints; j++) {
1197
+ auto idx = distrib(gen);
1198
+ auto distance = (*dis)(idx);
1199
+ // C::cmp(nearest_d, distance) is true iff distance is
1200
+ // strictly better than the current nearest_d.
1201
+ if (C::cmp(nearest_d, distance)) {
1202
+ nearest = idx;
1203
+ nearest_d = distance;
1204
+ }
1205
+ }
1206
+ FAISS_THROW_IF_NOT_MSG(
1207
+ nearest >= 0, "Could not find a valid entrypoint.");
1208
+
1209
+ RangeQueryResult& qres = pres.new_result(i);
1210
+ RangeResultHandler<C> res(&qres, radius);
1211
+ std::unique_ptr<VisitedTable> vt =
1212
+ VisitedTable::create(ntotal, hnsw.use_visited_hashset);
1213
+ HNSWStats stats;
1214
+ hnsw.search_level_0(
1215
+ *dis, res, 1, &nearest, &nearest_d, 1, stats, *vt, params);
1216
+ n1 += stats.n1;
1217
+ n2 += stats.n2;
1218
+ ndis += stats.ndis;
1219
+ nhops += stats.nhops;
1220
+ }
1221
+
1222
+ pres.set_lims();
1223
+ result->do_allocation();
1224
+ pres.copy_result();
1225
+
1226
+ hnsw_stats.combine({n1, n2, ndis, nhops});
1227
+ };
1228
+
1229
+ if (is_similarity_metric(metric_type)) {
1230
+ run.template operator()<HNSW::C_similarity>();
1231
+ } else {
1232
+ run.template operator()<HNSW::C_distance>();
1233
+ }
1234
+ }
1235
+
1068
1236
  faiss::NumericType IndexHNSWCagra::get_numeric_type() const {
1069
1237
  return numeric_type_;
1070
1238
  }