faiss 0.6.2 → 0.6.4

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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/Clustering.h +8 -0
  7. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  8. data/vendor/faiss/faiss/Index.h +2 -2
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  10. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  11. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +24 -4
  12. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +16 -145
  13. data/vendor/faiss/faiss/IndexBinaryHNSW.h +0 -6
  14. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  15. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +8 -18
  16. data/vendor/faiss/faiss/IndexBinaryIVF.h +8 -1
  17. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  18. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  19. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  20. data/vendor/faiss/faiss/IndexFlat.cpp +21 -54
  21. data/vendor/faiss/faiss/IndexFlat.h +2 -2
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +311 -102
  23. data/vendor/faiss/faiss/IndexHNSW.h +31 -7
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +26 -8
  25. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +36 -10
  27. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  28. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  29. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  30. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  31. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  32. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  33. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  34. data/vendor/faiss/faiss/IndexIVFPQ.cpp +49 -23
  35. data/vendor/faiss/faiss/IndexIVFPQ.h +11 -0
  36. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  37. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  38. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  39. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  40. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  41. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  42. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  43. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  44. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  45. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  46. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  47. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  48. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  49. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  50. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  51. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  52. data/vendor/faiss/faiss/IndexShards.cpp +5 -5
  53. data/vendor/faiss/faiss/IndexShardsIVF.cpp +6 -5
  54. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  55. data/vendor/faiss/faiss/SuperKMeans.cpp +286 -247
  56. data/vendor/faiss/faiss/SuperKMeans.h +33 -2
  57. data/vendor/faiss/faiss/VectorTransform.cpp +71 -2
  58. data/vendor/faiss/faiss/VectorTransform.h +3 -0
  59. data/vendor/faiss/faiss/clone_index.cpp +8 -0
  60. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  61. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  62. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +1 -5
  63. data/vendor/faiss/faiss/gpu/GpuDistance.h +2 -5
  64. data/vendor/faiss/faiss/gpu/GpuIndex.h +38 -16
  65. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +71 -1
  66. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  67. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  68. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  69. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  70. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  72. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  73. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  74. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +1 -1
  75. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +7 -4
  76. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  77. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  78. data/vendor/faiss/faiss/impl/HNSW.cpp +528 -267
  79. data/vendor/faiss/faiss/impl/HNSW.h +46 -7
  80. data/vendor/faiss/faiss/impl/IDSelector.h +44 -0
  81. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  82. data/vendor/faiss/faiss/impl/NNDescent.cpp +10 -3
  83. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  84. data/vendor/faiss/faiss/impl/Panorama.h +20 -9
  85. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  86. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +38 -26
  87. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +45 -37
  88. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  89. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +239 -72
  90. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +66 -4
  91. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +4 -13
  92. data/vendor/faiss/faiss/impl/ResultHandler.h +34 -34
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +287 -84
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +26 -10
  95. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  96. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  97. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  98. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +90 -14
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  100. data/vendor/faiss/faiss/impl/expanded_scanners.h +5 -1
  101. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +1 -0
  102. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  103. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  104. data/vendor/faiss/faiss/impl/index_read.cpp +491 -50
  105. data/vendor/faiss/faiss/impl/index_write.cpp +86 -30
  106. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  107. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  108. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  109. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  110. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +73 -39
  111. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  112. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  113. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  114. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +4 -4
  115. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  116. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  117. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  118. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  119. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  120. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +887 -0
  121. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +2 -2
  122. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  123. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +90 -24
  124. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  125. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  126. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  127. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +169 -13
  128. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +125 -26
  129. data/vendor/faiss/faiss/impl/simd_dispatch.h +70 -31
  130. data/vendor/faiss/faiss/index_factory.cpp +40 -7
  131. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  132. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +9 -6
  133. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +29 -8
  134. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  135. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  136. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  137. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  138. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  139. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  140. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +47 -5
  141. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +23 -3
  142. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +4 -2
  143. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +2 -1
  144. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +10 -4
  145. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +2 -1
  146. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  147. data/vendor/faiss/faiss/utils/distances.cpp +30 -11
  148. data/vendor/faiss/faiss/utils/distances_dispatch.h +30 -24
  149. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +1 -1
  150. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  151. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  152. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  153. data/vendor/faiss/faiss/utils/hamming.cpp +16 -10
  154. data/vendor/faiss/faiss/utils/hamming.h +10 -1
  155. data/vendor/faiss/faiss/utils/hamming_distance/common.h +14 -3
  156. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +24 -0
  157. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +1 -1
  158. data/vendor/faiss/faiss/utils/hamming_distance/{hamming_computer-avx512_spr.h → hamming_computer-avx512_vpopcnt.h} +85 -24
  159. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +141 -0
  160. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  161. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  162. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +194 -30
  163. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  164. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  165. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +198 -18
  166. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  167. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +330 -40
  168. data/vendor/faiss/faiss/utils/simd_impl/{rabitq_avx512_spr.cpp → rabitq_avx512_vpopcnt.cpp} +112 -23
  169. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  170. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  171. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_dispatch.h +2 -7
  172. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels.h +6 -1
  173. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +34 -0
  174. data/vendor/faiss/faiss/utils/simd_levels.cpp +196 -47
  175. data/vendor/faiss/faiss/utils/simd_levels.h +33 -8
  176. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  177. metadata +21 -5
  178. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_spr.cpp +0 -15
