usearch 0.22.3 → 1.0.0

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 (93) hide show
  1. package/.github/workflows/prerelease.yml +3 -3
  2. package/.releaserc +10 -2
  3. package/.vscode/launch.json +10 -10
  4. package/.vscode/settings.json +9 -1
  5. package/.vscode/tasks.json +4 -4
  6. package/CMakeLists.txt +6 -1
  7. package/Cargo.toml +2 -2
  8. package/README.md +35 -41
  9. package/build.rs +2 -2
  10. package/c/CMakeLists.txt +48 -0
  11. package/c/README.md +34 -0
  12. package/c/lib.cpp +83 -72
  13. package/c/test.c +20 -20
  14. package/c/usearch.h +132 -32
  15. package/conanfile.py +1 -1
  16. package/cpp/CMakeLists.txt +3 -1
  17. package/cpp/README.md +16 -16
  18. package/cpp/bench.cpp +50 -126
  19. package/cpp/test.cpp +164 -80
  20. package/docs/benchmarks.md +26 -5
  21. package/docs/c/reference.rst +3 -3
  22. package/docs/compilation.md +31 -49
  23. package/docs/cpp/index.rst +1 -1
  24. package/docs/cpp/reference.rst +4 -4
  25. package/golang/lib.go +275 -141
  26. package/golang/lib_test.go +105 -0
  27. package/include/usearch/index.hpp +1681 -1656
  28. package/include/usearch/index_dense.hpp +1400 -0
  29. package/include/usearch/index_plugins.hpp +1263 -0
  30. package/java/README.md +1 -1
  31. package/java/cloud/unum/usearch/Index.java +10 -10
  32. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +34 -37
  33. package/java/test/IndexTest.java +1 -1
  34. package/javascript/README.md +6 -6
  35. package/javascript/lib.cpp +94 -55
  36. package/javascript/test.js +25 -11
  37. package/javascript/usearch.d.ts +87 -0
  38. package/javascript/usearch.js +2 -118
  39. package/objc/USearchObjective.mm +57 -31
  40. package/objc/include/USearchObjective.h +32 -22
  41. package/package.json +5 -3
  42. package/python/README.md +23 -14
  43. package/python/lib.cpp +468 -444
  44. package/python/scripts/bench.ipynb +3 -2
  45. package/python/scripts/bench.py +4 -4
  46. package/python/scripts/index_faiss.py +5 -5
  47. package/python/scripts/join.py +4 -4
  48. package/python/scripts/test.py +152 -61
  49. package/python/scripts/test_jit.py +10 -12
  50. package/python/scripts/test_sparse.py +32 -0
  51. package/python/usearch/client.py +26 -28
  52. package/python/usearch/eval.py +73 -23
  53. package/python/usearch/index.py +520 -228
  54. package/python/usearch/io.py +57 -25
  55. package/python/usearch/numba.py +4 -4
  56. package/python/usearch/server.py +10 -10
  57. package/rust/lib.cpp +22 -27
  58. package/rust/lib.hpp +11 -12
  59. package/rust/lib.rs +3 -3
  60. package/setup.py +5 -2
  61. package/simsimd/.github/workflows/prerelease.yml +73 -0
  62. package/simsimd/.github/workflows/release.yml +110 -0
  63. package/simsimd/.github/workflows/update_version.sh +3 -0
  64. package/simsimd/.releaserc +77 -0
  65. package/simsimd/.vscode/launch.json +27 -1
  66. package/simsimd/.vscode/settings.json +22 -1
  67. package/simsimd/.vscode/tasks.json +6 -0
  68. package/simsimd/CMakeLists.txt +3 -2
  69. package/simsimd/README.md +82 -46
  70. package/simsimd/bench.cxx +63 -24
  71. package/simsimd/include/simsimd/simsimd.h +88 -66
  72. package/simsimd/include/simsimd/simsimd_chem.h +126 -0
  73. package/simsimd/package.json +8 -0
  74. package/simsimd/pyproject.toml +42 -0
  75. package/simsimd/python/python.c +81 -0
  76. package/simsimd/python/run.sh +1 -0
  77. package/simsimd/python/test.c +109 -0
  78. package/simsimd/python/test.py +23 -0
  79. package/simsimd/setup.py +44 -0
  80. package/swift/Index+Sugar.swift +75 -23
  81. package/swift/README.md +26 -2
  82. package/swift/Test.swift +19 -7
  83. package/swift/USearch.swift +2 -2
  84. package/tsconfig.json +8 -0
  85. package/wasm/CMakeLists.txt +1 -0
  86. package/wasmer.toml +1 -1
  87. package/wolfram/CMakeLists.txt +1 -0
  88. package/wolfram/lib.cpp +11 -13
  89. package/golang/usearch.h +0 -9
  90. package/golang/usearch_par_test.go +0 -40
  91. package/golang/usearch_test.go +0 -82
  92. package/include/usearch/index_punned_dense.hpp +0 -1001
  93. package/include/usearch/index_punned_helpers.hpp +0 -676
