usearch 2.10.3 → 2.10.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 (112) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +101 -0
  3. package/c/CMakeLists.txt +27 -0
  4. package/c/README.md +34 -0
  5. package/c/lib.cpp +362 -0
  6. package/c/test.c +382 -0
  7. package/c/usearch.h +356 -0
  8. package/cmake/clipp.cmake +7 -0
  9. package/cmake/config.cmake.in +15 -0
  10. package/cmake/emscripten.cmake +8 -0
  11. package/cmake/jemalloc.cmake +62 -0
  12. package/cmake/pkg-config.pc.in +4 -0
  13. package/cmake/usearchConfigVersion.cmake.in +20 -0
  14. package/cpp/CMakeLists.txt +14 -0
  15. package/cpp/README.md +161 -0
  16. package/cpp/bench.cpp +616 -0
  17. package/cpp/test.cpp +324 -0
  18. package/fp16/CMakeLists.txt +176 -0
  19. package/fp16/LICENSE +11 -0
  20. package/fp16/README.md +20 -0
  21. package/fp16/bench/alt-element.cc +89 -0
  22. package/fp16/bench/from-alt-array.cc +175 -0
  23. package/fp16/bench/from-ieee-array.cc +399 -0
  24. package/fp16/bench/ieee-element.cc +272 -0
  25. package/fp16/bench/to-alt-array.cc +91 -0
  26. package/fp16/bench/to-ieee-array.cc +303 -0
  27. package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
  28. package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
  29. package/fp16/cmake/DownloadPSimd.cmake +15 -0
  30. package/fp16/configure.py +65 -0
  31. package/fp16/confu.yaml +8 -0
  32. package/fp16/include/fp16/__init__.py +0 -0
  33. package/fp16/include/fp16/avx.py +50 -0
  34. package/fp16/include/fp16/avx2.py +53 -0
  35. package/fp16/include/fp16/bitcasts.h +92 -0
  36. package/fp16/include/fp16/fp16.h +451 -0
  37. package/fp16/include/fp16/psimd.h +131 -0
  38. package/fp16/include/fp16.h +11 -0
  39. package/fp16/jni/Android.mk +6 -0
  40. package/fp16/jni/Application.mk +4 -0
  41. package/fp16/test/alt-from-fp32-value.cc +498 -0
  42. package/fp16/test/alt-to-fp32-bits.cc +263 -0
  43. package/fp16/test/alt-to-fp32-psimd.cc +145 -0
  44. package/fp16/test/alt-to-fp32-value.cc +348 -0
  45. package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
  46. package/fp16/test/bitcasts.cc +56 -0
  47. package/fp16/test/ieee-from-fp32-value.cc +523 -0
  48. package/fp16/test/ieee-to-fp32-bits.cc +333 -0
  49. package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
  50. package/fp16/test/ieee-to-fp32-value.cc +420 -0
  51. package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
  52. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
  53. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
  54. package/fp16/test/peachpy/stubs.py +38 -0
  55. package/fp16/test/tables.cc +530 -0
  56. package/fp16/test/tables.h +13 -0
  57. package/fp16/third-party/THHalf.h +92 -0
  58. package/fp16/third-party/eigen-half.h +170 -0
  59. package/fp16/third-party/float16-compressor.h +80 -0
  60. package/fp16/third-party/half.hpp +2916 -0
  61. package/fp16/third-party/npy-halffloat.h +328 -0
  62. package/include/usearch/index.hpp +3838 -0
  63. package/include/usearch/index_dense.hpp +2004 -0
  64. package/include/usearch/index_plugins.hpp +2255 -0
  65. package/javascript/dist-package-cjs.json +3 -0
  66. package/javascript/dist-package-esm.json +3 -0
  67. package/javascript/lib.cpp +350 -0
  68. package/javascript/node-gyp-build.d.ts +1 -0
  69. package/javascript/tsconfig-base.json +25 -0
  70. package/javascript/tsconfig-cjs.json +8 -0
  71. package/javascript/tsconfig-esm.json +8 -0
  72. package/javascript/usearch.test.js +84 -0
  73. package/javascript/usearch.ts +640 -0
  74. package/package.json +10 -3
  75. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  76. package/prebuilds/linux-arm64/usearch.node +0 -0
  77. package/prebuilds/linux-x64/usearch.node +0 -0
  78. package/prebuilds/win32-ia32/usearch.node +0 -0
  79. package/prebuilds/win32-x64/usearch.node +0 -0
  80. package/simsimd/.releaserc +87 -0
  81. package/simsimd/CONTRIBUTING.md +114 -0
  82. package/simsimd/Cargo.lock +709 -0
  83. package/simsimd/Cargo.toml +44 -0
  84. package/simsimd/LICENSE +201 -0
  85. package/simsimd/README.md +708 -0
  86. package/simsimd/binding.gyp +36 -0
  87. package/simsimd/build.rs +51 -0
  88. package/simsimd/c/lib.c +110 -0
  89. package/simsimd/include/simsimd/binary.h +258 -0
  90. package/simsimd/include/simsimd/dot.h +1237 -0
  91. package/simsimd/include/simsimd/geospatial.h +37 -0
  92. package/simsimd/include/simsimd/probability.h +563 -0
  93. package/simsimd/include/simsimd/simsimd.h +1129 -0
  94. package/simsimd/include/simsimd/spatial.h +1019 -0
  95. package/simsimd/include/simsimd/types.h +309 -0
  96. package/simsimd/javascript/bench.js +127 -0
  97. package/simsimd/javascript/dist-package-cjs.json +3 -0
  98. package/simsimd/javascript/dist-package-esm.json +3 -0
  99. package/simsimd/javascript/fallback.ts +203 -0
  100. package/simsimd/javascript/lib.c +107 -0
  101. package/simsimd/javascript/node-gyp-build.d.ts +1 -0
  102. package/simsimd/javascript/simsimd.ts +135 -0
  103. package/simsimd/javascript/test.mjs +179 -0
  104. package/simsimd/javascript/tsconfig-base.json +37 -0
  105. package/simsimd/javascript/tsconfig-cjs.json +8 -0
  106. package/simsimd/javascript/tsconfig-esm.json +8 -0
  107. package/simsimd/package-lock.json +1709 -0
  108. package/simsimd/package.json +61 -0
  109. package/simsimd/rust/benches/cosine.rs +27 -0
  110. package/simsimd/rust/benches/native.rs +30 -0
  111. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  112. package/simsimd/rust/lib.rs +778 -0