@@ -7,6 +7,7 @@
7
7
 
8
8
  // -*- c++ -*-
9
9
 
10
+ #include <cmath>
10
11
  #include <cstring>
11
12
  #include <memory>
12
13
 
@@ -206,6 +207,206 @@ const float kLloydMaxBoundaries8[] = {
206
207
  2.8413202220f, 2.9594262622f, 3.0937618168f, 3.2505157199f,
207
208
  3.4405881450f, 3.6864032383f, 4.0502898282f
208
209
  };
210
+ const float kLloydMaxCentroids5[] = {
211
+ -3.260726295605043f, -2.691115579554310f, -2.317736402126149f,
212
+ -2.028725991363304f, -1.787231211885846f, -1.576226389073775f,
213
+ -1.386338935362625f, -1.211803212032400f, -1.048782381365585f,
214
+ -0.894564395854440f, -0.747135131789057f, -0.604933168939543f,
215
+ -0.466699175119721f, -0.331378051429876f, -0.198051689203879f,
216
+ -0.065889622349093f, 0.065889622349093f, 0.198051689203879f,
217
+ 0.331378051429876f, 0.466699175119721f, 0.604933168939543f,
218
+ 0.747135131789057f, 0.894564395854440f, 1.048782381365585f,
219
+ 1.211803212032400f, 1.386338935362625f, 1.576226389073775f,
220
+ 1.787231211885846f, 2.028725991363304f, 2.317736402126149f,
221
+ 2.691115579554310f, 3.260726295605043f
222
+ };
223
+ const float kLloydMaxBoundaries5[] = {
224
+ -2.975920937579676f, -2.504425990840229f,
225
+ -2.173231196744727f, -1.907978601624575f,
226
+ -1.681728800479811f, -1.481282662218200f,
227
+ -1.299071073697513f, -1.130292796698992f,
228
+ -0.971673388610013f, -0.820849763821748f,
229
+ -0.676034150364300f, -0.535816172029632f,
230
+ -0.399038613274799f, -0.264714870316877f,
231
+ -0.131970655776486f, 0.000000000000000f,
232
+ 0.131970655776486f, 0.264714870316877f,
233
+ 0.399038613274799f, 0.535816172029632f,
234
+ 0.676034150364300f, 0.820849763821748f,
235
+ 0.971673388610013f, 1.130292796698992f,
236
+ 1.299071073697513f, 1.481282662218200f,
237
+ 1.681728800479811f, 1.907978601624575f,
238
+ 2.173231196744727f, 2.504425990840229f,
239
+ 2.975920937579676f
240
+ };
241
+ const float kLloydMaxCentroids6[] = {
242
+ -3.744069023696475f, -3.240416640324168f, -2.917391465309850f,
243
+ -2.672261701410258f, -2.471294740528467f, -2.298972741297400f,
244
+ -2.146803022259123f, -2.009604894545278f, -1.883971899229356f,
245
+ -1.767537148129260f, -1.658584711429843f, -1.555827465952835f,
246
+ -1.458272959944728f, -1.365137897182360f, -1.275791622351997f,
247
+ -1.189717571132746f, -1.106485959691858f, -1.025734313393752f,
248
+ -0.947153093015629f, -0.870474868055092f, -0.795466052902551f,
249
+ -0.721920472069418f, -0.649654245231535f, -0.578501846064579f,
250
+ -0.508312758753803f, -0.438948800917774f, -0.370281933281155f,
251
+ -0.302192289440361f, -0.234566569768735f, -0.167296609901720f,
252
+ -0.100278121713920f, -0.033409455880258f, 0.033409455880258f,
253
+ 0.100278121713920f, 0.167296609901720f, 0.234566569768735f,
254
+ 0.302192289440361f, 0.370281933281155f, 0.438948800917774f,
255
+ 0.508312758753803f, 0.578501846064579f, 0.649654245231535f,
256
+ 0.721920472069418f, 0.795466052902551f, 0.870474868055092f,
257
+ 0.947153093015629f, 1.025734313393752f, 1.106485959691858f,
258
+ 1.189717571132746f, 1.275791622351997f, 1.365137897182360f,
259
+ 1.458272959944728f, 1.555827465952835f, 1.658584711429843f,
260
+ 1.767537148129260f, 1.883971899229356f, 2.009604894545278f,
261
+ 2.146803022259123f, 2.298972741297400f, 2.471294740528467f,
262
+ 2.672261701410258f, 2.917391465309850f, 3.240416640324168f,
263
+ 3.744069023696475f
264
+ };
265
+ const float kLloydMaxBoundaries6[] = {
266
+ -3.492242832010322f, -3.078904052817009f,
267
+ -2.794826583360054f, -2.571778220969362f,
268
+ -2.385133740912933f, -2.222887881778261f,
269
+ -2.078203958402201f, -1.946788396887317f,
270
+ -1.825754523679308f, -1.713060929779552f,
271
+ -1.607206088691339f, -1.507050212948781f,
272
+ -1.411705428563544f, -1.320464759767178f,
273
+ -1.232754596742371f, -1.148101765412302f,
274
+ -1.066110136542805f, -0.986443703204690f,
275
+ -0.908813980535361f, -0.832970460478822f,
276
+ -0.758693262485985f, -0.685787358650477f,
277
+ -0.614078045648057f, -0.543407302409191f,
278
+ -0.473630779835788f, -0.404615367099464f,
279
+ -0.336237111360758f, -0.268379429604548f,
280
+ -0.200931589835227f, -0.133787365807820f,
281
+ -0.066843788797089f, 0.000000000000000f,
282
+ 0.066843788797089f, 0.133787365807820f,
283
+ 0.200931589835227f, 0.268379429604548f,
284
+ 0.336237111360758f, 0.404615367099464f,
285
+ 0.473630779835788f, 0.543407302409191f,
286
+ 0.614078045648057f, 0.685787358650477f,
287
+ 0.758693262485985f, 0.832970460478822f,
288
+ 0.908813980535361f, 0.986443703204690f,
289
+ 1.066110136542805f, 1.148101765412302f,
290
+ 1.232754596742371f, 1.320464759767178f,
291
+ 1.411705428563544f, 1.507050212948781f,
292
+ 1.607206088691339f, 1.713060929779552f,
293
+ 1.825754523679308f, 1.946788396887317f,
294
+ 2.078203958402201f, 2.222887881778261f,
295
+ 2.385133740912933f, 2.571778220969362f,
296
+ 2.794826583360054f, 3.078904052817009f,
297
+ 3.492242832010322f
298
+ };
299
+ const float kLloydMaxCentroids7[] = {
300
+ -4.189521933023523f, -3.734857105369156f, -3.447381010593710f,
301
+ -3.231896182843021f, -3.057216102842374f, -2.909021527386642f,
302
+ -2.779491911054078f, -2.663867022558051f, -2.559023499137449f,
303
+ -2.462795908452321f, -2.373620316421171f, -2.290326870492530f,
304
+ -2.212016460501213f, -2.137983242734970f, -2.067662153838450f,
305
+ -2.000593243383757f, -1.936396720412283f, -1.874755326807296f,
306
+ -1.815400993379865f, -1.758105160675647f, -1.702671162415673f,
307
+ -1.648928306223807f, -1.596727786313424f, -1.545938700893484f,
308
+ -1.496445137501057f, -1.448144252238147f, -1.400944106526214f,
309
+ -1.354762105115604f, -1.309523700469555f, -1.265161378699170f,
310
+ -1.221613876387012f, -1.178825265520504f, -1.136744262343403f,
311
+ -1.095323771921318f, -1.054520418037026f, -1.014294067830043f,
312
+ -0.974607484216044f, -0.935426075762634f, -0.896717615963322f,
313
+ -0.858451939611374f, -0.820600783245502f, -0.783137537563140f,
314
+ -0.746037126679468f, -0.709275946093977f, -0.672831567493634f,
315
+ -0.636682661398141f, -0.600808970989236f, -0.565191195643323f,
316
+ -0.529810843625619f, -0.494650181610316f, -0.459692230045422f,
317
+ -0.424920552397901f, -0.390319263305967f, -0.355873075050329f,
318
+ -0.321567103923158f, -0.287386806226095f, -0.253318071908346f,
319
+ -0.219347083403316f, -0.185460257086808f, -0.151644263013162f,
320
+ -0.117885968250325f, -0.084172419896719f, -0.050490753968962f,
321
+ -0.016828143177728f, 0.016828143177728f, 0.050490753968962f,
322
+ 0.084172419896719f, 0.117885968250325f, 0.151644263013162f,
323
+ 0.185460257086808f, 0.219347083403316f, 0.253318071908346f,
324
+ 0.287386806226095f, 0.321567103923158f, 0.355873075050329f,
325
+ 0.390319263305967f, 0.424920552397901f, 0.459692230045422f,
326
+ 0.494650181610316f, 0.529810843625619f, 0.565191195643323f,
327
+ 0.600808970989236f, 0.636682661398141f, 0.672831567493634f,
328
+ 0.709275946093977f, 0.746037126679468f, 0.783137537563140f,
329
+ 0.820600783245502f, 0.858451939611374f, 0.896717615963322f,
330
+ 0.935426075762634f, 0.974607484216044f, 1.014294067830043f,
331
+ 1.054520418037026f, 1.095323771921318f, 1.136744262343403f,
332
+ 1.178825265520504f, 1.221613876387012f, 1.265161378699170f,
333
+ 1.309523700469555f, 1.354762105115604f, 1.400944106526214f,
334
+ 1.448144252238147f, 1.496445137501057f, 1.545938700893484f,
335
+ 1.596727786313424f, 1.648928306223807f, 1.702671162415673f,
336
+ 1.758105160675647f, 1.815400993379865f, 1.874755326807296f,
337
+ 1.936396720412283f, 2.000593243383757f, 2.067662153838450f,
338
+ 2.137983242734970f, 2.212016460501213f, 2.290326870492530f,
339
+ 2.373620316421171f, 2.462795908452321f, 2.559023499137449f,
340
+ 2.663867022558051f, 2.779491911054078f, 2.909021527386642f,
341
+ 3.057216102842374f, 3.231896182843021f, 3.447381010593710f,
342
+ 3.734857105369156f, 4.189521933023523f
343
+ };
344
+ const float kLloydMaxBoundaries7[] = {
345
+ -3.962189519196340f, -3.591119057981433f,
346
+ -3.339638596718365f, -3.144556142842697f,
347
+ -2.983118815114508f, -2.844256719220360f,
348
+ -2.721679466806065f, -2.611445260847750f,
349
+ -2.510909703794885f, -2.418208112436746f,
350
+ -2.331973593456850f, -2.251171665496871f,
351
+ -2.174999851618091f, -2.102822698286710f,
352
+ -2.034127698611103f, -1.968494981898020f,
353
+ -1.905576023609790f, -1.845078160093581f,
354
+ -1.786753077027756f, -1.730388161545660f,
355
+ -1.675799734319740f, -1.622828046268615f,
356
+ -1.571333243603454f, -1.521191919197270f,
357
+ -1.472294694869602f, -1.424544179382180f,
358
+ -1.377853105820909f, -1.332142902792580f,
359
+ -1.287342539584363f, -1.243387627543091f,
360
+ -1.200219570953758f, -1.157784763931954f,
361
+ -1.116034017132360f, -1.074922094979172f,
362
+ -1.034407242933534f, -0.994450776023043f,
363
+ -0.955016779989339f, -0.916071845862978f,
364
+ -0.877584777787348f, -0.839526361428438f,
365
+ -0.801869160404321f, -0.764587332121304f,
366
+ -0.727656536386722f, -0.691053756793806f,
367
+ -0.654757114445887f, -0.618745816193689f,
368
+ -0.583000083316280f, -0.547501019634471f,
369
+ -0.512230512617967f, -0.477171205827869f,
370
+ -0.442306391221661f, -0.407619907851934f,
371
+ -0.373096169178148f, -0.338720089486743f,
372
+ -0.304476955074627f, -0.270352439067221f,
373
+ -0.236332577655831f, -0.202403670245062f,
374
+ -0.168552260049985f, -0.134765115631744f,
375
+ -0.101029194073522f, -0.067331586932840f,
376
+ -0.033659448573345f, 0.000000000000000f,
377
+ 0.033659448573345f, 0.067331586932840f,
378
+ 0.101029194073522f, 0.134765115631744f,
379
+ 0.168552260049985f, 0.202403670245062f,
380
+ 0.236332577655831f, 0.270352439067221f,
381
+ 0.304476955074627f, 0.338720089486743f,
382
+ 0.373096169178148f, 0.407619907851934f,
383
+ 0.442306391221661f, 0.477171205827869f,
384
+ 0.512230512617967f, 0.547501019634471f,
385
+ 0.583000083316280f, 0.618745816193689f,
386
+ 0.654757114445887f, 0.691053756793806f,
387
+ 0.727656536386722f, 0.764587332121304f,
388
+ 0.801869160404321f, 0.839526361428438f,
389
+ 0.877584777787348f, 0.916071845862978f,
390
+ 0.955016779989339f, 0.994450776023043f,
391
+ 1.034407242933534f, 1.074922094979172f,
392
+ 1.116034017132360f, 1.157784763931954f,
393
+ 1.200219570953758f, 1.243387627543091f,
394
+ 1.287342539584363f, 1.332142902792580f,
395
+ 1.377853105820909f, 1.424544179382180f,
396
+ 1.472294694869602f, 1.521191919197270f,
397
+ 1.571333243603454f, 1.622828046268615f,
398
+ 1.675799734319740f, 1.730388161545660f,
399
+ 1.786753077027756f, 1.845078160093581f,
400
+ 1.905576023609790f, 1.968494981898020f,
401
+ 2.034127698611103f, 2.102822698286710f,
402
+ 2.174999851618091f, 2.251171665496871f,
403
+ 2.331973593456850f, 2.418208112436746f,
404
+ 2.510909703794885f, 2.611445260847750f,
405
+ 2.721679466806065f, 2.844256719220360f,
406
+ 2.983118815114508f, 3.144556142842697f,
407
+ 3.339638596718365f, 3.591119057981433f,
408
+ 3.962189519196340f
409
+ };
209
410
  // clang-format on