@@ -12,6 +12,7 @@
12
12
  #pragma once
13
13
  #include <math.h> // `sqrt`
14
14
  #include <stddef.h> // `size_t`
15
+ #include <stdint.h>
15
16
 
16
17
  #if defined(__AVX2__)
17
18
  #include <immintrin.h>
@@ -25,6 +26,13 @@
25
26
  #include <arm_sve.h>
26
27
  #endif
27
28
 
29
+ #ifdef _MSC_VER
30
+ #include <intrin.h>
31
+ #define popcount32 __popcnt
32
+ #else
33
+ #define popcount32 __builtin_popcount
34
+ #endif
35
+
28
36
  #ifdef __cplusplus
29
37
  extern "C" {
30
38
  #endif
@@ -38,7 +46,7 @@ union simsimd_f32i32_t {
38
46
  float f;
39
47
  };
40
48
 
41
- inline static simsimd_f32_t simsimd_dot_f32sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
49
+ inline static simsimd_f32_t simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
42
50
  #if defined(__ARM_FEATURE_SVE)
43
51
  size_t i = 0;
44
52
  svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
@@ -50,14 +58,14 @@ inline static simsimd_f32_t simsimd_dot_f32sve(simsimd_f32_t const* a, simsimd_f
50
58
  i += svcntw();
51
59
  pg_vec = svwhilelt_b32(i, d);
52
60
  } while (svptest_any(svptrue_b32(), pg_vec));
53
- return svaddv_f32(svptrue_b32(), ab_vec);
61
+ return 1 - svaddv_f32(svptrue_b32(), ab_vec);
54
62
  #else
55
63
  (void)a, (void)b, (void)d;
56
- return 0;
64
+ return -1;
57
65
  #endif
58
66
  }
59
67
 
60
- inline static simsimd_f32_t simsimd_cos_f32sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
68
+ inline static simsimd_f32_t simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
61
69
  #if defined(__ARM_FEATURE_SVE)
62
70
  size_t i = 0;
63
71
  svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
@@ -76,14 +84,14 @@ inline static simsimd_f32_t simsimd_cos_f32sve(simsimd_f32_t const* a, simsimd_f
76
84
  simsimd_f32_t ab = svaddv_f32(svptrue_b32(), ab_vec);
77
85
  simsimd_f32_t a2 = svaddv_f32(svptrue_b32(), a2_vec);
78
86
  simsimd_f32_t b2 = svaddv_f32(svptrue_b32(), b2_vec);
79
- return ab / (sqrt(a2) * sqrt(b2));
87
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
80
88
  #else
81
89
  (void)a, (void)b, (void)d;
82
- return 0;
90
+ return -1;
83
91
  #endif
84
92
  }
85
93
 
86
- inline static simsimd_f32_t simsimd_l2sq_f32sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
94
+ inline static simsimd_f32_t simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
87
95
  #if defined(__ARM_FEATURE_SVE)
88
96
  size_t i = 0;
89
97
  svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
@@ -100,11 +108,39 @@ inline static simsimd_f32_t simsimd_l2sq_f32sve(simsimd_f32_t const* a, simsimd_
100
108
  return sqrt(d2);
101
109
  #else
102
110
  (void)a, (void)b, (void)d;
103
- return 0;
111
+ return -1;
104
112
  #endif
105
113
  }
106
114
 
