faiss 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/ext/faiss/extconf.rb +1 -1
  4. data/lib/faiss/version.rb +1 -1
  5. data/vendor/faiss/faiss/AutoTune.cpp +175 -0
  6. data/vendor/faiss/faiss/Clustering.cpp +150 -60
  7. data/vendor/faiss/faiss/Clustering.h +29 -0
  8. data/vendor/faiss/faiss/IVFlib.cpp +99 -0
  9. data/vendor/faiss/faiss/IVFlib.h +15 -0
  10. data/vendor/faiss/faiss/Index.cpp +44 -0
  11. data/vendor/faiss/faiss/Index.h +19 -14
  12. data/vendor/faiss/faiss/IndexBinary.cpp +8 -0
  13. data/vendor/faiss/faiss/IndexBinary.h +8 -0
  14. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +82 -4
  15. data/vendor/faiss/faiss/IndexBinaryHNSW.h +19 -0
  16. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +34 -0
  17. data/vendor/faiss/faiss/IndexFastScan.cpp +33 -17
  18. data/vendor/faiss/faiss/IndexFastScan.h +6 -3
  19. data/vendor/faiss/faiss/IndexFlat.cpp +56 -0
  20. data/vendor/faiss/faiss/IndexFlat.h +31 -0
  21. data/vendor/faiss/faiss/IndexFlatCodes.cpp +6 -1
  22. data/vendor/faiss/faiss/IndexHNSW.cpp +456 -10
  23. data/vendor/faiss/faiss/IndexHNSW.h +38 -0
  24. data/vendor/faiss/faiss/IndexIDMap.cpp +32 -0
  25. data/vendor/faiss/faiss/IndexIDMap.h +7 -0
  26. data/vendor/faiss/faiss/IndexIVF.cpp +281 -31
  27. data/vendor/faiss/faiss/IndexIVF.h +53 -1
  28. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +46 -14
  29. data/vendor/faiss/faiss/IndexIVFFastScan.h +4 -2
  30. data/vendor/faiss/faiss/IndexIVFFlat.cpp +33 -0
  31. data/vendor/faiss/faiss/IndexIVFFlat.h +16 -0
  32. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +1 -0
  33. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +3 -20
  34. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +50 -14
  35. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -2
  36. data/vendor/faiss/faiss/IndexIVFSQFastScan.cpp +855 -0
  37. data/vendor/faiss/faiss/IndexIVFSQFastScan.h +145 -0
  38. data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -3
  39. data/vendor/faiss/faiss/IndexPiPNN.cpp +201 -0
  40. data/vendor/faiss/faiss/IndexPiPNN.h +83 -0
  41. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +6 -3
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +10 -4
  43. data/vendor/faiss/faiss/IndexReplicas.cpp +20 -0
  44. data/vendor/faiss/faiss/IndexReplicas.h +9 -0
  45. data/vendor/faiss/faiss/IndexSQFastScan.cpp +648 -0
  46. data/vendor/faiss/faiss/IndexSQFastScan.h +129 -0
  47. data/vendor/faiss/faiss/IndexShards.cpp +10 -0
  48. data/vendor/faiss/faiss/IndexShards.h +5 -0
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +57 -15
  50. data/vendor/faiss/faiss/SuperKMeans.h +9 -4
  51. data/vendor/faiss/faiss/VectorTransform.cpp +3 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +24 -0
  53. data/vendor/faiss/faiss/gpu/Dataset.h +112 -0
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +16 -13
  55. data/vendor/faiss/faiss/gpu/GpuCloner.h +4 -0
  56. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndex.h +4 -13
  58. data/vendor/faiss/faiss/gpu/GpuIndexBinaryCagra.h +26 -13
  59. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +45 -22
  60. data/vendor/faiss/faiss/gpu/GpuIndexFlat.h +11 -0
  61. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
  62. data/vendor/faiss/faiss/gpu/test/TestGpuDatasetHeader.cpp +18 -0
  63. data/vendor/faiss/faiss/gpu/test/TestGpuIndexFlat.cpp +215 -0
  64. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +98 -0
  65. data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +11 -0
  66. data/vendor/faiss/faiss/gpu/utils/StackDeviceMemory.cpp +1 -1
  67. data/vendor/faiss/faiss/gpu_metal/MetalCloner.h +11 -0
  68. data/vendor/faiss/faiss/gpu_metal/MetalDistance.h +92 -0
  69. data/vendor/faiss/faiss/gpu_metal/MetalIndex.h +4 -0
  70. data/vendor/faiss/faiss/gpu_metal/MetalIndexBinaryFlat.h +69 -0
  71. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +38 -0
  72. data/vendor/faiss/faiss/gpu_metal/MetalKernels.h +106 -0
  73. data/vendor/faiss/faiss/gpu_metal/MetalPythonBridge.h +10 -0
  74. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +2 -0
  75. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +1 -6
  76. data/vendor/faiss/faiss/impl/ClusteringHelpers.cpp +134 -45
  77. data/vendor/faiss/faiss/impl/ClusteringHelpers.h +36 -0
  78. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +286 -136
  79. data/vendor/faiss/faiss/impl/DistanceComputer.cpp +63 -0
  80. data/vendor/faiss/faiss/impl/DistanceComputer.h +3 -29
  81. data/vendor/faiss/faiss/impl/HNSW.cpp +46 -10
  82. data/vendor/faiss/faiss/impl/HNSW.h +72 -0
  83. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +6 -19
  84. data/vendor/faiss/faiss/impl/NNDescent.cpp +2 -1
  85. data/vendor/faiss/faiss/impl/NSG.cpp +3 -2
  86. data/vendor/faiss/faiss/impl/Panorama.h +3 -2
  87. data/vendor/faiss/faiss/impl/PiPNN.cpp +822 -0
  88. data/vendor/faiss/faiss/impl/PiPNN.h +122 -0
  89. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +54 -11
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.h +1 -1
  91. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +81 -57
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +15 -0
  93. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +29 -0
  94. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -6
  95. data/vendor/faiss/faiss/impl/approx_topk/avx2.cpp +0 -4
  96. data/vendor/faiss/faiss/impl/approx_topk/neon.cpp +0 -4
  97. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryHash_impl.h +5 -2
  98. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +0 -4
  99. data/vendor/faiss/faiss/impl/binary_hamming/avx512.cpp +0 -4
  100. data/vendor/faiss/faiss/impl/binary_hamming/neon.cpp +0 -4
  101. data/vendor/faiss/faiss/impl/binary_hamming/rvv.cpp +0 -4
  102. data/vendor/faiss/faiss/impl/expanded_scanners.h +88 -0
  103. data/vendor/faiss/faiss/impl/fast_scan/FastScanDistancePostProcessing.h +4 -0
  104. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops.h +61 -64
  105. data/vendor/faiss/faiss/impl/fast_scan/accumulate_loops_512.h +13 -14
  106. data/vendor/faiss/faiss/impl/fast_scan/decompose_qbs.h +17 -129
  107. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +25 -19
  108. data/vendor/faiss/faiss/impl/fast_scan/fast_scan.cpp +5 -0
  109. data/vendor/faiss/faiss/impl/fast_scan/impl-avx2.cpp +0 -4
  110. data/vendor/faiss/faiss/impl/fast_scan/impl-avx512.cpp +0 -4
  111. data/vendor/faiss/faiss/impl/fast_scan/impl-neon.cpp +0 -88
  112. data/vendor/faiss/faiss/impl/fast_scan/impl-riscv.cpp +0 -4
  113. data/vendor/faiss/faiss/impl/fast_scan/kernels_neon.h +155 -0
  114. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd256.h +22 -1
  115. data/vendor/faiss/faiss/impl/fast_scan/kernels_simd512.h +3 -2
  116. data/vendor/faiss/faiss/impl/fast_scan/simd_result_handlers.h +41 -23
  117. data/vendor/faiss/faiss/impl/hnsw/avx2.cpp +0 -4
  118. data/vendor/faiss/faiss/impl/hnsw/avx512.cpp +0 -4
  119. data/vendor/faiss/faiss/impl/index_read.cpp +172 -0
  120. data/vendor/faiss/faiss/impl/index_write.cpp +95 -0
  121. data/vendor/faiss/faiss/impl/pipnn/HashPrune.cpp +159 -0
  122. data/vendor/faiss/faiss/impl/pipnn/HashPrune.h +69 -0
  123. data/vendor/faiss/faiss/impl/pipnn/Partition.cpp +587 -0
  124. data/vendor/faiss/faiss/impl/pipnn/Partition.h +93 -0
  125. data/vendor/faiss/faiss/impl/pipnn/kernels.cpp +326 -0
  126. data/vendor/faiss/faiss/impl/pipnn/kernels.h +92 -0
  127. data/vendor/faiss/faiss/impl/platform_macros.h +45 -0
  128. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +0 -4
  129. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQScanner_impl.h +79 -4
  130. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +6 -0
  131. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +0 -4
  132. data/vendor/faiss/faiss/impl/pq_code_distance/avx512.cpp +0 -4
  133. data/vendor/faiss/faiss/impl/pq_code_distance/neon.cpp +0 -4
  134. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx2.h +4 -0
  135. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-avx512.h +4 -1
  136. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +19 -0
  137. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.h +6 -6
  138. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +131 -6
  139. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-sve.cpp +0 -4
  140. data/vendor/faiss/faiss/impl/pq_code_distance/pq_rvv_kernel.h +89 -0
  141. data/vendor/faiss/faiss/impl/pq_code_distance/pq_scan_impl.h +21 -10
  142. data/vendor/faiss/faiss/impl/pq_code_distance/rvv.cpp +45 -18
  143. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +2 -1
  144. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +0 -4
  145. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +0 -4
  146. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +0 -4
  147. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +0 -4
  148. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +5 -1
  149. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +28 -52
  150. data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +59 -11
  151. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +226 -77
  152. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +482 -103
  153. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +2 -149
  154. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +3 -698
  155. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +66 -57
  156. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +101 -96
  157. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-rvv.cpp +3185 -144
  158. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_coefficients.h +48 -0
  159. data/vendor/faiss/faiss/impl/scalar_quantizer/sq8_rvv_kernel.h +546 -0
  160. data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +2 -2
  161. data/vendor/faiss/faiss/impl/simd_dispatch.h +7 -0
  162. data/vendor/faiss/faiss/impl/simdlib/simdlib_avx2.h +14 -0
  163. data/vendor/faiss/faiss/impl/simdlib/simdlib_dispatch.h +7 -8
  164. data/vendor/faiss/faiss/impl/sq_fastscan_utils.h +232 -0
  165. data/vendor/faiss/faiss/index_factory.cpp +76 -0
  166. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +3 -1
  167. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +3 -2
  168. data/vendor/faiss/faiss/utils/AlignedTable.h +6 -1
  169. data/vendor/faiss/faiss/utils/Heap.cpp +4 -3
  170. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +16 -5
  171. data/vendor/faiss/faiss/utils/bf16.h +36 -0
  172. data/vendor/faiss/faiss/utils/distances.cpp +226 -15
  173. data/vendor/faiss/faiss/utils/distances.h +34 -0
  174. data/vendor/faiss/faiss/utils/distances_dispatch.h +6 -4
  175. data/vendor/faiss/faiss/utils/distances_fused/avx512.cpp +2 -5
  176. data/vendor/faiss/faiss/utils/distances_fused/distances_fused.cpp +0 -14
  177. data/vendor/faiss/faiss/utils/distances_fused/rvv.cpp +25 -0
  178. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based.cpp +0 -4
  179. data/vendor/faiss/faiss/utils/distances_fused/simdlib_based_neon.cpp +0 -4
  180. data/vendor/faiss/faiss/utils/distances_fused/simdlib_kernel-inl.h +2 -1
  181. data/vendor/faiss/faiss/utils/distances_simd.cpp +9 -0
  182. data/vendor/faiss/faiss/utils/fp16-fp16c.h +4 -0
  183. data/vendor/faiss/faiss/utils/fp16-inl.h +26 -52
  184. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx2.cpp +0 -4
  185. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512.cpp +0 -4
  186. data/vendor/faiss/faiss/utils/hamming_distance/hamming_avx512_vpopcnt.cpp +0 -4
  187. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512.h +33 -5
  188. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-avx512_vpopcnt.h +15 -30
  189. data/vendor/faiss/faiss/utils/hamming_distance/hamming_computer-neon.h +0 -4
  190. data/vendor/faiss/faiss/utils/hamming_distance/hamming_impl.h +2 -1
  191. data/vendor/faiss/faiss/utils/hamming_distance/hamming_neon.cpp +0 -4
  192. data/vendor/faiss/faiss/utils/hamming_distance/hamming_rvv.cpp +0 -4
  193. data/vendor/faiss/faiss/utils/pq_code_distance.h +8 -4
  194. data/vendor/faiss/faiss/utils/prefetch.h +1 -26
  195. data/vendor/faiss/faiss/utils/quantize_lut.cpp +26 -27
  196. data/vendor/faiss/faiss/utils/quantize_lut.h +11 -0
  197. data/vendor/faiss/faiss/utils/rabitq_simd.h +51 -16
  198. data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +9 -4
  199. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +37 -5
  200. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +64 -5
  201. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +64 -5
  202. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +353 -71
  203. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv_kernel.h +68 -0
  204. data/vendor/faiss/faiss/utils/simd_impl/exhaustive_L2sqr_blas_cmax.h +93 -0
  205. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels.h +76 -0
  206. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx2.cpp +52 -0
  207. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_avx512.cpp +52 -0
  208. data/vendor/faiss/faiss/utils/simd_impl/fp16_kernels_neon.cpp +48 -0
  209. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx2.h +44 -0
  210. data/vendor/faiss/faiss/utils/simd_impl/masked_sum-avx512.h +43 -0
  211. data/vendor/faiss/faiss/utils/simd_impl/masked_sum.h +38 -0
  212. data/vendor/faiss/faiss/utils/simd_impl/partitioning_avx2.cpp +0 -4
  213. data/vendor/faiss/faiss/utils/simd_impl/partitioning_neon.cpp +0 -4
  214. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +54 -15
  215. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +38 -21
  216. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_vpopcnt.cpp +51 -14
  217. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +297 -10
  218. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +0 -4
  219. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx2.cpp +0 -4
  220. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_avx512.cpp +0 -4
  221. data/vendor/faiss/faiss/utils/simd_impl/super_kmeans_kernels_sve.cpp +0 -4
  222. data/vendor/faiss/faiss/utils/simd_levels.cpp +19 -3
  223. data/vendor/faiss/faiss/utils/simd_levels.h +74 -1
  224. data/vendor/faiss/faiss/utils/transpose/transpose-avx2-inl.h +0 -4
  225. data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +0 -4
  226. data/vendor/faiss/faiss/utils/utils.cpp +80 -0
  227. data/vendor/faiss/faiss/utils/utils.h +24 -0
  228. metadata +33 -1