210
411
 
211
412
  struct LloydMaxTable {
@@ -219,20 +420,35 @@ const LloydMaxTable kLloydMaxTables[] = {
219
420
  {kLloydMaxCentroids2, kLloydMaxBoundaries2}, // 2
220
421
  {kLloydMaxCentroids3, kLloydMaxBoundaries3}, // 3
221
422
  {kLloydMaxCentroids4, kLloydMaxBoundaries4}, // 4
222
- {nullptr, nullptr}, // 5 (unused)
223
- {nullptr, nullptr}, // 6 (unused)
224
- {nullptr, nullptr}, // 7 (unused)
423
+ {kLloydMaxCentroids5, kLloydMaxBoundaries5}, // 5
424
+ {kLloydMaxCentroids6, kLloydMaxBoundaries6}, // 6
425
+ {kLloydMaxCentroids7, kLloydMaxBoundaries7}, // 7
225
426
  {kLloydMaxCentroids8, kLloydMaxBoundaries8}, // 8
226
427
  };
227
428
 
228
- void populate_lloyd_max_trained(size_t mse_bits, std::vector<float>& trained) {
429
+ // The tables are Lloyd-Max optimal for N(0, 1) input. Callers whose input has
430
+ // a different standard deviation pass it as `scale` to stretch the table.
431
+ void populate_lloyd_max_trained(
432
+ size_t mse_bits,
433
+ std::vector<float>& trained,
434
+ float scale = 1.0f) {
229
435
  FAISS_THROW_IF_NOT(mse_bits >= 1 && mse_bits <= 8);
230
- FAISS_THROW_IF_NOT(kLloydMaxTables[mse_bits].centroids != nullptr);
436
+ FAISS_THROW_IF_NOT(kLloydMaxTables[mse_bits].centroids);
231
437
  size_t k = size_t(1) << mse_bits;
232
438
  const auto& t = kLloydMaxTables[mse_bits];
233
439
  trained.resize(k + (k - 1));
234
- std::copy(t.centroids, t.centroids + k, trained.begin());
235
- std::copy(t.boundaries, t.boundaries + k - 1, trained.begin() + k);
440
+ for (size_t i = 0; i < k; i++) {
441
+ trained[i] = t.centroids[i] * scale;
442
+ }
443
+ for (size_t i = 0; i + 1 < k; i++) {
444
+ trained[k + i] = t.boundaries[i] * scale;
445
+ }
446
+ }
447
+
448
+ // Component scale of a unit-norm vector in R^d.
449
+ float unit_norm_component_scale(size_t d) {
450
+ FAISS_THROW_IF_NOT(d > 0);
451
+ return 1.0f / std::sqrt(static_cast<float>(d));
236
452
  }
237
453
 
238
454
  } // namespace
@@ -251,14 +467,17 @@ ScalarQuantizer::ScalarQuantizer() {}
251
467
  void ScalarQuantizer::set_derived_sizes() {
252
468
  switch (qtype) {
253
469
  case QT_1bit_tqmse:
470
+ case QT_1bit_eden:
254
471
  code_size = (d + 7) / 8;
255
472
  bits = 1;
256
473
  break;
257
474
  case QT_2bit_tqmse:
475
+ case QT_2bit_eden:
258
476
  code_size = (d * 2 + 7) / 8;
259
477
  bits = 2;
260
478
  break;
261
479
  case QT_3bit_tqmse:
480
+ case QT_3bit_eden:
262
481
  code_size = (d * 3 + 7) / 8;
263
482
  bits = 3;
264
483
  break;
@@ -267,19 +486,30 @@ void ScalarQuantizer::set_derived_sizes() {
267
486
  case QT_8bit_direct:
268
487
  case QT_8bit_direct_signed:
269
488
  case QT_8bit_tqmse:
489
+ case QT_8bit_eden:
270
490
  code_size = d;
271
491
  bits = 8;
272
492
  break;
273
493
  case QT_4bit:
274
494
  case QT_4bit_uniform:
275
495
  case QT_4bit_tqmse:
496
+ case QT_4bit_eden:
276
497
  code_size = (d + 1) / 2;
277
498
  bits = 4;
278
499
  break;
500
+ case QT_5bit_eden:
501
+ code_size = (d * 5 + 7) / 8;
502
+ bits = 5;
503
+ break;
279
504
  case QT_6bit:
505
+ case QT_6bit_eden:
280
506
  code_size = (d * 6 + 7) / 8;
281
507
  bits = 6;
282
508
  break;
509
+ case QT_7bit_eden:
510
+ code_size = (d * 7 + 7) / 8;
511
+ bits = 7;
512
+ break;
283
513
  case QT_fp16:
284
514
  code_size = d * 2;
285
515
  bits = 16;
@@ -334,7 +564,7 @@ void ScalarQuantizer::train(size_t n, const float* x) {
334
564
  case QT_4bit_uniform:
335
565
  case QT_8bit_uniform:
336
566
  FAISS_THROW_IF_NOT(n > 0);
337
- FAISS_THROW_IF_NOT(x != nullptr);
567
+ FAISS_THROW_IF_NOT(x);
338
568
  train_Uniform(
339
569
  rangestat,
340
570
  rangestat_arg,
@@ -347,7 +577,7 @@ void ScalarQuantizer::train(size_t n, const float* x) {
347
577
  case QT_8bit:
348
578
  case QT_6bit:
349
579
  FAISS_THROW_IF_NOT(n > 0);
350
- FAISS_THROW_IF_NOT(x != nullptr);
580
+ FAISS_THROW_IF_NOT(x);
351
581
  train_NonUniform(
352
582
  rangestat,
353
583
  rangestat_arg,
@@ -363,20 +593,35 @@ void ScalarQuantizer::train(size_t n, const float* x) {
363
593
  case QT_8bit_direct_signed:
364
594
  // no training necessary
365
595
  break;
596
+ case QT_1bit_eden:
597
+ case QT_2bit_eden:
598
+ case QT_3bit_eden:
599
+ case QT_4bit_eden:
600
+ case QT_5bit_eden:
601
+ case QT_6bit_eden:
602
+ case QT_7bit_eden:
603
+ case QT_8bit_eden:
604
+ populate_lloyd_max_trained(bits, trained);
605
+ break;
366
606
  case QT_1bit_tqmse:
367
- populate_lloyd_max_trained(1, trained);
607
+ populate_lloyd_max_trained(
608
+ 1, trained, unit_norm_component_scale(d));
368
609
  break;
369
610
  case QT_2bit_tqmse:
370
- populate_lloyd_max_trained(2, trained);
611
+ populate_lloyd_max_trained(
612
+ 2, trained, unit_norm_component_scale(d));
371
613
  break;
372
614
  case QT_3bit_tqmse:
373
- populate_lloyd_max_trained(3, trained);
615
+ populate_lloyd_max_trained(
616
+ 3, trained, unit_norm_component_scale(d));
374
617
  break;
375
618
  case QT_4bit_tqmse:
376
- populate_lloyd_max_trained(4, trained);
619
+ populate_lloyd_max_trained(
620
+ 4, trained, unit_norm_component_scale(d));
377
621
  break;
378
622
  case QT_8bit_tqmse:
379
- populate_lloyd_max_trained(8, trained);
623
+ populate_lloyd_max_trained(
624
+ 8, trained, unit_norm_component_scale(d));
380
625
  break;
381
626
  case QT_2bit_tq:
382
627
  case QT_3bit_tq:
@@ -390,7 +635,6 @@ void ScalarQuantizer::train(size_t n, const float* x) {
390
635
  trained.push_back(seed_f[0]);
391
636
  trained.push_back(seed_f[1]);
392
637
  trained.push_back(static_cast<float>(turboq_refine.qjl_type));
393
- turboq_refine.init_projection(d);
394
638
  break;
395
639
  }
396
640
  default:
@@ -398,36 +642,15 @@ void ScalarQuantizer::train(size_t n, const float* x) {
398
642
  }
399
643
  }
400
644
 
401
- void ScalarQuantizer::TurboQuantRefine::init_projection(size_t d) {
402
- if (use_fwht()) {
403
- padded_d = 1;
404
- while (padded_d < d) {
405
- padded_d <<= 1;
406
- }
407
- fwht_signs.resize(padded_d);
408
- RandomGenerator rng(seed);
409
- for (size_t i = 0; i < padded_d; i++) {
410
- fwht_signs[i] = (rng.rand_int(2) == 0) ? 1.0f : -1.0f;
411
- }
412
- } else {
413
- rr_matrix.resize(d * d);
414
- float_randn(rr_matrix.data(), d * d, seed);
415
- matrix_qr(static_cast<int>(d), static_cast<int>(d), rr_matrix.data());
416
- }
417
- }
418
-
419
645
  ScalarQuantizer::SQuantizer* ScalarQuantizer::select_quantizer() const {
420
- return with_simd_level_spr([&]<SIMDLevel SL>() -> SQuantizer* {
421
- if constexpr (SL != SIMDLevel::NONE) {
422
- auto* q = scalar_quantizer::sq_select_quantizer<SL>(
423
- qtype, d, trained);
424
- if (q) {
425
- return q;
426
- }
427
- }
428
- return scalar_quantizer::sq_select_quantizer<SIMDLevel::NONE>(
429
- qtype, d, trained);
430
- });
646
+ // A SIMD level's factory returns nullptr when the dimension is
647
+ // incompatible (e.g. AVX-512 needs d % 16 == 0); the dispatcher then falls
648
+ // back to the next-lower level (AVX-512 -> AVX2 -> scalar).
649
+ return with_simd_level_fallback<AVAILABLE_SIMD_LEVELS_BASE_WITH_SPR>(
650
+ [&]<SIMDLevel SL>() -> SQuantizer* {
651
+ return scalar_quantizer::sq_select_quantizer<SL>(
652
+ qtype, d, trained);
653
+ });
431
654
  }
432
655
 
433
656
  void ScalarQuantizer::compute_codes(const float* x, uint8_t* codes, size_t n)
@@ -438,7 +661,7 @@ void ScalarQuantizer::compute_codes(const float* x, uint8_t* codes, size_t n)
438
661
  std::unique_ptr<SQuantizer> squant(select_quantizer());
439
662
 
440
663
  memset(codes, 0, code_size * n);
441
- #pragma omp parallel for
664
+ #pragma omp parallel for if (n > 100)
442
665
  for (int64_t i = 0; i < static_cast<int64_t>(n); i++) {
443
666
  squant->encode_vector(x + i * d, codes + i * code_size);
444
667
  }
@@ -451,7 +674,7 @@ void ScalarQuantizer::decode(const uint8_t* codes, float* x, size_t n) const {
451
674
  }
452
675
  std::unique_ptr<SQuantizer> squant(select_quantizer());
453
676
 
454
- #pragma omp parallel for
677
+ #pragma omp parallel for if (n > 100)
455
678
  for (int64_t i = 0; i < static_cast<int64_t>(n); i++) {
456
679
  squant->decode_vector(codes + i * code_size, x + i * d);
457
680
  }
@@ -460,17 +683,11 @@ void ScalarQuantizer::decode(const uint8_t* codes, float* x, size_t n) const {
460
683
  ScalarQuantizer::SQDistanceComputer* ScalarQuantizer::get_distance_computer(
461
684
  MetricType metric) const {
462
685
  FAISS_THROW_IF_NOT(metric == METRIC_L2 || metric == METRIC_INNER_PRODUCT);
463
- return with_simd_level_spr([&]<SIMDLevel SL>() -> SQDistanceComputer* {
464
- if constexpr (SL != SIMDLevel::NONE) {
465
- auto* dc = scalar_quantizer::sq_select_distance_computer<SL>(
466
- metric, qtype, d, trained);
467
- if (dc) {
468
- return dc;
469
- }
470
- }
471
- return scalar_quantizer::sq_select_distance_computer<SIMDLevel::NONE>(
472
- metric, qtype, d, trained);
473
- });
686
+ return with_simd_level_fallback<AVAILABLE_SIMD_LEVELS_BASE_WITH_SPR>(
687
+ [&]<SIMDLevel SL>() -> SQDistanceComputer* {
688
+ return scalar_quantizer::sq_select_distance_computer<SL>(
689
+ metric, qtype, d, trained);
690
+ });
474
691
  }
475
692
 
476
693
  InvertedListScanner* ScalarQuantizer::select_InvertedListScanner(
@@ -479,33 +696,19 @@ InvertedListScanner* ScalarQuantizer::select_InvertedListScanner(
479
696
  bool store_pairs,
480
697
  const IDSelector* sel,
481
698
  bool by_residual) const {
482
- return with_simd_level_spr([&]<SIMDLevel SL>() -> InvertedListScanner* {
483
- if constexpr (SL != SIMDLevel::NONE) {
484
- auto* s = scalar_quantizer::sq_select_InvertedListScanner<SL>(
485
- qtype,
486
- mt,
487
- d,
488
- code_size,
489
- trained,
490
- quantizer,
491
- store_pairs,
492
- sel,
493
- by_residual);
494
- if (s) {
495
- return s;
496
- }
497
- }
498
- return scalar_quantizer::sq_select_InvertedListScanner<SIMDLevel::NONE>(
499
- qtype,
500
- mt,
501
- d,
502
- code_size,
503
- trained,
504
- quantizer,
505
- store_pairs,
506
- sel,
507
- by_residual);
508
- });
699
+ return with_simd_level_fallback<AVAILABLE_SIMD_LEVELS_BASE_WITH_SPR>(
700
+ [&]<SIMDLevel SL>() -> InvertedListScanner* {
701
+ return scalar_quantizer::sq_select_InvertedListScanner<SL>(
702
+ qtype,
703
+ mt,
704
+ d,
705
+ code_size,
706
+ trained,
707
+ quantizer,
708
+ store_pairs,
709
+ sel,
710
+ by_residual);
711
+ });
509
712
  }
510
713
 
511
714
  } // namespace faiss
@@ -45,6 +45,14 @@ struct ScalarQuantizer : Quantizer {
45
45
  QT_3bit_tq, ///< Full TurboQuant (2-bit MSE + 1-bit QJL + factors)
46
46
  QT_4bit_tq, ///< Full TurboQuant (3-bit MSE + 1-bit QJL + factors)
47
47
  QT_5bit_tq, ///< Full TurboQuant (4-bit MSE + 1-bit QJL + factors)
48
+ QT_1bit_eden, ///< EDEN Lloyd-Max scalar code, 1 bit per component
49
+ QT_2bit_eden, ///< EDEN Lloyd-Max scalar code, 2 bits per component
50
+ QT_3bit_eden, ///< EDEN Lloyd-Max scalar code, 3 bits per component
51
+ QT_4bit_eden, ///< EDEN Lloyd-Max scalar code, 4 bits per component
52
+ QT_5bit_eden, ///< EDEN Lloyd-Max scalar code, 5 bits per component
53
+ QT_6bit_eden, ///< EDEN Lloyd-Max scalar code, 6 bits per component
54
+ QT_7bit_eden, ///< EDEN Lloyd-Max scalar code, 7 bits per component
55
+ QT_8bit_eden, ///< EDEN Lloyd-Max scalar code, 8 bits per component
48
56
  QT_count
49
57
  };
50
58
 
@@ -135,6 +143,18 @@ struct ScalarQuantizer : Quantizer {
135
143
  float distance_to_code(const uint8_t* code) final {
136
144
  return query_to_code(code);
137
145
  }
146
+
147
+ void distance_to_code_batch_4(
148
+ const uint8_t* c1,
149
+ const uint8_t* c2,
150
+ const uint8_t* c3,
151
+ const uint8_t* c4,
152
+ float& d1,
153
+ float& d2,
154
+ float& d3,
155
+ float& d4) override {
156
+ query_to_codes_batch_4(c1, c2, c3, c4, d1, d2, d3, d4);
157
+ }
138
158
  };
139
159
 
140
160
  /// TurboQuant full (QT_*_tq) refinement state, isolated from the
@@ -157,18 +177,9 @@ struct ScalarQuantizer : Quantizer {
157
177
  return s;
158
178
  }
159
179
 
180
+ /// The selected projection is built from `trained` by the quantizer.
160
181
  uint8_t qjl_type = 0;
161
182
  uint64_t seed = 42;
162
- size_t padded_d = 0;
163
- std::vector<float> fwht_signs;
164
- std::vector<float> rr_matrix;
165
- size_t nb_bits_lo = 0;
166
- size_t n_hi_dims = 0;
167
-
168
- void init_projection(size_t d);
169
- bool use_fwht() const {
170
- return qjl_type == 0;
171
- }
172
183
 
173
184
  struct DistanceComputer : SQDistanceComputer {
174
185
  virtual void configure(uint8_t qb, bool int_qjl) = 0;
@@ -179,6 +190,11 @@ struct ScalarQuantizer : Quantizer {
179
190
  };
180
191
  };
181
192
 
193
+ static_assert(
194
+ sizeof(TurboQuantRefine) <= 16,
195
+ "keep this a small config struct -- do not add projection buffers "
196
+ "here (T287092602)");
197
+
182
198
  TurboQuantRefine turboq_refine;
183
199
 
184
200
  SQDistanceComputer* get_distance_computer(
@@ -70,8 +70,8 @@ void ThreadedIndex<IndexT>::addIndex(IndexT* index) {
70
70
 
71
71
  // Make sure this index is not duplicated
72
72
  for (auto& p : indices_) {
73
- FAISS_THROW_IF_NOT_MSG(
74
- p.first != index,
73
+ FAISS_THROW_IF_MSG(
74
+ p.first == index,
75
75
  "addIndex: attempting to add index "
76
76
  "that is already in the collection");
77
77
  }
@@ -15,8 +15,12 @@ namespace faiss {
15
15
  // advance() is O(1) except every 250 calls, which are O(size).
16
16
  // The hash set strategy is a constant factor slower for get()/set(),
17
17
  // but O(1) to construct and O(visits) to advance.
18
- // A size of ~1M seems to be the threshold where the hash set wins.
19
- size_t visited_table_hashset_threshold = 500000;
18
+ // 10M is only a current estimated threshold, not a proven crossover: we are not
19
+ // sure the array still wins at 10M. The point where the array stops paying off
20
+ // varies by dataset (it shifts with dimension, working-set / cache pressure,
21
+ // etc.), so this is a coarse default that should eventually be replaced by
22
+ // smarter per-index tuning.
23
+ size_t visited_table_hashset_threshold = 10000000;
20
24
 
21
25
  std::unique_ptr<VisitedTable> VisitedTable::create(
22
26
  size_t size,
@@ -29,6 +33,22 @@ std::unique_ptr<VisitedTable> VisitedTable::create(
29
33
  return std::make_unique<VisitedTableVector>(size);
30
34
  }
31
35
 
36
+ VisitedTable& VisitedTable::get_reusable(
37
+ size_t size,
38
+ std::optional<bool> use_hashset) {
39
+ bool use_set =
40
+ use_hashset.value_or(size >= visited_table_hashset_threshold);
41
+ if (use_set) {
42
+ thread_local VisitedTableSet tls_set;
43
+ tls_set.advance();
44
+ return tls_set;
45
+ }
46
+ thread_local VisitedTableVector tls_vec(0);
47
+ tls_vec.ensure_size(size);
48
+ tls_vec.advance();
49
+ return tls_vec;
50
+ }
51
+
32
52
  void VisitedTableVector::advance() {
33
53
  if (visno < 254) {
34
54
  // 254 rather than 255 because sometimes we use visno and visno+1
@@ -48,6 +48,18 @@ struct VisitedTable {
48
48
  static std::unique_ptr<VisitedTable> create(
49
49
  size_t size,
50
50
  std::optional<bool> use_hashset = std::nullopt);
51
+
52
+ /// Returns a thread-local, reusable table sized for at least `size` and
53
+ /// reset to a clean state. Unlike create(), it does not allocate on each
54
+ /// call: the O(size) versioned array is allocated once per thread and
55
+ /// reused across searches, avoiding a per-search alloc+zero of the whole
56
+ /// array when a static index is searched repeatedly.
57
+ ///
58
+ /// The returned reference is owned by thread-local storage: do not delete
59
+ /// it and do not use it beyond the current search on the calling thread.
60
+ static VisitedTable& get_reusable(
61
+ size_t size,
62
+ std::optional<bool> use_hashset = std::nullopt);
51
63
  };
52
64
 
53
65
  /// Set-based implementation using unordered_set.
@@ -87,6 +99,14 @@ struct VisitedTableVector FAISS_FINAL : VisitedTable {
87
99
 
88
100
  explicit VisitedTableVector(size_t size) : visited(size, 0) {}
89
101
 
102
+ /// Grow so indices in [0, size) are valid; new slots read as unvisited.
103
+ /// Never shrinks, so capacity is retained when the table is reused.
104
+ void ensure_size(size_t size) {
105
+ if (visited.size() < size) {
106
+ visited.resize(size, 0);
107
+ }
108
+ }
109
+
90
110
  bool set(size_t no) final {
91
111
  if (visited[no] == visno) {
92
112
  return false;