107
- inline static simsimd_f16_t simsimd_l2sq_f16sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
115
+ inline static simsimd_f32_t simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
116
+ #if defined(__ARM_FEATURE_SVE)
117
+ size_t i = 0;
118
+ svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
119
+ svfloat16_t a2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
120
+ svfloat16_t b2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
121
+ svbool_t pg_vec = svwhilelt_b16(i, d);
122
+ simsimd_f16_t const* a = (simsimd_f16_t const*)(a_enum);
123
+ simsimd_f16_t const* b = (simsimd_f16_t const*)(b_enum);
124
+ do {
125
+ svfloat16_t a_vec = svld1_f16(pg_vec, (float16_t const*)a + i);
126
+ svfloat16_t b_vec = svld1_f16(pg_vec, (float16_t const*)b + i);
127
+ ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
128
+ a2_vec = svmla_f16_x(pg_vec, a2_vec, a_vec, a_vec);
129
+ b2_vec = svmla_f16_x(pg_vec, b2_vec, b_vec, b_vec);
130
+ i += svcnth();
131
+ pg_vec = svwhilelt_b16(i, d);
132
+ } while (svptest_any(svptrue_b16(), pg_vec));
133
+ simsimd_f16_t ab = svaddv_f16(svptrue_b16(), ab_vec);
134
+ simsimd_f16_t a2 = svaddv_f16(svptrue_b16(), a2_vec);
135
+ simsimd_f16_t b2 = svaddv_f16(svptrue_b16(), b2_vec);
136
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
137
+ #else
138
+ (void)a_enum, (void)b_enum, (void)d;
139
+ return -1;
140
+ #endif
141
+ }
142
+
143
+ inline static simsimd_f32_t simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
108
144
  #if defined(__ARM_FEATURE_SVE)
109
145
  size_t i = 0;
110
146
  svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
@@ -120,16 +156,14 @@ inline static simsimd_f16_t simsimd_l2sq_f16sve(simsimd_f16_t const* a_enum, sim
120
156
  pg_vec = svwhilelt_b16(i, d);
121
157
  } while (svptest_any(svptrue_b16(), pg_vec));
122
158
  float16_t f16 = svaddv_f16(svptrue_b16(), d2_vec);
123
- simsimd_f16_t i16;
124
- memcpy(&i16, &f16, sizeof(i16));
125
- return i16;
159
+ return 1 - simsimd_f32_t(f16);
126
160
  #else
127
161
  (void)a_enum, (void)b_enum, (void)d;
128
- return 0;
162
+ return -1;
129
163
  #endif
130
164
  }
131
165
 
132
- inline static size_t simsimd_hamming_b1x8sve(uint8_t const* a, uint8_t const* b, size_t d) {
166
+ inline static simsimd_f32_t simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t const* b, size_t d) {
133
167
  #if defined(__ARM_FEATURE_SVE)
134
168
  size_t i = 0;
135
169
  svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
@@ -142,26 +176,26 @@ inline static size_t simsimd_hamming_b1x8sve(uint8_t const* a, uint8_t const* b,
142
176
  i += svcntb() * __CHAR_BIT__;
143
177
  pg_vec = svwhilelt_b32(i, d);
144
178
  } while (svptest_any(svptrue_b32(), pg_vec));
145
- return svaddv_u8(svptrue_b32(), d_vec);
179
+ return 1 - svaddv_u8(svptrue_b32(), d_vec);
146
180
  #else
147
181
  (void)a, (void)b, (void)d;
148
- return 0;
182
+ return -1;
149
183
  #endif
150
184
  }
151
185
 
152
- inline static simsimd_f32_t simsimd_dot_f32x4neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
186
+ inline static simsimd_f32_t simsimd_dot_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
153
187
  #if defined(__ARM_NEON)
154
188
  float32x4_t ab_vec = vdupq_n_f32(0);
155
189
  for (size_t i = 0; i != d; i += 4)
156
190
  ab_vec = vfmaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
157
- return vaddvq_f32(ab_vec);
191
+ return 1 - vaddvq_f32(ab_vec);
158
192
  #else
159
193
  (void)a, (void)b, (void)d;
160
- return 0;
194
+ return -1;
161
195
  #endif
162
196
  }
163
197
 
164
- inline static simsimd_f32_t simsimd_cos_f16x4neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
198
+ inline static simsimd_f32_t simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
165
199
  #if defined(__ARM_NEON)
166
200
  float32x4_t ab_vec = vdupq_n_f32(0);
167
201
  float32x4_t a2_vec = vdupq_n_f32(0);
@@ -177,14 +211,14 @@ inline static simsimd_f32_t simsimd_cos_f16x4neon(simsimd_f16_t const* a, simsim
177
211
  simsimd_f32_t ab = vaddvq_f32(ab_vec);
178
212
  simsimd_f32_t a2 = vaddvq_f32(a2_vec);
179
213
  simsimd_f32_t b2 = vaddvq_f32(b2_vec);
180
- return ab / (sqrt(a2) * sqrt(b2));
214
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
181
215
  #else
182
216
  (void)a, (void)b, (void)d;
183
- return 0;
217
+ return -1;
184
218
  #endif
185
219
  }
186
220
 