@@ -0,0 +1,309 @@
1
+ /**
2
+ * @file types.h
3
+ * @brief Shared definitions for the SimSIMD library.
4
+ * @author Ash Vardanian
5
+ * @date October 2, 2023
6
+ *
7
+ * Defines:
8
+ * - Sized aliases for numeric types, like: `simsimd_i32_t` and `simsimd_f64_t`.
9
+ * - Macros for compiler/hardware checks, like: `SIMSIMD_TARGET_NEON`
10
+ */
11
+ #ifndef SIMSIMD_TYPES_H
12
+ #define SIMSIMD_TYPES_H
13
+
14
+ /* Annotation for the public API symbols:
15
+ *
16
+ * - `SIMSIMD_PUBLIC` is used for functions that are part of the public API.
17
+ * - `SIMSIMD_INTERNAL` is used for internal helper functions with unstable APIs.
18
+ * - `SIMSIMD_DYNAMIC` is used for functions that are part of the public API, but are dispatched at runtime.
19
+ */
20
+ #if defined(_WIN32) || defined(__CYGWIN__)
21
+ #define SIMSIMD_DYNAMIC __declspec(dllexport)
22
+ #define SIMSIMD_PUBLIC inline static
23
+ #define SIMSIMD_INTERNAL inline static
24
+ #elif defined(__GNUC__) || defined(__clang__)
25
+ #define SIMSIMD_DYNAMIC __attribute__((visibility("default")))
26
+ #define SIMSIMD_PUBLIC __attribute__((unused)) inline static
27
+ #define SIMSIMD_INTERNAL __attribute__((always_inline)) inline static
28
+ #else
29
+ #define SIMSIMD_DYNAMIC inline static
30
+ #define SIMSIMD_PUBLIC inline static
31
+ #define SIMSIMD_INTERNAL inline static
32
+ #endif
33
+
34
+ // Compiling for Arm: SIMSIMD_TARGET_ARM
35
+ #if !defined(SIMSIMD_TARGET_ARM)
36
+ #if defined(__aarch64__) || defined(_M_ARM64)
37
+ #define SIMSIMD_TARGET_ARM 1
38
+ #else
39
+ #define SIMSIMD_TARGET_ARM 0
40
+ #endif // defined(__aarch64__) || defined(_M_ARM64)
41
+ #endif // !defined(SIMSIMD_TARGET_ARM)
42
+
43
+ // Compiling for x86: SIMSIMD_TARGET_X86
44
+ #if !defined(SIMSIMD_TARGET_X86)
45
+ #if defined(__x86_64__) || defined(_M_X64)
46
+ #define SIMSIMD_TARGET_X86 1
47
+ #else
48
+ #define SIMSIMD_TARGET_X86 0
49
+ #endif // defined(__x86_64__) || defined(_M_X64)
50
+ #endif // !defined(SIMSIMD_TARGET_X86)
51
+
52
+ // Compiling for Arm: SIMSIMD_TARGET_NEON
53
+ #if !defined(SIMSIMD_TARGET_NEON) || (SIMSIMD_TARGET_NEON && !SIMSIMD_TARGET_ARM)
54
+ #if defined(__ARM_NEON)
55
+ #define SIMSIMD_TARGET_NEON SIMSIMD_TARGET_ARM
56
+ #else
57
+ #undef SIMSIMD_TARGET_NEON
58
+ #define SIMSIMD_TARGET_NEON 0
59
+ #endif // defined(__ARM_NEON)
60
+ #endif // !defined(SIMSIMD_TARGET_NEON)
61
+
62
+ // Compiling for Arm: SIMSIMD_TARGET_SVE
63
+ #if !defined(SIMSIMD_TARGET_SVE) || (SIMSIMD_TARGET_SVE && !SIMSIMD_TARGET_ARM)
64
+ #if defined(__ARM_FEATURE_SVE)
65
+ #define SIMSIMD_TARGET_SVE SIMSIMD_TARGET_ARM
66
+ #else
67
+ #undef SIMSIMD_TARGET_SVE
68
+ #define SIMSIMD_TARGET_SVE 0
69
+ #endif // defined(__ARM_FEATURE_SVE)
70
+ #endif // !defined(SIMSIMD_TARGET_SVE)
71
+
72
+ // Compiling for x86: SIMSIMD_TARGET_HASWELL
73
+ //
74
+ // Starting with Ivy Bridge, Intel supports the `F16C` extensions for fast half-precision
75
+ // to single-precision floating-point conversions. On AMD those instructions
76
+ // are supported on all CPUs starting with Jaguar 2009.
77
+ // Starting with Sandy Bridge, Intel adds basic AVX support in their CPUs and in 2013
78
+ // extends it with AVX2 in the Haswell generation. Moreover, Haswell adds FMA support.
79
+ #if !defined(SIMSIMD_TARGET_HASWELL) || (SIMSIMD_TARGET_HASWELL && !SIMSIMD_TARGET_X86)
80
+ #if defined(__AVX2__) && defined(__FMA__) && defined(__F16C__)
81
+ #define SIMSIMD_TARGET_HASWELL 1
82
+ #else
83
+ #undef SIMSIMD_TARGET_HASWELL
84
+ #define SIMSIMD_TARGET_HASWELL 0
85
+ #endif // defined(__AVX2__)
86
+ #endif // !defined(SIMSIMD_TARGET_HASWELL)
87
+
88
+ // Compiling for x86: SIMSIMD_TARGET_SKYLAKE, SIMSIMD_TARGET_ICE, SIMSIMD_TARGET_SAPPHIRE
89
+ //
90
+ // It's important to provide fine-grained controls over AVX512 families, as they are very fragmented:
91
+ // - Intel Skylake servers: F, CD, VL, DQ, BW
92
+ // - Intel Cascade Lake workstations: F, CD, VL, DQ, BW, VNNI
93
+ // > In other words, it extends Skylake with VNNI support
94
+ // - Intel Sunny Cove (Ice Lake) servers:
95
+ // F, CD, VL, DQ, BW, VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ
96
+ // - AMD Zen4 (Genoa):
97
+ // F, CD, VL, DQ, BW, VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ, BF16
98
+ // > In other words, it extends Sunny Cove with BF16 support
99
+ // - Golden Cove (Sapphire Rapids): extends Zen4 and Sunny Cove with FP16 support
100
+ //
101
+ // Intel Palm Cove was an irrelevant intermediate release extending Skylake with IFMA and VBMI.
102
+ // Intel Willow Cove was an irrelevant intermediate release extending Sunny Cove with VP2INTERSECT,
103
+ // that aren't supported by any other CPU built to date... and those are only available in Tiger Lake laptops.
104
+ // Intel Cooper Lake was the only intermediary platform, that supported BF16, but not FP16.
105
+ // It's mostly used in 4-socket and 8-socket high-memory configurations.
106
+ //
107
+ // In practical terms, it makes sense to differentiate only 3 AVX512 generations:
108
+ // 1. Skylake (pre 2019): supports single-precision dot-products.
109
+ // 2. Ice Lake (2019-2021): advanced integer algorithms.
110
+ // 3. Sapphire Rapids (2023+): advanced mixed-precision float processing.
111
+ //
112
+ // To list all available macros for x86, take a recent compiler, like GCC 12 and run:
113
+ // gcc-12 -march=sapphirerapids -dM -E - < /dev/null | egrep "SSE|AVX" | sort
114
+ // On Arm machines you may want to check for other flags:
115
+ // gcc-12 -march=native -dM -E - < /dev/null | egrep "NEON|SVE|FP16|FMA" | sort
116
+ #if !defined(SIMSIMD_TARGET_SKYLAKE) || (SIMSIMD_TARGET_SKYLAKE && !SIMSIMD_TARGET_X86)
117
+ #if defined(__AVX512F__) && defined(__AVX512CD__) && defined(__AVX512VL__) && defined(__AVX512DQ__) && \
118
+ defined(__AVX512BW__)
119
+ #define SIMSIMD_TARGET_SKYLAKE 1
120
+ #else
121
+ #undef SIMSIMD_TARGET_SKYLAKE
122
+ #define SIMSIMD_TARGET_SKYLAKE 0
123
+ #endif
124
+ #endif // !defined(SIMSIMD_TARGET_SKYLAKE)
125
+ #if !defined(SIMSIMD_TARGET_ICE) || (SIMSIMD_TARGET_ICE && !SIMSIMD_TARGET_X86)
126
+ #if defined(__AVX512VNNI__) && defined(__AVX512IFMA__) && defined(__AVX512BITALG__) && defined(__AVX512VBMI2__) && \
127
+ defined(__AVX512VPOPCNTDQ__)
128
+ #define SIMSIMD_TARGET_ICE 1
129
+ #else
130
+ #undef SIMSIMD_TARGET_ICE
131
+ #define SIMSIMD_TARGET_ICE 0
132
+ #endif
133
+ #endif // !defined(SIMSIMD_TARGET_ICE)
134
+ #if !defined(SIMSIMD_TARGET_SAPPHIRE) || (SIMSIMD_TARGET_SAPPHIRE && !SIMSIMD_TARGET_X86)
135
+ #if defined(__AVX512FP16__)
136
+ #define SIMSIMD_TARGET_SAPPHIRE 1
137
+ #else
138
+ #undef SIMSIMD_TARGET_SAPPHIRE
139
+ #define SIMSIMD_TARGET_SAPPHIRE 0
140
+ #endif
141
+ #endif // !defined(SIMSIMD_TARGET_SAPPHIRE)
142
+
143
+ #ifdef _MSC_VER
144
+ #include <intrin.h>
145
+ #else
146
+
147
+ #if SIMSIMD_TARGET_NEON
148
+ #include <arm_neon.h>
149
+ #endif
150
+
151
+ #if SIMSIMD_TARGET_SVE
152
+ #include <arm_sve.h>
153
+ #endif
154
+
155
+ #if SIMSIMD_TARGET_HASWELL || SIMSIMD_TARGET_SKYLAKE
156
+ #include <immintrin.h>
157
+ #endif
158
+
159
+ #endif
160
+
161
+ #ifndef SIMSIMD_RSQRT
162
+ #include <math.h>
163
+ #define SIMSIMD_RSQRT(x) (1 / sqrtf(x))
164
+ #endif
165
+
166
+ #ifndef SIMSIMD_LOG
167
+ #include <math.h>
168
+ #define SIMSIMD_LOG(x) (logf(x))
169
+ #endif
170
+
171
+ #ifndef SIMSIMD_F32_DIVISION_EPSILON
172
+ #define SIMSIMD_F32_DIVISION_EPSILON (1e-7)
173
+ #endif
174
+
175
+ #ifndef SIMSIMD_F16_DIVISION_EPSILON
176
+ #define SIMSIMD_F16_DIVISION_EPSILON (1e-3)
177
+ #endif
178
+
179
+ #ifdef __cplusplus
180
+ extern "C" {
181
+ #endif
182
+
183
+ typedef int simsimd_i32_t;
184
+ typedef float simsimd_f32_t;
185
+ typedef double simsimd_f64_t;
186
+ typedef signed char simsimd_i8_t;
187
+ typedef unsigned char simsimd_b8_t;
188
+ typedef unsigned long long simsimd_u64_t;
189
+
190
+ typedef simsimd_u64_t simsimd_size_t;
191
+ typedef simsimd_f64_t simsimd_distance_t;
192
+
193
+ #if !defined(SIMSIMD_NATIVE_F16) || SIMSIMD_NATIVE_F16
194
+ /**
195
+ * @brief Half-precision floating-point type.
196
+ *
197
+ * - GCC or Clang on 64-bit ARM: `__fp16`, may require `-mfp16-format` option.
198
+ * - GCC or Clang on 64-bit x86: `_Float16`.
199
+ * - Default: `unsigned short`.
200
+ */
201
+ #if (defined(__GNUC__) || defined(__clang__)) && (defined(__ARM_ARCH) || defined(__aarch64__)) && \
202
+ (defined(__ARM_FP16_FORMAT_IEEE))
203
+ #if !defined(SIMSIMD_NATIVE_F16)
204
+ #define SIMSIMD_NATIVE_F16 1
205
+ #endif
206
+ typedef __fp16 simsimd_f16_t;
207
+ #elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__)) && \
208
+ (defined(__SSE2__) || defined(__AVX512F__)))
209
+ typedef _Float16 simsimd_f16_t;
210
+ #if !defined(SIMSIMD_NATIVE_F16)
211
+ #define SIMSIMD_NATIVE_F16 1
212
+ #endif
213
+ #else // Unknown compiler or architecture
214
+ #define SIMSIMD_NATIVE_F16 0
215
+ #endif // Unknown compiler or architecture
216
+ #endif // !SIMSIMD_NATIVE_F16
217
+
218
+ #if !SIMSIMD_NATIVE_F16
219
+ typedef unsigned short simsimd_f16_t;
220
+ #endif
221
+
222
+ /**
223
+ * @brief Alias for the half-precision floating-point type on Arm.
224
+ * Clang and GCC bring the `float16_t` symbol when you compile for Aarch64.
225
+ * MSVC lacks it, and it's `vld1_f16`-like intrinsics are in reality macros,
226
+ * that cast to 16-bit integers internally, instead of using floats.
227
+ */
228
+ #if SIMSIMD_TARGET_ARM
229
+ #if defined(_MSC_VER)
230
+ typedef simsimd_f16_t simsimd_f16_for_arm_simd_t;
231
+ #else
232
+ typedef float16_t simsimd_f16_for_arm_simd_t;
233
+ #endif
234
+ #endif
235
+
236
+ #define SIMSIMD_IDENTIFY(x) (x)
237
+
238
+ /**
239
+ * @brief Returns the value of the half-precision floating-point number,
240
+ * potentially decompressed into single-precision.
241
+ */
242
+ #ifndef SIMSIMD_UNCOMPRESS_F16
243
+ #if SIMSIMD_NATIVE_F16
244
+ #define SIMSIMD_UNCOMPRESS_F16(x) SIMSIMD_IDENTIFY(x)
245
+ #else
246
+ #define SIMSIMD_UNCOMPRESS_F16(x) simsimd_uncompress_f16(x)
247
+ #endif
248
+ #endif
249
+
250
+ typedef union {
251
+ unsigned i;
252
+ float f;
253
+ } simsimd_f32i32_t;
254
+
255
+ /**
256
+ * @brief Computes `1/sqrt(x)` using the trick from Quake 3, replacing
257
+ * magic numbers with the ones suggested by Jan Kadlec.
258
+ */
259
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_approximate_inverse_square_root(simsimd_f32_t number) {
260
+ simsimd_f32i32_t conv;
261
+ conv.f = number;
262
+ conv.i = 0x5F1FFFF9 - (conv.i >> 1);
263
+ conv.f *= 0.703952253f * (2.38924456f - number * conv.f * conv.f);
264
+ return conv.f;
265
+ }
266
+
267
+ /**
268
+ * @brief Computes `log(x)` using the Mercator series.
269
+ * The series converges to the natural logarithm for args between -1 and 1.
270
+ * Published in 1668 in "Logarithmotechnia".
271
+ */
272
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_approximate_log(simsimd_f32_t number) {
273
+ simsimd_f32_t x = number - 1;
274
+ simsimd_f32_t x2 = x * x;
275
+ simsimd_f32_t x3 = x * x * x;
276
+ return x - x2 / 2 + x3 / 3;
277
+ }
278
+
279
+ /**
280
+ * @brief For compilers that don't natively support the `_Float16` type,
281
+ * upcasts contents into a more conventional `float`.
282
+ *
283
+ * @warning This function won't handle boundary conditions well.
284
+ *
285
+ * https://stackoverflow.com/a/60047308
286
+ * https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
287
+ * https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
288
+ */
289
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_uncompress_f16(unsigned short x) {
290
+ union float_or_unsigned_int_t {
291
+ float f;
292
+ unsigned int i;
293
+ };
294
+ unsigned int exponent = (x & 0x7C00) >> 10;
295
+ unsigned int mantissa = (x & 0x03FF) << 13;
296
+ union float_or_unsigned_int_t mantissa_union;
297
+ mantissa_union.f = (float)mantissa;
298
+ unsigned int v = (mantissa_union.i) >> 23;
299
+ union float_or_unsigned_int_t result_union;
300
+ result_union.i = (x & 0x8000) << 16 | (exponent != 0) * ((exponent + 112) << 23 | mantissa) |
301
+ ((exponent == 0) & (mantissa != 0)) * ((v - 37) << 23 | ((mantissa << (150 - v)) & 0x007FE000));
302
+ return result_union.f;
303
+ }
304
+
305
+ #ifdef __cplusplus
306
+ } // extern "C"
307
+ #endif
308
+
309
+ #endif
@@ -0,0 +1,127 @@
1
+ const benchmark = require('benchmark');
2
+ const math = require('mathjs');
3
+ const usearch = require('usearch');
4
+ const MetricKind = usearch.MetricKind;
5
+ const simsimd = require("./dist/cjs/simsimd.js");
6
+
7
+ // Assuming the vectors are of the same length
8
+ function cosineDistance(a, b) {
9
+ let dotProduct = 0;
10
+ let magA = 0;
11
+ let magB = 0;
12
+ for (let i = 0; i < a.length; i++) {
13
+ dotProduct += a[i] * b[i];
14
+ magA += a[i] * a[i];
15
+ magB += b[i] * b[i];
16
+ }
17
+ return 1 - (dotProduct / (Math.sqrt(magA) * Math.sqrt(magB)));
18
+ }
19
+
20
+ function cosineDistanceMathJS(a, b) {
21
+ let dotProduct = math.dot(a, b);
22
+ let magA = math.sqrt(math.dot(a, a));
23
+ let magB = math.sqrt(math.dot(b, b));
24
+ return 1 - (dotProduct / (magA * magB));
25
+ }
26
+
27
+ // Generate random data for testing
28
+ const dimensions = 1536; // Adjust dimensions as needed
29
+ const array1 = Array.from({ length: dimensions }, () => Math.random() * 100);
30
+ const array2 = Array.from({ length: dimensions }, () => Math.random() * 100);
31
+ const mathm1 = math.matrix(Array.from({ length: dimensions }, () => Math.random() * 100));
32
+ const mathm2 = math.matrix(Array.from({ length: dimensions }, () => Math.random() * 100));
33
+ const floatArray1 = new Float32Array(array1);
34
+ const floatArray2 = new Float32Array(array2);
35
+ const intArray1 = new Int8Array(array1);
36
+ const intArray2 = new Int8Array(array2);
37
+
38
+ // Generate random batch data for testing
39
+ const batchSize = 1000;
40
+ const matrix1 = Array.from({ length: dimensions * batchSize }, () => Math.random() * 100);
41
+ const matrix2 = Array.from({ length: dimensions * batchSize }, () => Math.random() * 100);
42
+ const floatMatrix1 = new Float32Array(matrix1);
43
+ const floatMatrix2 = new Float32Array(matrix2);
44
+ const intMatrix1 = new Int8Array(matrix1);
45
+ const intMatrix2 = new Int8Array(matrix2);
46
+
47
+ // Create benchmark suite
48
+ const singleSuite = new benchmark.Suite('Single Vector Processing');
49
+ const batchSuite = new benchmark.Suite('Batch Vector Processing');
50
+
51
+ // Single-vector processing benchmarks
52
+ singleSuite
53
+
54
+ // Pure JavaScript
55
+ .add('Array of Numbers', () => {
56
+ cosineDistance(array1, array2);
57
+ })
58
+ .add('TypedArray of Float32', () => {
59
+ cosineDistance(floatArray1, floatArray2);
60
+ })
61
+ .add('TypedArray of Int8', () => {
62
+ cosineDistance(intArray1, intArray2);
63
+ })
64
+
65
+ // Math JS
66
+ .add('Array of Numbers with MathJS', () => {
67
+ cosineDistanceMathJS(array1, array2);
68
+ })
69
+ .add('MathMatrix with MathJS', () => {
70
+ cosineDistanceMathJS(mathm1, mathm2);
71
+ })
72
+
73
+ // SimSIMD
74
+ .add('TypedArray of Float32 with SimSIMD', () => {
75
+ simsimd.cosine(floatArray1, floatArray2);
76
+ })
77
+ .add('TypedArray of Int8 with SimSIMD', () => {
78
+ simsimd.cosine(intArray1, intArray2);
79
+ })
80
+
81
+ .on('cycle', (event) => {
82
+ if (event.target.error) {
83
+ console.error(String(event.target.error));
84
+ } else {
85
+ console.log(String(event.target));
86
+ }
87
+ })
88
+ .on('complete', () => {
89
+ console.log('Fastest Single-Vector Processing is ' + singleSuite.filter('fastest').map('name'));
90
+ })
91
+ .run({
92
+ noCache: true,
93
+ async: false,
94
+ });
95
+
96
+ // Batch-vector processing benchmarks
97
+ batchSuite
98
+ .add('2D Array of Numbers', () => {
99
+ for (let i = 0; i < batchSize; i++) {
100
+ for (let j = 0; j < batchSize; j++) {
101
+ const start = i * dimensions;
102
+ const end = start + dimensions;
103
+ cosineDistance(matrix1.slice(start, end), matrix2.slice(start, end));
104
+ }
105
+ }
106
+ })
107
+ .add('2D TypedArray of Float32 with exact USearch', () => {
108
+ usearch.exactSearch(floatMatrix1, floatMatrix2, dimensions, 1, MetricKind.Cos);
109
+ })
110
+ .add('2D TypedArray of Int8 with exact USearch', () => {
111
+ usearch.exactSearch(intMatrix1, intMatrix2, dimensions, 1, MetricKind.Cos);
112
+ })
113
+ .on('cycle', (event) => {
114
+ if (event.target.error) {
115
+ console.error(String(event.target.error));
116
+ } else {
117
+ console.log(String(event.target));
118
+ }
119
+ })
120
+ .on('complete', () => {
121
+ console.log('Fastest Batch-Vector Processing is ' + batchSuite.filter('fastest').map('name'));
122
+ })
123
+ .run({
124
+ noCache: true,
125
+ async: false,
126
+ });
127
+
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * @brief Computes the inner distance of two vectors (same as dot product).
3
+ * @param {Float64Array|Float32Array} a - The first vector.
4
+ * @param {Float64Array|Float32Array} b - The second vector.
5
+ * @returns {number} The inner distance of vectors a and b.
6
+ */
7
+ export function inner(a: Float64Array | Float32Array, b: Float64Array | Float32Array): number {
8
+ if (a.length !== b.length) {
9
+ throw new Error("Vectors must have the same length");
10
+ }
11
+
12
+ let result = 0;
13
+ for (let i = 0; i < a.length; i++) {
14
+ result += a[i] * b[i];
15
+ }
16
+ return result;
17
+ }
18
+
19
+ /**
20
+ * @brief Computes the inner distance of two vectors (same as inner product).
21
+ * @param {Float64Array|Float32Array} a - The first vector.
22
+ * @param {Float64Array|Float32Array} b - The second vector.
23
+ * @returns {number} The inner distance of vectors a and b.
24
+ */
25
+ export function dot(a: Float64Array | Float32Array, b: Float64Array | Float32Array): number {
26
+ return inner(a, b);
27
+ }
28
+
29
+ /**
30
+ * @brief Computes the squared Euclidean distance between two vectors.
31
+ * @param {Float64Array|Float32Array|Int8Array} a - The first vector.
32
+ * @param {Float64Array|Float32Array|Int8Array} b - The second vector.
33
+ * @returns {number} The squared Euclidean distance between vectors a and b.
34
+ */
35
+ export function sqeuclidean(
36
+ a: Float64Array | Float32Array | Int8Array,
37
+ b: Float64Array | Float32Array | Int8Array
38
+ ): number {
39
+ if (a.length !== b.length) {
40
+ throw new Error("Vectors must have the same length");
41
+ }
42
+
43
+ let result = 0;
44
+ for (let i = 0; i < a.length; i++) {
45
+ result += (a[i] - b[i]) * (a[i] - b[i]);
46
+ }
47
+ return result;
48
+ }
49
+
50
+ /**
51
+ * @brief Computes the cosine distance between two vectors.
52
+ * @param {Float64Array|Float32Array|Int8Array} a - The first vector.
53
+ * @param {Float64Array|Float32Array|Int8Array} b - The second vector.
54
+ * @returns {number} The cosine distance between vectors a and b.
55
+ */
56
+ export function cosine(
57
+ a: Float64Array | Float32Array | Int8Array,
58
+ b: Float64Array | Float32Array | Int8Array
59
+ ): number {
60
+ if (a.length !== b.length) {
61
+ throw new Error("Vectors must have the same length");
62
+ }
63
+
64
+ let dotProduct = 0;
65
+ let magnitudeA = 0;
66
+ let magnitudeB = 0;
67
+
68
+ for (let i = 0; i < a.length; i++) {
69
+ dotProduct += a[i] * b[i];
70
+ magnitudeA += a[i] * a[i];
71
+ magnitudeB += b[i] * b[i];
72
+ }
73
+
74
+ magnitudeA = Math.sqrt(magnitudeA);
75
+ magnitudeB = Math.sqrt(magnitudeB);
76
+
77
+ if (magnitudeA === 0 || magnitudeB === 0) {
78
+ console.warn(
79
+ "Warning: One of the magnitudes is zero. Cosine similarity is undefined."
80
+ );
81
+ return 0;
82
+ }
83
+
84
+ return 1 - dotProduct / (magnitudeA * magnitudeB);
85
+ }
86
+
87
+ /**
88
+ * @brief Computes the bitwise Hamming distance between two vectors.
89
+ * @param {Uint8Array} a - The first vector.
90
+ * @param {Uint8Array} b - The second vector.
91
+ * @returns {number} The Hamming distance between vectors a and b.
92
+ */
93
+ export const hamming = (a: Uint8Array, b: Uint8Array): number => {
94
+ if (a.length !== b.length) {
95
+ throw new Error("Arrays must be of the same length");
96
+ }
97
+
98
+ let distance = 0;
99
+
100
+ for (let i = 0; i < a.length; i++) {
101
+ let xor = a[i] ^ b[i]; // XOR operation to find differing bits
102
+
103
+ // Count the number of set bits (differing bits)
104
+ while (xor > 0) {
105
+ distance += xor & 1;
106
+ xor >>= 1;
107
+ }
108
+ }
109
+
110
+ return distance;
111
+ };
112
+
113
+ /**
114
+ * @brief Computes the bitwise Jaccard similarity coefficient between two vectors.
115
+ * @param {Uint8Array} a - The first vector.
116
+ * @param {Uint8Array} b - The second vector.
117
+ * @returns {number} The Jaccard similarity coefficient between vectors a and b.
118
+ */
119
+ export const jaccard = (a: Uint8Array, b: Uint8Array): number => {
120
+ if (a.length !== b.length) {
121
+ throw new Error("Arrays must be of the same length");
122
+ }
123
+
124
+ let intersection = 0;
125
+ let union = 0;
126
+
127
+ for (let i = 0; i < a.length; i++) {
128
+ let ai = a[i];
129
+ let bi = b[i];
130
+
131
+ // Count the number of set bits in a AND b for intersection
132
+ let and = ai & bi;
133
+ while (and > 0) {
134
+ intersection += and & 1;
135
+ and >>= 1;
136
+ }
137
+
138
+ // Count the number of set bits in a OR b for union
139
+ let or = ai | bi;
140
+ while (or > 0) {
141
+ union += or & 1;
142
+ or >>= 1;
143
+ }
144
+ }
145
+
146
+ if (union === 0) return 0; // Avoid division by zero
147
+
148
+ return 1 - intersection / union;
149
+ };
150
+
151
+ /**
152
+ * @brief Computes the kullbackleibler similarity coefficient between two vectors.
153
+ * @param {Float64Array|Float32Array} a - The first vector.
154
+ * @param {Float64Array|Float32Array} b - The second vector.
155
+ * @returns {number} The Jaccard similarity coefficient between vectors a and b.
156
+ */
157
+ export const kullbackleibler = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
158
+ if (a.length !== b.length) {
159
+ throw new Error("Arrays must be of the same length");
160
+ }
161
+
162
+ let divergence = 0.0;
163
+
164
+ for (let i = 0; i < a.length; i++) {
165
+ if (a[i] > 0) {
166
+ if (b[i] === 0) {
167
+ throw new Error(
168
+ "Division by zero encountered in KL divergence calculation"
169
+ );
170
+ }
171
+ divergence += a[i] * Math.log(a[i] / b[i]);
172
+ }
173
+ }
174
+
175
+ return divergence;
176
+ };
177
+
178
+ /**
179
+ * @brief Computes the jensenshannon similarity coefficient between two vectors.
180
+ * @param {Float64Array|Float32Array} a - The first vector.
181
+ * @param {Float64Array|Float32Array} b - The second vector.
182
+ * @returns {number} The Jaccard similarity coefficient between vectors a and b.
183
+ */
184
+ export const jensenshannon = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
185
+ if (a.length !== b.length) {
186
+ throw new Error("Arrays must be of the same length");
187
+ }
188
+
189
+ const m = a.map((value, index) => (value + b[index]) / 2);
190
+
191
+ const divergence = 0.5 * kullbackleibler(a, m) + 0.5 * kullbackleibler(b, m);
192
+ return Math.sqrt(divergence);
193
+ };
194
+
195
+ export default {
196
+ sqeuclidean,
197
+ cosine,
198
+ inner,
199
+ hamming,
200
+ jaccard,
201
+ kullbackleibler,
202
+ jensenshannon,
203
+ };