@@ -0,0 +1,48 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #pragma once
9
+
10
+ /* QT_8bit reconstruction coefficients.
11
+ *
12
+ * Scalar and portable. A caller on any architecture may include this.
13
+ *
14
+ * For a QT_8bit non-uniform quantizer with trained values vmin and vdiff:
15
+ *
16
+ * a[i] = vdiff[i] / 255
17
+ * rmin[i] = vmin[i] + 0.5 * a[i]
18
+ * e[i] = query[i] - rmin[i]
19
+ *
20
+ * so reconstruct(code)[i] = rmin[i] + a[i] * code[i].
21
+ */
22
+
23
+ #include <cstddef>
24
+
25
+ namespace faiss {
26
+ namespace sq8 {
27
+
28
+ /// Derive the per-dimension scale and offset from a trained quantizer.
29
+ /// @param trained 2*d floats: vmin[0..d) then vdiff[0..d)
30
+ /// @param levels the largest code value: 255 for QT_8bit, 15 for QT_4bit.
31
+ /// Both quantizers reconstruct as rmin[i] + a[i] * code[i],
32
+ /// so only the divisor differs.
33
+ inline void make_coefficients(
34
+ const float* trained,
35
+ size_t d,
36
+ float* a,
37
+ float* rmin,
38
+ float levels = 255.0f) {
39
+ const float* vmin = trained;
40
+ const float* vdiff = trained + d;
41
+ for (size_t i = 0; i < d; i++) {
42
+ a[i] = vdiff[i] / levels;
43
+ rmin[i] = vmin[i] + 0.5f * a[i];
44
+ }
45
+ }
46
+
47
+ } // namespace sq8
48
+ } // namespace faiss
@@ -0,0 +1,546 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #pragma once
9
+
10
+ /* QT_8bit distance kernel for RISC-V Vector.
11
+ *
12
+ * Free-standing (no exceptions, heap or threads), so a freestanding RISC-V
13
+ * target can compile it. sq-rvv.cpp calls `l2_distance`; the block, strided
14
+ * and 4-bit forms serve callers that store codes transposed, such as the
15
+ * MTIA kernels in faiss/fb/mtia. No Buck target here builds the RISC-V CPU
16
+ * path. See sq8_coefficients.h for the meaning of a and e.
17
+ */
18
+
19
+ #include <riscv_vector.h>
20
+
21
+ #include <cstddef>
22
+ #include <cstdint>
23
+
24
+ namespace faiss {
25
+ namespace sq8_rvv {
26
+
27
+ /// Candidates per transposed block. One block fills an f32 LMUL 4 group at
28
+ /// VLEN=512, so every lane holds one candidate. On a smaller VLEN the block
29
+ /// functions loop over the block in pieces of one vector length.
30
+ constexpr size_t kBlockVectors = 64;
31
+
32
+ /// Squared L2 for a whole block, from codes stored transposed: the byte for
33
+ /// dimension i of lane c sits at `block[i * kBlockVectors + c]`. Each lane
34
+ /// accumulates one candidate, so no reduction is needed.
35
+ inline void l2_distance_block(
36
+ const uint8_t* block,
37
+ const float* a,
38
+ const float* e,
39
+ size_t d,
40
+ float* out) {
41
+ for (size_t c = 0; c < kBlockVectors;) {
42
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
43
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
44
+ for (size_t i = 0; i < d; i++) {
45
+ vuint8m1_t c8 =
46
+ __riscv_vle8_v_u8m1(block + c + i * kBlockVectors, vl);
47
+ vfloat32m4_t cf = __riscv_vfcvt_f_xu_v_f32m4(
48
+ __riscv_vzext_vf4_u32m4(c8, vl), vl);
49
+ // a[i] and e[i] are the same for every candidate in the block.
50
+ vfloat32m4_t t = __riscv_vfmv_v_f_f32m4(e[i], vl);
51
+ t = __riscv_vfnmsac_vf_f32m4(t, a[i], cf, vl);
52
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
53
+ }
54
+ __riscv_vse32_v_f32m4(out + c, acc, vl);
55
+ c += vl;
56
+ }
57
+ }
58
+
59
+ /// Squared L2 for two transposed blocks at once.
60
+ ///
61
+ /// Issuing the loads of both blocks first keeps two loads in flight. Four
62
+ /// blocks would need 16 of the 32 vector registers for accumulators and
63
+ /// spill.
64
+ inline void l2_distance_block_x2(
65
+ const uint8_t* block0,
66
+ const uint8_t* block1,
67
+ const float* a,
68
+ const float* e,
69
+ size_t d,
70
+ float* out0,
71
+ float* out1) {
72
+ for (size_t c = 0; c < kBlockVectors;) {
73
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
74
+ vfloat32m4_t acc0 = __riscv_vfmv_v_f_f32m4(0.0f, vl);
75
+ vfloat32m4_t acc1 = __riscv_vfmv_v_f_f32m4(0.0f, vl);
76
+ for (size_t i = 0; i < d; i++) {
77
+ vuint8m1_t v0 =
78
+ __riscv_vle8_v_u8m1(block0 + c + i * kBlockVectors, vl);
79
+ vuint8m1_t v1 =
80
+ __riscv_vle8_v_u8m1(block1 + c + i * kBlockVectors, vl);
81
+ const float ei = e[i];
82
+ const float ai = a[i];
83
+ vfloat32m4_t c0 = __riscv_vfcvt_f_xu_v_f32m4(
84
+ __riscv_vzext_vf4_u32m4(v0, vl), vl);
85
+ vfloat32m4_t t0 = __riscv_vfmv_v_f_f32m4(ei, vl);
86
+ t0 = __riscv_vfnmsac_vf_f32m4(t0, ai, c0, vl);
87
+ acc0 = __riscv_vfmacc_vv_f32m4(acc0, t0, t0, vl);
88
+ vfloat32m4_t c1 = __riscv_vfcvt_f_xu_v_f32m4(
89
+ __riscv_vzext_vf4_u32m4(v1, vl), vl);
90
+ vfloat32m4_t t1 = __riscv_vfmv_v_f_f32m4(ei, vl);
91
+ t1 = __riscv_vfnmsac_vf_f32m4(t1, ai, c1, vl);
92
+ acc1 = __riscv_vfmacc_vv_f32m4(acc1, t1, t1, vl);
93
+ }
94
+ __riscv_vse32_v_f32m4(out0 + c, acc0, vl);
95
+ __riscv_vse32_v_f32m4(out1 + c, acc1, vl);
96
+ c += vl;
97
+ }
98
+ }
99
+
100
+ /// The code-dependent inner product term for two transposed blocks at once.
101
+ inline void ip_partial_block_x2(
102
+ const uint8_t* block0,
103
+ const uint8_t* block1,
104
+ const float* eq,
105
+ size_t d,
106
+ float* out0,
107
+ float* out1) {
108
+ for (size_t c = 0; c < kBlockVectors;) {
109
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
110
+ vfloat32m4_t acc0 = __riscv_vfmv_v_f_f32m4(0.0f, vl);
111
+ vfloat32m4_t acc1 = __riscv_vfmv_v_f_f32m4(0.0f, vl);
112
+ for (size_t i = 0; i < d; i++) {
113
+ vuint8m1_t v0 =
114
+ __riscv_vle8_v_u8m1(block0 + c + i * kBlockVectors, vl);
115
+ vuint8m1_t v1 =
116
+ __riscv_vle8_v_u8m1(block1 + c + i * kBlockVectors, vl);
117
+ const float q = eq[i];
118
+ acc0 = __riscv_vfmacc_vf_f32m4(
119
+ acc0,
120
+ q,
121
+ __riscv_vfcvt_f_xu_v_f32m4(
122
+ __riscv_vzext_vf4_u32m4(v0, vl), vl),
123
+ vl);
124
+ acc1 = __riscv_vfmacc_vf_f32m4(
125
+ acc1,
126
+ q,
127
+ __riscv_vfcvt_f_xu_v_f32m4(
128
+ __riscv_vzext_vf4_u32m4(v1, vl), vl),
129
+ vl);
130
+ }
131
+ __riscv_vse32_v_f32m4(out0 + c, acc0, vl);
132
+ __riscv_vse32_v_f32m4(out1 + c, acc1, vl);
133
+ c += vl;
134
+ }
135
+ }
136
+
137
+ /// The low nibbles of a row of packed QT_4bit codes, as floats.
138
+ inline vfloat32m4_t low_nibbles(vuint8m1_t packed, size_t vl) {
139
+ return __riscv_vfcvt_f_xu_v_f32m4(
140
+ __riscv_vzext_vf4_u32m4(__riscv_vand_vx_u8m1(packed, 0x0F, vl), vl),
141
+ vl);
142
+ }
143
+
144
+ /// Squared L2 for a transposed block of QT_4bit codes. Faiss packs
145
+ /// dimension i into `code[i / 2]`, even dimensions in the low nibble and odd
146
+ /// in the high one, so d dimensions occupy (d + 1) / 2 bytes. For an odd d
147
+ /// the last byte holds one dimension, in its low nibble.
148
+ inline void l2_distance_4bit_block(
149
+ const uint8_t* block,
150
+ const float* a,
151
+ const float* e,
152
+ size_t d,
153
+ float* out) {
154
+ for (size_t c = 0; c < kBlockVectors;) {
155
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
156
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
157
+ for (size_t i = 0; i < d / 2; i++) {
158
+ vuint8m1_t packed =
159
+ __riscv_vle8_v_u8m1(block + c + i * kBlockVectors, vl);
160
+ vfloat32m4_t lo = __riscv_vfcvt_f_xu_v_f32m4(
161
+ __riscv_vzext_vf4_u32m4(
162
+ __riscv_vand_vx_u8m1(packed, 0x0F, vl), vl),
163
+ vl);
164
+ vfloat32m4_t hi = __riscv_vfcvt_f_xu_v_f32m4(
165
+ __riscv_vzext_vf4_u32m4(
166
+ __riscv_vsrl_vx_u8m1(packed, 4, vl), vl),
167
+ vl);
168
+ vfloat32m4_t t = __riscv_vfmv_v_f_f32m4(e[2 * i], vl);
169
+ t = __riscv_vfnmsac_vf_f32m4(t, a[2 * i], lo, vl);
170
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
171
+ t = __riscv_vfmv_v_f_f32m4(e[2 * i + 1], vl);
172
+ t = __riscv_vfnmsac_vf_f32m4(t, a[2 * i + 1], hi, vl);
173
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
174
+ }
175
+ if (d % 2 != 0) {
176
+ vfloat32m4_t lo = low_nibbles(
177
+ __riscv_vle8_v_u8m1(
178
+ block + c + (d / 2) * kBlockVectors, vl),
179
+ vl);
180
+ vfloat32m4_t t = __riscv_vfmv_v_f_f32m4(e[d - 1], vl);
181
+ t = __riscv_vfnmsac_vf_f32m4(t, a[d - 1], lo, vl);
182
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
183
+ }
184
+ __riscv_vse32_v_f32m4(out + c, acc, vl);
185
+ c += vl;
186
+ }
187
+ }
188
+
189
+ /// The code-dependent inner product term for a transposed QT_4bit block.
190
+ inline void ip_partial_4bit_block(
191
+ const uint8_t* block,
192
+ const float* eq,
193
+ size_t d,
194
+ float* out) {
195
+ for (size_t c = 0; c < kBlockVectors;) {
196
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
197
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
198
+ for (size_t i = 0; i < d / 2; i++) {
199
+ vuint8m1_t packed =
200
+ __riscv_vle8_v_u8m1(block + c + i * kBlockVectors, vl);
201
+ vfloat32m4_t lo = __riscv_vfcvt_f_xu_v_f32m4(
202
+ __riscv_vzext_vf4_u32m4(
203
+ __riscv_vand_vx_u8m1(packed, 0x0F, vl), vl),
204
+ vl);
205
+ vfloat32m4_t hi = __riscv_vfcvt_f_xu_v_f32m4(
206
+ __riscv_vzext_vf4_u32m4(
207
+ __riscv_vsrl_vx_u8m1(packed, 4, vl), vl),
208
+ vl);
209
+ acc = __riscv_vfmacc_vf_f32m4(acc, eq[2 * i], lo, vl);
210
+ acc = __riscv_vfmacc_vf_f32m4(acc, eq[2 * i + 1], hi, vl);
211
+ }
212
+ if (d % 2 != 0) {
213
+ vfloat32m4_t lo = low_nibbles(
214
+ __riscv_vle8_v_u8m1(
215
+ block + c + (d / 2) * kBlockVectors, vl),
216
+ vl);
217
+ acc = __riscv_vfmacc_vf_f32m4(acc, eq[d - 1], lo, vl);
218
+ }
219
+ __riscv_vse32_v_f32m4(out + c, acc, vl);
220
+ c += vl;
221
+ }
222
+ }
223
+
224
+ /// Squared L2 for one QT_4bit candidate in the transposed layout, for the
225
+ /// runs an `IDSelector` produces, which start anywhere.
226
+ inline float l2_distance_4bit_strided(
227
+ const uint8_t* p,
228
+ const float* a,
229
+ const float* e,
230
+ size_t d) {
231
+ float acc = 0.0f;
232
+ for (size_t i = 0; i < d / 2; i++) {
233
+ const uint8_t packed = p[i * kBlockVectors];
234
+ const float t0 =
235
+ e[2 * i] - a[2 * i] * static_cast<float>(packed & 0x0F);
236
+ const float t1 =
237
+ e[2 * i + 1] - a[2 * i + 1] * static_cast<float>(packed >> 4);
238
+ acc += t0 * t0 + t1 * t1;
239
+ }
240
+ if (d % 2 != 0) {
241
+ const uint8_t packed = p[(d / 2) * kBlockVectors];
242
+ const float t = e[d - 1] - a[d - 1] * static_cast<float>(packed & 0x0F);
243
+ acc += t * t;
244
+ }
245
+ return acc;
246
+ }
247
+
248
+ /// The code-dependent inner product term for one strided QT_4bit candidate.
249
+ inline float ip_partial_4bit_strided(
250
+ const uint8_t* p,
251
+ const float* eq,
252
+ size_t d) {
253
+ float acc = 0.0f;
254
+ for (size_t i = 0; i < d / 2; i++) {
255
+ const uint8_t packed = p[i * kBlockVectors];
256
+ acc += eq[2 * i] * static_cast<float>(packed & 0x0F);
257
+ acc += eq[2 * i + 1] * static_cast<float>(packed >> 4);
258
+ }
259
+ if (d % 2 != 0) {
260
+ const uint8_t packed = p[(d / 2) * kBlockVectors];
261
+ acc += eq[d - 1] * static_cast<float>(packed & 0x0F);
262
+ }
263
+ return acc;
264
+ }
265
+
266
+ /// Squared L2 for two transposed QT_4bit blocks at once: half the bytes of
267
+ /// QT_8bit and two loads in flight.
268
+ inline void l2_distance_4bit_block_x2(
269
+ const uint8_t* block0,
270
+ const uint8_t* block1,
271
+ const float* a,
272
+ const float* e,
273
+ size_t d,
274
+ float* out0,
275
+ float* out1) {
276
+ for (size_t c = 0; c < kBlockVectors;) {
277
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
278
+ vfloat32m4_t acc0 = __riscv_vfmv_v_f_f32m4(0.0f, vl);
279
+ vfloat32m4_t acc1 = __riscv_vfmv_v_f_f32m4(0.0f, vl);
280
+ for (size_t i = 0; i < d / 2; i++) {
281
+ vuint8m1_t p0 =
282
+ __riscv_vle8_v_u8m1(block0 + c + i * kBlockVectors, vl);
283
+ vuint8m1_t p1 =
284
+ __riscv_vle8_v_u8m1(block1 + c + i * kBlockVectors, vl);
285
+ const float e0 = e[2 * i];
286
+ const float e1 = e[2 * i + 1];
287
+ const float a0 = a[2 * i];
288
+ const float a1 = a[2 * i + 1];
289
+ auto step = [&](vuint8m1_t p, vfloat32m4_t acc) {
290
+ vfloat32m4_t lo = __riscv_vfcvt_f_xu_v_f32m4(
291
+ __riscv_vzext_vf4_u32m4(
292
+ __riscv_vand_vx_u8m1(p, 0x0F, vl), vl),
293
+ vl);
294
+ vfloat32m4_t hi = __riscv_vfcvt_f_xu_v_f32m4(
295
+ __riscv_vzext_vf4_u32m4(
296
+ __riscv_vsrl_vx_u8m1(p, 4, vl), vl),
297
+ vl);
298
+ vfloat32m4_t t = __riscv_vfmv_v_f_f32m4(e0, vl);
299
+ t = __riscv_vfnmsac_vf_f32m4(t, a0, lo, vl);
300
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
301
+ t = __riscv_vfmv_v_f_f32m4(e1, vl);
302
+ t = __riscv_vfnmsac_vf_f32m4(t, a1, hi, vl);
303
+ return __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
304
+ };
305
+ acc0 = step(p0, acc0);
306
+ acc1 = step(p1, acc1);
307
+ }
308
+ if (d % 2 != 0) {
309
+ const size_t last = (d / 2) * kBlockVectors;
310
+ auto tail = [&](vuint8m1_t p, vfloat32m4_t acc) {
311
+ vfloat32m4_t t = __riscv_vfmv_v_f_f32m4(e[d - 1], vl);
312
+ t = __riscv_vfnmsac_vf_f32m4(
313
+ t, a[d - 1], low_nibbles(p, vl), vl);
314
+ return __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
315
+ };
316
+ acc0 = tail(__riscv_vle8_v_u8m1(block0 + c + last, vl), acc0);
317
+ acc1 = tail(__riscv_vle8_v_u8m1(block1 + c + last, vl), acc1);
318
+ }
319
+ __riscv_vse32_v_f32m4(out0 + c, acc0, vl);
320
+ __riscv_vse32_v_f32m4(out1 + c, acc1, vl);
321
+ c += vl;
322
+ }
323
+ }
324
+
325
+ /// Squared L2 for one candidate whose code is stored transposed: its d bytes
326
+ /// sit `kBlockVectors` apart. For runs that do not start on a block
327
+ /// boundary, such as the runs an `IDSelector` produces.
328
+ inline float l2_distance_strided(
329
+ const uint8_t* p,
330
+ const float* a,
331
+ const float* e,
332
+ size_t d) {
333
+ const size_t vl = __riscv_vsetvl_e8m1(d > 0 ? d : 1);
334
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
335
+ size_t i = 0;
336
+ while (i < d) {
337
+ const size_t v = __riscv_vsetvl_e8m1(d - i);
338
+ vuint8m1_t c8 = __riscv_vlse8_v_u8m1(
339
+ p + i * kBlockVectors,
340
+ static_cast<ptrdiff_t>(kBlockVectors),
341
+ v);
342
+ vfloat32m4_t cf =
343
+ __riscv_vfcvt_f_xu_v_f32m4(__riscv_vzext_vf4_u32m4(c8, v), v);
344
+ vfloat32m4_t t = __riscv_vle32_v_f32m4(e + i, v);
345
+ t = __riscv_vfnmsac_vv_f32m4(t, __riscv_vle32_v_f32m4(a + i, v), cf, v);
346
+ acc = __riscv_vfmacc_vv_f32m4_tu(acc, t, t, v);
347
+ i += v;
348
+ }
349
+ vfloat32m1_t red = __riscv_vfredusum_vs_f32m4_f32m1(
350
+ acc, __riscv_vfmv_v_f_f32m1(0.0f, 1), vl);
351
+ return __riscv_vfmv_f_s_f32m1_f32(red);
352
+ }
353
+
354
+ /// The code-dependent inner product term for one transposed candidate.
355
+ inline float ip_partial_strided(const uint8_t* p, const float* eq, size_t d) {
356
+ const size_t vl = __riscv_vsetvl_e8m1(d > 0 ? d : 1);
357
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
358
+ size_t i = 0;
359
+ while (i < d) {
360
+ const size_t v = __riscv_vsetvl_e8m1(d - i);
361
+ vuint8m1_t c8 = __riscv_vlse8_v_u8m1(
362
+ p + i * kBlockVectors,
363
+ static_cast<ptrdiff_t>(kBlockVectors),
364
+ v);
365
+ vfloat32m4_t cf =
366
+ __riscv_vfcvt_f_xu_v_f32m4(__riscv_vzext_vf4_u32m4(c8, v), v);
367
+ acc = __riscv_vfmacc_vv_f32m4_tu(
368
+ acc, __riscv_vle32_v_f32m4(eq + i, v), cf, v);
369
+ i += v;
370
+ }
371
+ vfloat32m1_t red = __riscv_vfredusum_vs_f32m4_f32m1(
372
+ acc, __riscv_vfmv_v_f_f32m1(0.0f, 1), vl);
373
+ return __riscv_vfmv_f_s_f32m1_f32(red);
374
+ }
375
+
376
+ /// Squared L2 on the 8-bit grid for a transposed block, with integer
377
+ /// weights. The host scales the result back.
378
+ inline void l2_distance_int_block(
379
+ const uint8_t* block,
380
+ const uint8_t* qcode,
381
+ const int32_t* w,
382
+ size_t d,
383
+ float* out) {
384
+ for (size_t c = 0; c < kBlockVectors;) {
385
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
386
+ vint32m4_t acc = __riscv_vmv_v_x_i32m4(0, vl);
387
+ for (size_t i = 0; i < d; i++) {
388
+ vuint8m1_t c8 =
389
+ __riscv_vle8_v_u8m1(block + c + i * kBlockVectors, vl);
390
+ vint32m4_t c = __riscv_vreinterpret_v_u32m4_i32m4(
391
+ __riscv_vzext_vf4_u32m4(c8, vl));
392
+ vint32m4_t diff = __riscv_vsub_vx_i32m4(
393
+ c, static_cast<int32_t>(qcode[i]), vl);
394
+ vint32m4_t sq = __riscv_vmul_vv_i32m4(diff, diff, vl);
395
+ acc = __riscv_vmacc_vx_i32m4(acc, w[i], sq, vl);
396
+ }
397
+ __riscv_vse32_v_f32m4(out + c, __riscv_vfcvt_f_x_v_f32m4(acc, vl), vl);
398
+ c += vl;
399
+ }
400
+ }
401
+
402
+ /// The code-dependent term of the inner product, for a transposed block.
403
+ inline void ip_partial_block(
404
+ const uint8_t* block,
405
+ const float* eq,
406
+ size_t d,
407
+ float* out) {
408
+ for (size_t c = 0; c < kBlockVectors;) {
409
+ const size_t vl = __riscv_vsetvl_e32m4(kBlockVectors - c);
410
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
411
+ for (size_t i = 0; i < d; i++) {
412
+ vuint8m1_t c8 =
413
+ __riscv_vle8_v_u8m1(block + c + i * kBlockVectors, vl);
414
+ vfloat32m4_t cf = __riscv_vfcvt_f_xu_v_f32m4(
415
+ __riscv_vzext_vf4_u32m4(c8, vl), vl);
416
+ acc = __riscv_vfmacc_vf_f32m4(acc, eq[i], cf, vl);
417
+ }
418
+ __riscv_vse32_v_f32m4(out + c, acc, vl);
419
+ c += vl;
420
+ }
421
+ }
422
+
423
+ /// Squared L2 distance between a query and one 8-bit code.
424
+ inline float l2_distance(
425
+ const uint8_t* code,
426
+ const float* a,
427
+ const float* e,
428
+ size_t d) {
429
+ // VLMAX for e8m1 equals the f32 lane count of an m4 group. Guard d == 0,
430
+ // because vsetvl then returns 0 and the chunk loop stalls.
431
+ const size_t vl = __riscv_vsetvl_e8m1(d > 0 ? d : 1);
432
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
433
+
434
+ size_t i = 0;
435
+ if (i + vl <= d) {
436
+ // Software pipeline: load the next chunk's code bytes at the top.
437
+ vuint8m1_t c8 = __riscv_vle8_v_u8m1(code + i, vl);
438
+ i += vl;
439
+
440
+ for (; i + vl <= d; i += vl) {
441
+ vuint8m1_t c8_next = __riscv_vle8_v_u8m1(code + i, vl);
442
+ vfloat32m4_t cf = __riscv_vfcvt_f_xu_v_f32m4(
443
+ __riscv_vzext_vf4_u32m4(c8, vl), vl);
444
+ vfloat32m4_t t = __riscv_vle32_v_f32m4(e + i - vl, vl);
445
+ t = __riscv_vfnmsac_vv_f32m4(
446
+ t, __riscv_vle32_v_f32m4(a + i - vl, vl), cf, vl);
447
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
448
+ c8 = c8_next;
449
+ }
450
+
451
+ vfloat32m4_t cf =
452
+ __riscv_vfcvt_f_xu_v_f32m4(__riscv_vzext_vf4_u32m4(c8, vl), vl);
453
+ vfloat32m4_t t = __riscv_vle32_v_f32m4(e + i - vl, vl);
454
+ t = __riscv_vfnmsac_vv_f32m4(
455
+ t, __riscv_vle32_v_f32m4(a + i - vl, vl), cf, vl);
456
+ acc = __riscv_vfmacc_vv_f32m4(acc, t, t, vl);
457
+ }
458
+
459
+ // Tail: fewer than vl dimensions remain. The reduction covers every lane.
460
+ if (i < d) {
461
+ const size_t vt = __riscv_vsetvl_e8m1(d - i);
462
+ vuint8m1_t c8 = __riscv_vle8_v_u8m1(code + i, vt);
463
+ vfloat32m4_t cf =
464
+ __riscv_vfcvt_f_xu_v_f32m4(__riscv_vzext_vf4_u32m4(c8, vt), vt);
465
+ vfloat32m4_t t = __riscv_vle32_v_f32m4(e + i, vt);
466
+ t = __riscv_vfnmsac_vv_f32m4(
467
+ t, __riscv_vle32_v_f32m4(a + i, vt), cf, vt);
468
+ acc = __riscv_vfmacc_vv_f32m4_tu(acc, t, t, vt);
469
+ }
470
+
471
+ vfloat32m1_t red = __riscv_vfredusum_vs_f32m4_f32m1(
472
+ acc, __riscv_vfmv_v_f_f32m1(0.0f, 1), vl);
473
+ return __riscv_vfmv_f_s_f32m1_f32(red);
474
+ }
475
+
476
+ /// Inner product between a query and one 8-bit code, without the constant.
477
+ ///
478
+ /// Expanding the reconstruction,
479
+ ///
480
+ /// sum q[i] * v[i] = sum q[i]*rmin[i] + sum (q[i]*a[i]) * code[i]
481
+ ///
482
+ /// the first term does not depend on the code, so the caller adds it. This
483
+ /// computes the second, with u[i] = q[i] * a[i].
484
+ inline float ip_partial(const uint8_t* code, const float* u, size_t d) {
485
+ const size_t vl = __riscv_vsetvl_e8m1(d > 0 ? d : 1);
486
+ vfloat32m4_t acc = __riscv_vfmv_v_f_f32m4(0.0f, vl);
487
+
488
+ size_t i = 0;
489
+ for (; i + vl <= d; i += vl) {
490
+ vuint8m1_t c8 = __riscv_vle8_v_u8m1(code + i, vl);
491
+ vfloat32m4_t cf =
492
+ __riscv_vfcvt_f_xu_v_f32m4(__riscv_vzext_vf4_u32m4(c8, vl), vl);
493
+ acc = __riscv_vfmacc_vv_f32m4(
494
+ acc, cf, __riscv_vle32_v_f32m4(u + i, vl), vl);
495
+ }
496
+ if (i < d) {
497
+ const size_t vt = __riscv_vsetvl_e8m1(d - i);
498
+ vuint8m1_t c8 = __riscv_vle8_v_u8m1(code + i, vt);
499
+ vfloat32m4_t cf =
500
+ __riscv_vfcvt_f_xu_v_f32m4(__riscv_vzext_vf4_u32m4(c8, vt), vt);
501
+ acc = __riscv_vfmacc_vv_f32m4_tu(
502
+ acc, cf, __riscv_vle32_v_f32m4(u + i, vt), vt);
503
+ }
504
+
505
+ vfloat32m1_t red = __riscv_vfredusum_vs_f32m4_f32m1(
506
+ acc, __riscv_vfmv_v_f_f32m1(0.0f, 1), vl);
507
+ return __riscv_vfmv_f_s_f32m1_f32(red);
508
+ }
509
+
510
+ /// Squared L2 distance with the query quantized onto the same 8-bit grid.
511
+ ///
512
+ /// sum (q[i] - v[i])^2 = sum a[i]^2 * (qcode[i] - code[i])^2
513
+ ///
514
+ /// The weights hold a[i]^2 as integers, so the loop is integer only. The
515
+ /// caller scales the result back and must keep max(w) * 65025 * d inside
516
+ /// int32.
517
+ inline float l2_distance_int(
518
+ const uint8_t* code,
519
+ const uint8_t* qcode,
520
+ const int32_t* w,
521
+ size_t d) {
522
+ // Accumulate at LMUL 4, not 8. A group of eight uses eight of the 32
523
+ // vector registers and leaves only four groups, which is slower.
524
+ vint32m4_t sum = __riscv_vmv_v_x_i32m4(0, __riscv_vsetvlmax_e32m4());
525
+ size_t i = 0;
526
+ while (i < d) {
527
+ const size_t vl = __riscv_vsetvl_e16m2(d - i);
528
+ vint16m2_t c = __riscv_vreinterpret_v_u16m2_i16m2(
529
+ __riscv_vzext_vf2_u16m2(__riscv_vle8_v_u8m1(code + i, vl), vl));
530
+ vint16m2_t q =
531
+ __riscv_vreinterpret_v_u16m2_i16m2(__riscv_vzext_vf2_u16m2(
532
+ __riscv_vle8_v_u8m1(qcode + i, vl), vl));
533
+ vint16m2_t diff = __riscv_vsub_vv_i16m2(q, c, vl);
534
+ // A difference of two bytes fits in int16 and its square in int32.
535
+ vint32m4_t sq = __riscv_vwmul_vv_i32m4(diff, diff, vl);
536
+ sum = __riscv_vmacc_vv_i32m4_tu(
537
+ sum, sq, __riscv_vle32_v_i32m4(w + i, vl), vl);
538
+ i += vl;
539
+ }
540
+ vint32m1_t r = __riscv_vredsum_vs_i32m4_i32m1(
541
+ sum, __riscv_vmv_v_x_i32m1(0, 1), __riscv_vsetvlmax_e32m4());
542
+ return static_cast<float>(__riscv_vmv_x_s_i32m1_i32(r));
543
+ }
544
+
545
+ } // namespace sq8_rvv
546
+ } // namespace faiss
@@ -366,15 +366,15 @@ void train_NonUniform(
366
366
  } else {
367
367
  // transpose
368
368
  std::vector<float> xt(n * d);
369
- for (idx_t i = 1; i < n; i++) {
369
+ for (idx_t i = 0; i < n; i++) {
370
370
  const float* xi = x + i * d;
371
371
  for (int j = 0; j < d; j++) {
372
372
  xt[j * n + i] = xi[j];
373
373
  }
374
374
  }
375
- std::vector<float> trained_d(2);
376
375
  #pragma omp parallel for
377
376
  for (int j = 0; j < d; j++) {
377
+ std::vector<float> trained_d(2);
378
378
  train_Uniform(rs, rs_arg, n, k, xt.data() + j * n, trained_d);
379
379
  vmin[j] = trained_d[0];
380
380
  vmax[j] = trained_d[1];
@@ -36,6 +36,13 @@ constexpr int AVAILABLE_SIMD_LEVELS_AVX2_NEON = AVAILABLE_SIMD_LEVELS_NONE |
36
36
  constexpr int AVAILABLE_SIMD_LEVELS_BASE = AVAILABLE_SIMD_LEVELS_AVX2_NEON |
37
37
  (1 << int(SIMDLevel::AVX512)) | (1 << int(SIMDLevel::RISCV_RVV));
38
38
 
39
+ // BASE_NO_AVX512: BASE minus AVX512, for functions implemented at NONE,
40
+ // AVX2, ARM_NEON and RISCV_RVV but with no AVX512 specialization. AVX512
41
+ // machines fall through to AVX2, while RISC-V dispatches directly to the
42
+ // native vector-length-agnostic RVV kernels.
43
+ constexpr int AVAILABLE_SIMD_LEVELS_BASE_NO_AVX512 =
44
+ AVAILABLE_SIMD_LEVELS_BASE & ~(1 << int(SIMDLevel::AVX512));
45
+
39
46
  // BASE_WITH_VPOPCNT: BASE + AVX512_VPOPCNT, for kernels that need only
40
47
  // VPOPCNTDQ on top of baseline AVX-512 (Ice Lake, Zen 4, Zen 5).
41
48
  constexpr int AVAILABLE_SIMD_LEVELS_BASE_WITH_VPOPCNT =
@@ -901,6 +901,20 @@ inline float horizontal_add(const simd8float32_tpl<SIMDLevel::AVX2>& a) {
901
901
  return _mm_cvtss_f32(v3);
902
902
  }
903
903
 
904
+ /// (s[0] + s[2]) + (s[1] + s[3])
905
+ FAISS_ALWAYS_INLINE float reduce_4(__m128 s) {
906
+ const __m128 h = _mm_add_ps(s, _mm_movehl_ps(s, s));
907
+ return _mm_cvtss_f32(_mm_add_ss(h, _mm_movehdup_ps(h)));
908
+ }
909
+
910
+ /// reduce_4 of four vectors at once, with the same order for each, so a
911
+ /// batched distance is bit-identical to the single one.
912
+ FAISS_ALWAYS_INLINE __m128 reduce_4x4(__m128 a, __m128 b, __m128 c, __m128 d) {
913
+ const __m128 ab = _mm_add_ps(_mm_unpacklo_ps(a, b), _mm_unpackhi_ps(a, b));
914
+ const __m128 cd = _mm_add_ps(_mm_unpacklo_ps(c, d), _mm_unpackhi_ps(c, d));
915
+ return _mm_add_ps(_mm_movelh_ps(ab, cd), _mm_movehl_ps(cd, ab));
916
+ }
917
+
904
918
  } // namespace
905
919
 
906
920
  } // namespace faiss
@@ -16,23 +16,22 @@
16
16
  */
17
17
 
18
18
  #include <faiss/impl/simdlib/simdlib.h>
19
+ #include <faiss/utils/simd_levels.h>
19
20
 
20
- // Platform specializations — guarded by COMPILE_SIMD_* AND compiler macros.
21
- // In DD mode: COMPILE_SIMD_* are target-wide, compiler macros are per-file.
22
- // Only per-SIMD TUs (compiled with -mavx2 etc.) see the platform
23
- // specializations. In static mode: only the compiled-in level is available.
21
+ // Platform specializations — guarded by FAISS_SIMD_TU, so that only per-SIMD
22
+ // TUs (compiled with -mavx2 etc.) see them. In static mode: only the
23
+ // compiled-in level is available.
24
24
 
25
- #if (defined(COMPILE_SIMD_AVX512) || defined(COMPILE_SIMD_AVX512_SPR)) && \
26
- defined(__AVX512F__)
25
+ #if FAISS_SIMD_TU(AVX512) || FAISS_SIMD_TU(AVX512_SPR)
27
26
 
28
27
  // AVX512 includes AVX2 (simdlib_avx512.h includes simdlib_avx2.h)
29
28
  #include <faiss/impl/simdlib/simdlib_avx512.h>
30
29
 
31
- #elif defined(COMPILE_SIMD_AVX2) && defined(__AVX2__)
30
+ #elif FAISS_SIMD_TU(AVX2)
32
31
 
33
32
  #include <faiss/impl/simdlib/simdlib_avx2.h>
34
33
 
35
- #elif defined(COMPILE_SIMD_ARM_NEON) && defined(__aarch64__)
34
+ #elif FAISS_SIMD_TU(ARM_NEON)
36
35
 
37
36
  #include <faiss/impl/simdlib/simdlib_neon.h>
38
37