187
- inline static simsimd_f32_t simsimd_cos_i8x16neon(int8_t const* a, int8_t const* b, size_t d) {
221
+ inline static simsimd_f32_t simsimd_cos_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
188
222
  #if defined(__ARM_NEON)
189
223
  int32x4_t ab_vec = vdupq_n_s32(0);
190
224
  int32x4_t a2_vec = vdupq_n_s32(0);
@@ -205,11 +239,11 @@ inline static simsimd_f32_t simsimd_cos_i8x16neon(int8_t const* a, int8_t const*
205
239
  int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
206
240
  int16x8_t a2_part_vec = vmulq_s16(a_vec, a_vec);
207
241
  int16x8_t b2_part_vec = vmulq_s16(b_vec, b_vec);
208
- ab_vec =
242
+ ab_vec = //
209
243
  vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), vmovl_s16(vget_low_s16(ab_part_vec))));
210
- a2_vec =
244
+ a2_vec = //
211
245
  vaddq_s32(a2_vec, vaddq_s32(vmovl_s16(vget_high_s16(a2_part_vec)), vmovl_s16(vget_low_s16(a2_part_vec))));
212
- b2_vec =
246
+ b2_vec = //
213
247
  vaddq_s32(b2_vec, vaddq_s32(vmovl_s16(vget_high_s16(b2_part_vec)), vmovl_s16(vget_low_s16(b2_part_vec))));
214
248
  }
215
249
  #endif
@@ -220,14 +254,14 @@ inline static simsimd_f32_t simsimd_cos_i8x16neon(int8_t const* a, int8_t const*
220
254
  int32_t a2 = vget_lane_s32(vpadd_s32(a2_part, a2_part), 0);
221
255
  int32x2_t b2_part = vadd_s32(vget_high_s32(b2_vec), vget_low_s32(b2_vec));
222
256
  int32_t b2 = vget_lane_s32(vpadd_s32(b2_part, b2_part), 0);
223
- return ab / (sqrt(a2) * sqrt(b2));
257
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
224
258
  #else
225
259
  (void)a, (void)b, (void)d;
226
- return 0;
260
+ return -1;
227
261
  #endif
228
262
  }
229
263
 
230
- inline static simsimd_f32_t simsimd_cos_f32x4neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
264
+ inline static simsimd_f32_t simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
231
265
  #if defined(__ARM_NEON)
232
266
  float32x4_t ab_vec = vdupq_n_f32(0);
233
267
  float32x4_t a2_vec = vdupq_n_f32(0);
@@ -242,14 +276,14 @@ inline static simsimd_f32_t simsimd_cos_f32x4neon(simsimd_f32_t const* a, simsim
242
276
  simsimd_f32_t ab = vaddvq_f32(ab_vec);
243
277
  simsimd_f32_t a2 = vaddvq_f32(a2_vec);
244
278
  simsimd_f32_t b2 = vaddvq_f32(b2_vec);
245
- return ab / (sqrt(a2) * sqrt(b2));
279
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
246
280
  #else
247
281
  (void)a, (void)b, (void)d;
248
- return 0;
282
+ return -1;
249
283
  #endif
250
284
  }
251
285
 
252
- inline static size_t simsimd_hamming_b1x128sve(uint8_t const* a, uint8_t const* b, size_t d) {
286
+ inline static simsimd_f32_t simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
253
287
  #if defined(__ARM_NEON)
254
288
  /// Contains 16x 8-bit integers with running population count sums.
255
289
  uint8x16_t d_vec = vdupq_n_u8(0);
@@ -259,14 +293,14 @@ inline static size_t simsimd_hamming_b1x128sve(uint8_t const* a, uint8_t const*
259
293
  uint8x16_t a_xor_b_vec = veorq_u8(a_vec, b_vec);
260
294
  d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
261
295
  }
262
- return vaddvq_u8(d_vec);
296
+ return 1 - vaddvq_u8(d_vec);
263
297
  #else
264
298
  (void)a, (void)b, (void)d;
265
- return 0;
299
+ return -1;
266
300
  #endif
267
301
  }
268
302
 
269
- inline static simsimd_f32_t simsimd_dot_f32x4avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
303
+ inline static simsimd_f32_t simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
270
304
  #if defined(__AVX2__)
271
305
  __m128 ab_vec = _mm_set1_ps(0);
272
306
  for (size_t i = 0; i != d; i += 4)
@@ -275,14 +309,14 @@ inline static simsimd_f32_t simsimd_dot_f32x4avx2(simsimd_f32_t const* a, simsim
275
309
  ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
276
310
  simsimd_f32i32_t ab_union;
277
311
  ab_union.i = _mm_cvtsi128_si32(_mm_castps_si128(ab_vec));
278
- return ab_union.f;
312
+ return 1 - ab_union.f;
279
313
  #else
280
314
  (void)a, (void)b, (void)d;
281
- return 0;
315
+ return -1;
282
316
  #endif
283
317
  }
284
318
 
285
- inline static simsimd_f32_t simsimd_cos_f16x16avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
319
+ inline static simsimd_f32_t simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
286
320
  #if defined(__AVX512F__)
287
321
  __m512 ab_vec = _mm512_set1_ps(0);
288
322
  __m512 a2_vec = _mm512_set1_ps(0);
@@ -297,35 +331,14 @@ inline static simsimd_f32_t simsimd_cos_f16x16avx512(simsimd_f16_t const* a, sim
297
331
  simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
298
332
  simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
299
333
  simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
300
- return ab / (sqrt(a2) * sqrt(b2));
334
+ return 1 - ab / (sqrt(a2) * sqrt(b2));
301
335
  #else
302
336
  (void)a, (void)b, (void)d;
303
- return 0;
337
+ return -1;
304
338
  #endif
305
339
  }
306
340
 
307
- inline static int32_t simsimd_dot_i8x16avx2(int8_t const* a, int8_t const* b, size_t d) {
308
- #if defined(__AVX2__)
309
- __m256i ab_vec = _mm256_set1_epi32(0);
310
- for (size_t i = 0; i != d; i += 8) {
311
- ab_vec = _mm256_add_epi32( //
312
- ab_vec, _mm256_madd_epi16( //
313
- _mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(a + i))),
314
- _mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(b + i)))));
315
- }
316
- ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
317
- ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
318
- ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
319
- simsimd_f32i32_t ab_union;
320
- ab_union.f = _mm256_cvtss_f32(_mm256_castsi256_ps(ab_vec));
321
- return ab_union.i;
322
- #else
323
- (void)a, (void)b, (void)d;
324
- return 0;
325
- #endif
326
- }
327
-
328
- inline static simsimd_f32_t simsimd_cos_f32x4avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
341
+ inline static simsimd_f32_t simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
329
342
  #if defined(__AVX2__)
330
343
  __m128 ab_vec = _mm_set1_ps(0);
331
344
  __m128 a2_vec = _mm_set1_ps(0);
@@ -346,14 +359,14 @@ inline static simsimd_f32_t simsimd_cos_f32x4avx2(simsimd_f32_t const* a, simsim
346
359
  simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
347
360
  simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
348
361
  simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
349
- return ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
362
+ return 1 - ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
350
363
  #else
351
364
  (void)a, (void)b, (void)d;
352
- return 0;
365
+ return -1;
353
366
  #endif
354
367
  }
355
368
 
356
- inline static size_t simsimd_hamming_b1x128avx512(uint8_t const* a, uint8_t const* b, size_t d) {
369
+ inline static simsimd_f32_t simsimd_hamming_b1x128_avx512(uint8_t const* a, uint8_t const* b, size_t d) {
357
370
  #if defined(__AVX512VPOPCNTDQ__)
358
371
  size_t words = d / 128;
359
372
  uint64_t const* a64 = (uint64_t const*)(a);
@@ -369,10 +382,19 @@ inline static size_t simsimd_hamming_b1x128avx512(uint8_t const* a, uint8_t cons
369
382
  return _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
370
383
  #else
371
384
  (void)a, (void)b, (void)d;
372
- return 0;
385
+ return -1;
373
386
  #endif
374
387
  }
375
388
 
389
+ inline static simsimd_f32_t simsimd_tanimoto_b1x8_naive(uint8_t const* a, uint8_t const* b, size_t d) {
390
+ size_t and_count = 0, or_count = 0;
391
+ for (size_t i = 0; i != d; ++i)
392
+ and_count += popcount32(a[i] & b[i]), or_count += popcount32(a[i] | b[i]);
393
+ return 1 - (simsimd_f32_t)(and_count) / or_count;
394
+ }
395
+
396
+ #undef popcount32
397
+
376
398
  #ifdef __cplusplus
377
399
  }
378
400
  #endif
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @brief Collection of Chemistry-oriented Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
3
+ *
4
+ * @author Ashot Vardanian
5
+ * @date July 1, 2023
6
+ */
7
+
8
+ #pragma once
9
+ #include <string.h> // `memcpy`
10
+
11
+ #include <simsimd/simsimd.h>
12
+
13
+ #ifdef _MSC_VER
14
+ #include <intrin.h>
15
+ #define popcount64 __popcnt64
16
+ #else
17
+ #define popcount64 __builtin_popcountll
18
+ #endif
19
+
20
+ #ifdef __cplusplus
21
+ extern "C" {
22
+ #endif
23
+
24
+ /**
25
+ * @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
26
+ * forming 21 incomplete bytes for the MACCS fingerprints used in
27
+ * computation chemistry.
28
+ */
29
+ inline static simsimd_f32_t simsimd_tanimoto_maccs_naive(uint8_t const* a_chars, uint8_t const* b_chars) {
30
+ unsigned long a[3] = {0};
31
+ unsigned long b[3] = {0};
32
+ memcpy(&a[0], a_chars, 21);
33
+ memcpy(&b[0], b_chars, 21);
34
+ float and_count = popcount64(a[0] & b[0]) + popcount64(a[1] & b[1]) + popcount64(a[2] & b[2]);
35
+ float or_count = popcount64(a[0] | b[0]) + popcount64(a[1] | b[1]) + popcount64(a[2] | b[2]);
36
+ return 1 - and_count / or_count;
37
+ }
38
+
39
+ /**
40
+ * @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
41
+ * forming 21 incomplete bytes for the MACCS fingerprints used in
42
+ * computation chemistry, accelerated for Arm NEON ISA.
43
+ */
44
+ inline static simsimd_f32_t simsimd_tanimoto_maccs_neon(uint8_t const* a_chars, uint8_t const* b_chars) {
45
+ #if defined(__ARM_NEON)
46
+ unsigned char a[32] = {0};
47
+ unsigned char b[32] = {0};
48
+ memcpy(&a[0], a_chars, 21);
49
+ memcpy(&b[0], b_chars, 21);
50
+ uint8x16_t a_first = vld1q_u8(&a[0]);
51
+ uint8x16_t a_second = vld1q_u8(&a[16]);
52
+ uint8x16_t b_first = vld1q_u8(&b[0]);
53
+ uint8x16_t b_second = vld1q_u8(&b[16]);
54
+ uint8x16_t a_and_b_first = vandq_u8(a_first, b_first);
55
+ uint8x16_t a_and_b_second = vandq_u8(a_second, b_second);
56
+ uint8x16_t a_or_b_first = vorrq_u8(a_first, b_first);
57
+ uint8x16_t a_or_b_second = vorrq_u8(a_second, b_second);
58
+ float and_populations = vaddvq_u8(vaddq_u8(vcntq_u8(a_and_b_first), vcntq_u8(a_and_b_second)));
59
+ float or_populations = vaddvq_u8(vaddq_u8(vcntq_u8(a_or_b_first), vcntq_u8(a_or_b_second)));
60
+ return 1 - and_populations / or_populations;
61
+ #else
62
+ (void)a_chars, (void)b_chars;
63
+ return 0;
64
+ #endif
65
+ }
66
+
67
+ /**
68
+ * @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
69
+ * forming 21 incomplete bytes for the MACCS fingerprints used in
70
+ * computation chemistry, accelerated for Arm NEON ISA.
71
+ */
72
+ inline static simsimd_f32_t simsimd_tanimoto_maccs_sve(uint8_t const* a_chars, uint8_t const* b_chars) {
73
+ #if defined(__ARM_FEATURE_SVE)
74
+ svbool_t pg_vec = svwhilelt_b8(0ul, 21ul);
75
+ svuint8_t a_vec = svld1_u8(pg_vec, (uint8_t const*)a_chars);
76
+ svuint8_t b_vec = svld1_u8(pg_vec, (uint8_t const*)b_chars);
77
+ svuint8_t a_and_b_vec = svand_u8_m(pg_vec, a_vec, b_vec);
78
+ svuint8_t a_or_b_vec = svorr_u8_m(pg_vec, a_vec, b_vec);
79
+ svuint8_t and_populations_vec = svcnt_u8_x(pg_vec, a_and_b_vec);
80
+ svuint8_t or_populations_vec = svcnt_u8_x(pg_vec, a_or_b_vec);
81
+
82
+ // We are gonna need at most two cycles like this, regardless of the architecture,
83
+ // assuming the SVE registers are at least 128 bits (16 bytes), and we onlu need 21 bytes.
84
+ pg_vec = svwhilelt_b8(svcntb(), 21ul);
85
+ a_vec = svld1_u8(pg_vec, (uint8_t const*)a_chars + svcntb());
86
+ b_vec = svld1_u8(pg_vec, (uint8_t const*)b_chars + svcntb());
87
+ a_and_b_vec = svand_u8_m(pg_vec, a_vec, b_vec);
88
+ a_or_b_vec = svorr_u8_m(pg_vec, a_vec, b_vec);
89
+ and_populations_vec = svadd_u8_m(pg_vec, svcnt_u8_x(pg_vec, a_and_b_vec), and_populations_vec);
90
+ or_populations_vec = svadd_u8_m(pg_vec, svcnt_u8_x(pg_vec, a_or_b_vec), or_populations_vec);
91
+
92
+ float and_populations = svaddv_u8(svptrue_b8(), and_populations_vec);
93
+ float or_populations = svaddv_u8(svptrue_b8(), or_populations_vec);
94
+ return 1 - and_populations / or_populations;
95
+ #else
96
+ (void)a_chars, (void)b_chars;
97
+ return 0;
98
+ #endif
99
+ }
100
+
101
+ /**
102
+ * @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
103
+ * forming 21 incomplete bytes for the MACCS fingerprints used in
104
+ * computation chemistry, accelerated with AVX-512 population count
105
+ * instructions.
106
+ */
107
+ inline static simsimd_f32_t simsimd_tanimoto_maccs_avx512(uint8_t const* a, uint8_t const* b) {
108
+ #if defined(__AVX512VPOPCNTDQ__)
109
+ __m256i a_vec = _mm256_maskz_loadu_epi8(0b11111111111111111111100000000000, a);
110
+ __m256i b_vec = _mm256_maskz_loadu_epi8(0b11111111111111111111100000000000, b);
111
+ __m256i and_vec _mm256_and_si256(a_vec, b_vec);
112
+ __m256i or_vec _mm256_or_si256(a_vec, b_vec);
113
+ __m256i and_counts = _mm256_popcnt_epi8(and_vec);
114
+ __m256i or_counts = _mm256_popcnt_epi8(or_vec);
115
+ return 1 - float(_mm256_reduce_add_epi8(and_counts)) / _mm256_reduce_add_epi8(or_counts);
116
+ #else
117
+ (void)a, (void)b;
118
+ return 0;
119
+ #endif
120
+ }
121
+
122
+ #undef popcount64
123
+
124
+ #ifdef __cplusplus
125
+ }
126
+ #endif
@@ -0,0 +1,8 @@
1
+ {
2
+ "devDependencies": {
3
+ "@semantic-release/exec": "^6.0.3",
4
+ "@semantic-release/git": "^10.0.1",
5
+ "conventional-changelog-eslint": "^4.0.0",
6
+ "semantic-release": "^21.0.7"
7
+ }
8
+ }
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel", "cmake>=3.22", "pybind11"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.pytest.ini_options]
6
+ minversion = "6.0"
7
+ addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
8
+ xfail_strict = true
9
+ filterwarnings = ["error"]
10
+
11
+ [tool.cibuildwheel]
12
+ test-requires = ["pytest"]
13
+ test-command = "pytest {project}/python/test.py -x"
14
+ build-verbosity = 0
15
+ skip = ["*musllinux*", "*i686*", "pp*"]
16
+
17
+ [tool.cibuildwheel.linux]
18
+ before-build = [
19
+ "rm -rf {project}/build",
20
+ ]
21
+ manylinux-x86_64-image = "manylinux_2_28"
22
+ manylinux-aarch64-image = "manylinux_2_28"
23
+
24
+ archs = ["x86_64", "aarch64"]
25
+ before-all = [
26
+ "yum install -y glibc-devel wget python3-devel"
27
+ ]
28
+ repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
29
+
30
+ [tool.cibuildwheel.macos]
31
+ before-build = [
32
+ "rm -rf {project}/build",
33
+ ]
34
+ archs = ["x86_64", "universal2", "arm64"]
35
+ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
36
+
37
+ [tool.cibuildwheel.windows]
38
+ before-build = [
39
+ "rd /s /q {project}\\build || echo Done",
40
+ ]
41
+ archs = ["x86", "AMD64"]
42
+ skip = ["*win32*", "pp*"]
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @file python.c
3
+ * @author Ashot Vardanian
4
+ * @date 2023-01-30
5
+ * @copyright Copyright (c) 2023
6
+ *
7
+ * @brief Pure CPython bindings for simsimd.
8
+ */
9
+
10
+ #include "simsimd/simsimd.h"
11
+ #include "simsimd/simsimd_chem.h"
12
+
13
+ #define PY_SSIZE_T_CLEAN
14
+ #include <Python.h>
15
+
16
+ static void pseudo_destroy(PyObject* obj) { (void)obj; }
17
+
18
+ PyObject* distance(void* func) { return PyCapsule_New(func, NULL, pseudo_destroy); }
19
+
20
+ static PyObject* to_int(PyObject* self, PyObject* args) {
21
+ PyObject* capsule;
22
+ if (!PyArg_ParseTuple(args, "O", &capsule)) {
23
+ return NULL;
24
+ }
25
+
26
+ if (!PyCapsule_IsValid(capsule, NULL)) {
27
+ PyErr_SetString(PyExc_ValueError, "Object is not a valid capsule");
28
+ return NULL;
29
+ }
30
+
31
+ void* pointer = PyCapsule_GetPointer(capsule, NULL);
32
+
33
+ return PyLong_FromVoidPtr(pointer);
34
+ }
35
+
36
+ static PyMethodDef simsimd_methods[] = {
37
+ {"to_int", (PyCFunction)to_int, METH_VARARGS, "Converts CPython capsule to `int`"},
38
+ {NULL, NULL, 0, NULL} /* Sentinel */
39
+ };
40
+
41
+ static PyModuleDef simsimd_module = {
42
+ PyModuleDef_HEAD_INIT,
43
+ .m_name = "SimSIMD",
44
+ .m_doc = "SIMD-accelerated similarity measures for x86 and Arm: AVX2, AVX512, NEON, SVE",
45
+ .m_size = -1,
46
+ .m_methods = simsimd_methods,
47
+ };
48
+
49
+ PyMODINIT_FUNC PyInit_simsimd(void) {
50
+ PyObject* m = PyModule_Create(&simsimd_module);
51
+ if (m == NULL)
52
+ return NULL;
53
+
54
+ PyModule_AddObject(m, "dot_f32_sve", distance(&simsimd_dot_f32_sve));
55
+ PyModule_AddObject(m, "dot_f32x4_neon", distance(&simsimd_dot_f32x4_neon));
56
+
57
+ PyModule_AddObject(m, "cos_f32_sve", distance(&simsimd_cos_f32_sve));
58
+ PyModule_AddObject(m, "cos_f16_sve", distance(&simsimd_cos_f16_sve));
59
+ PyModule_AddObject(m, "cos_f16x4_neon", distance(&simsimd_cos_f16x4_neon));
60
+ PyModule_AddObject(m, "cos_i8x16_neon", distance(&simsimd_cos_i8x16_neon));
61
+ PyModule_AddObject(m, "cos_f32x4_neon", distance(&simsimd_cos_f32x4_neon));
62
+ PyModule_AddObject(m, "cos_f16x16_avx512", distance(&simsimd_cos_f16x16_avx512));
63
+ PyModule_AddObject(m, "cos_f32x4_avx2", distance(&simsimd_cos_f32x4_avx2));
64
+
65
+ PyModule_AddObject(m, "l2sq_f32_sve", distance(&simsimd_l2sq_f32_sve));
66
+ PyModule_AddObject(m, "l2sq_f16_sve", distance(&simsimd_l2sq_f16_sve));
67
+
68
+ PyModule_AddObject(m, "hamming_b1x8_sve", distance(&simsimd_hamming_b1x8_sve));
69
+ PyModule_AddObject(m, "hamming_b1x128_sve", distance(&simsimd_hamming_b1x128_sve));
70
+ PyModule_AddObject(m, "hamming_b1x128_avx512", distance(&simsimd_hamming_b1x128_avx512));
71
+
72
+ PyModule_AddObject(m, "tanimoto_b1x8_naive", distance(&simsimd_tanimoto_b1x8_naive));
73
+
74
+ // Specific vector sizes for chemistry
75
+ PyModule_AddObject(m, "tanimoto_maccs_naive", distance(&simsimd_tanimoto_maccs_naive));
76
+ PyModule_AddObject(m, "tanimoto_maccs_neon", distance(&simsimd_tanimoto_maccs_neon));
77
+ PyModule_AddObject(m, "tanimoto_maccs_sve", distance(&simsimd_tanimoto_maccs_sve));
78
+ PyModule_AddObject(m, "tanimoto_maccs_avx512", distance(&simsimd_tanimoto_maccs_avx512));
79
+
80
+ return m;
81
+ }
@@ -0,0 +1 @@
1
+ pip install --upgrade --force-reinstall . && pytest --capture=no python/tests/