usearch 2.15.1 → 2.15.3

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.
@@ -35,7 +35,7 @@
35
35
  #define SIMSIMD_PUBLIC __attribute__((unused)) inline static
36
36
  #define SIMSIMD_INTERNAL __attribute__((always_inline)) inline static
37
37
  #else
38
- #define SIMSIMD_DYNAMIC inline static
38
+ #define SIMSIMD_DYNAMIC
39
39
  #define SIMSIMD_PUBLIC inline static
40
40
  #define SIMSIMD_INTERNAL inline static
41
41
  #endif
@@ -98,6 +98,16 @@
98
98
  #define SIMSIMD_TARGET_SVE_BF16 SIMSIMD_TARGET_SVE
99
99
  #endif // !defined(SIMSIMD_TARGET_SVE_BF16)
100
100
 
101
+ // Compiling for Arm: SIMSIMD_TARGET_SVE2
102
+ #if !defined(SIMSIMD_TARGET_SVE2) || (SIMSIMD_TARGET_SVE2 && !SIMSIMD_TARGET_ARM)
103
+ #if defined(__ARM_FEATURE_SVE)
104
+ #define SIMSIMD_TARGET_SVE2 SIMSIMD_TARGET_ARM
105
+ #else
106
+ #undef SIMSIMD_TARGET_SVE2
107
+ #define SIMSIMD_TARGET_SVE2 0
108
+ #endif // defined(__ARM_FEATURE_SVE)
109
+ #endif // !defined(SIMSIMD_TARGET_SVE2)
110
+
101
111
  // Compiling for x86: SIMSIMD_TARGET_HASWELL
102
112
  //
103
113
  // Starting with Ivy Bridge, Intel supports the `F16C` extensions for fast half-precision
@@ -114,7 +124,7 @@
114
124
  #endif // defined(__AVX2__)
115
125
  #endif // !defined(SIMSIMD_TARGET_HASWELL)
116
126
 
117
- // Compiling for x86: SIMSIMD_TARGET_SKYLAKE, SIMSIMD_TARGET_ICE, SIMSIMD_TARGET_SAPPHIRE
127
+ // Compiling for x86: SIMSIMD_TARGET_SKYLAKE, SIMSIMD_TARGET_ICE, SIMSIMD_TARGET_GENOA, SIMSIMD_TARGET_SAPPHIRE
118
128
  //
119
129
  // To list all available macros for x86, take a recent compiler, like GCC 12 and run:
120
130
  // gcc-12 -march=sapphirerapids -dM -E - < /dev/null | egrep "SSE|AVX" | sort
@@ -163,31 +173,37 @@
163
173
  #include <arm_neon.h>
164
174
  #endif
165
175
 
166
- #if SIMSIMD_TARGET_SVE
176
+ #if SIMSIMD_TARGET_SVE || SIMSIMD_TARGET_SVE2
167
177
  #include <arm_sve.h>
168
178
  #endif
169
179
 
170
- #if SIMSIMD_TARGET_HASWELL || SIMSIMD_TARGET_SKYLAKE
180
+ #if SIMSIMD_TARGET_HASWELL || SIMSIMD_TARGET_SKYLAKE || SIMSIMD_TARGET_ICE || SIMSIMD_TARGET_GENOA || \
181
+ SIMSIMD_TARGET_SAPPHIRE
171
182
  #include <immintrin.h>
172
183
  #endif
173
184
 
174
185
  #endif
175
186
 
176
- #ifndef SIMSIMD_RSQRT
187
+ #if !defined(SIMSIMD_SQRT)
177
188
  #include <math.h>
178
- #define SIMSIMD_RSQRT(x) (1 / sqrtf(x))
189
+ #define SIMSIMD_SQRT(x) (sqrt(x))
179
190
  #endif
180
191
 
181
- #ifndef SIMSIMD_LOG
192
+ #if !defined(SIMSIMD_RSQRT)
182
193
  #include <math.h>
183
- #define SIMSIMD_LOG(x) (logf(x))
194
+ #define SIMSIMD_RSQRT(x) (1 / SIMSIMD_SQRT(x))
184
195
  #endif
185
196
 
186
- #ifndef SIMSIMD_F32_DIVISION_EPSILON
197
+ #if !defined(SIMSIMD_LOG)
198
+ #include <math.h>
199
+ #define SIMSIMD_LOG(x) (log(x))
200
+ #endif
201
+
202
+ #if !defined(SIMSIMD_F32_DIVISION_EPSILON)
187
203
  #define SIMSIMD_F32_DIVISION_EPSILON (1e-7)
188
204
  #endif
189
205
 
190
- #ifndef SIMSIMD_F16_DIVISION_EPSILON
206
+ #if !defined(SIMSIMD_F16_DIVISION_EPSILON)
191
207
  #define SIMSIMD_F16_DIVISION_EPSILON (1e-3)
192
208
  #endif
193
209
 
@@ -195,14 +211,20 @@
195
211
  extern "C" {
196
212
  #endif
197
213
 
198
- typedef int simsimd_i32_t;
199
- typedef float simsimd_f32_t;
200
- typedef double simsimd_f64_t;
201
- typedef signed char simsimd_i8_t;
202
214
  typedef unsigned char simsimd_b8_t;
203
- typedef long long simsimd_i64_t;
215
+
216
+ typedef signed char simsimd_i8_t;
217
+ typedef unsigned char simsimd_u8_t;
218
+ typedef signed short simsimd_i16_t;
219
+ typedef unsigned short simsimd_u16_t;
220
+ typedef signed int simsimd_i32_t;
221
+ typedef unsigned int simsimd_u32_t;
222
+ typedef signed long long simsimd_i64_t;
204
223
  typedef unsigned long long simsimd_u64_t;
205
224
 
225
+ typedef float simsimd_f32_t;
226
+ typedef double simsimd_f64_t;
227
+
206
228
  typedef simsimd_u64_t simsimd_size_t;
207
229
  typedef simsimd_f64_t simsimd_distance_t;
208
230
 
@@ -215,17 +237,19 @@ typedef simsimd_f64_t simsimd_distance_t;
215
237
  #if !defined(SIMSIMD_NATIVE_F16) || SIMSIMD_NATIVE_F16
216
238
  #if (defined(__GNUC__) || defined(__clang__)) && (defined(__ARM_ARCH) || defined(__aarch64__)) && \
217
239
  (defined(__ARM_FP16_FORMAT_IEEE))
218
- #if !defined(SIMSIMD_NATIVE_F16)
240
+ #undef SIMSIMD_NATIVE_F16
219
241
  #define SIMSIMD_NATIVE_F16 1
220
- #endif
221
242
  typedef __fp16 simsimd_f16_t;
222
243
  #elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__)) && \
223
- (defined(__SSE2__) || defined(__AVX512F__)))
244
+ (defined(__AVX512FP16__)))
224
245
  typedef _Float16 simsimd_f16_t;
225
- #if !defined(SIMSIMD_NATIVE_F16)
246
+ #undef SIMSIMD_NATIVE_F16
226
247
  #define SIMSIMD_NATIVE_F16 1
248
+ #else // Unknown compiler or architecture
249
+ #if defined(__GNUC__) || defined(__clang__) // Some compilers don't support warning pragmas
250
+ #warning "Unknown compiler or architecture for float16."
227
251
  #endif
228
- #else // Unknown compiler or architecture
252
+ #undef SIMSIMD_NATIVE_F16
229
253
  #define SIMSIMD_NATIVE_F16 0
230
254
  #endif // Unknown compiler or architecture
231
255
  #endif // !SIMSIMD_NATIVE_F16
@@ -242,23 +266,38 @@ typedef unsigned short simsimd_f16_t;
242
266
  * - GCC or Clang on 64-bit x86: `_BFloat16`.
243
267
  * - Default: `unsigned short`.
244
268
  *
269
+ * The compilers have added `__bf16` support in compliance with the x86-64 psABI spec.
270
+ * The motivation for this new special type is summed up as:
271
+ *
272
+ * Currently `__bfloat16` is a typedef of short, which creates a problem where the
273
+ * compiler does not raise any alarms if it is used to add, subtract, multiply or
274
+ * divide, but the result of the calculation is actually meaningless.
275
+ * To solve this problem, a real scalar type `__Bfloat16` needs to be introduced.
276
+ * It is mainly used for intrinsics, not available for C standard operators.
277
+ * `__Bfloat16` will also be used for movement like passing parameter, load and store,
278
+ * vector initialization, vector shuffle, and etc. It creates a need for a
279
+ * corresponding psABI.
280
+ *
245
281
  * @warning Apple Clang has hard time with bf16.
246
282
  * https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
247
283
  * https://forums.developer.apple.com/forums/thread/726201
284
+ * https://www.phoronix.com/news/GCC-LLVM-bf16-BFloat16-Type
248
285
  */
249
286
  #if (defined(__GNUC__) || defined(__clang__)) && (defined(__ARM_ARCH) || defined(__aarch64__)) && \
250
287
  (defined(__ARM_BF16_FORMAT_ALTERNATIVE))
251
- #if !defined(SIMSIMD_NATIVE_BF16)
288
+ #undef SIMSIMD_NATIVE_BF16
252
289
  #define SIMSIMD_NATIVE_BF16 1
253
- #endif
254
- typedef __fp16 simsimd_bf16_t;
290
+ typedef __bf16 simsimd_bf16_t;
255
291
  #elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__)) && \
256
- (defined(__SSE2__) || defined(__AVX512F__)))
257
- typedef _Float16 simsimd_bf16_t;
258
- #if !defined(SIMSIMD_NATIVE_BF16)
292
+ (defined(__AVX512BF16__)))
293
+ typedef __bfloat16 simsimd_bf16_t;
294
+ #undef SIMSIMD_NATIVE_BF16
259
295
  #define SIMSIMD_NATIVE_BF16 1
296
+ #else // Unknown compiler or architecture
297
+ #if defined(__GNUC__) || defined(__clang__) // Some compilers don't support warning pragmas
298
+ #warning "Unknown compiler or architecture for bfloat16."
260
299
  #endif
261
- #else // Unknown compiler or architecture
300
+ #undef SIMSIMD_NATIVE_BF16
262
301
  #define SIMSIMD_NATIVE_BF16 0
263
302
  #endif // Unknown compiler or architecture
264
303
  #endif // !SIMSIMD_NATIVE_BF16
@@ -286,17 +325,38 @@ typedef unsigned short simsimd_bf16_t;
286
325
  #endif
287
326
  #endif
288
327
 
289
- #define SIMSIMD_IDENTIFY(x) (x)
328
+ /*
329
+ * Let's make sure the sizes of the types are as expected.
330
+ * In C the `_Static_assert` is only available with C 11 and later.
331
+ */
332
+ #define SIMSIMD_STATIC_ASSERT(cond, msg) typedef char static_assertion_##msg[(cond) ? 1 : -1]
333
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_i8_t) == 1, simsimd_i8_t_must_be_1_byte);
334
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_u8_t) == 1, simsimd_u8_t_must_be_1_byte);
335
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_i16_t) == 2, simsimd_i16_t_must_be_2_bytes);
336
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_u16_t) == 2, simsimd_u16_t_must_be_2_bytes);
337
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_i32_t) == 4, simsimd_i32_t_must_be_4_bytes);
338
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_u32_t) == 4, simsimd_u32_t_must_be_4_bytes);
339
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_i64_t) == 8, simsimd_i64_t_must_be_8_bytes);
340
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_u64_t) == 8, simsimd_u64_t_must_be_8_bytes);
341
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_f32_t) == 4, simsimd_f32_t_must_be_4_bytes);
342
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_f64_t) == 8, simsimd_f64_t_must_be_8_bytes);
343
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_f16_t) == 2, simsimd_f16_t_must_be_2_bytes);
344
+ SIMSIMD_STATIC_ASSERT(sizeof(simsimd_bf16_t) == 2, simsimd_bf16_t_must_be_2_bytes);
345
+
346
+ #define SIMSIMD_DEREFERENCE(x) (*(x))
347
+ #define SIMSIMD_EXPORT(x, y) *(y) = x
290
348
 
291
349
  /**
292
350
  * @brief Returns the value of the half-precision floating-point number,
293
351
  * potentially decompressed into single-precision.
294
352
  */
295
- #ifndef SIMSIMD_UNCOMPRESS_F16
353
+ #if !defined(SIMSIMD_F16_TO_F32)
296
354
  #if SIMSIMD_NATIVE_F16
297
- #define SIMSIMD_UNCOMPRESS_F16(x) (SIMSIMD_IDENTIFY(x))
355
+ #define SIMSIMD_F16_TO_F32(x) (SIMSIMD_DEREFERENCE(x))
356
+ #define SIMSIMD_F32_TO_F16(x, y) (SIMSIMD_EXPORT(x, y))
298
357
  #else
299
- #define SIMSIMD_UNCOMPRESS_F16(x) (simsimd_uncompress_f16(x))
358
+ #define SIMSIMD_F16_TO_F32(x) (simsimd_f16_to_f32(x))
359
+ #define SIMSIMD_F32_TO_F16(x, y) (simsimd_f32_to_f16(x, y))
300
360
  #endif
301
361
  #endif
302
362
 
@@ -304,22 +364,34 @@ typedef unsigned short simsimd_bf16_t;
304
364
  * @brief Returns the value of the half-precision brain floating-point number,
305
365
  * potentially decompressed into single-precision.
306
366
  */
307
- #ifndef SIMSIMD_UNCOMPRESS_BF16
367
+ #if !defined(SIMSIMD_BF16_TO_F32)
308
368
  #if SIMSIMD_NATIVE_BF16
309
- #define SIMSIMD_UNCOMPRESS_BF16(x) (SIMSIMD_IDENTIFY(x))
369
+ #define SIMSIMD_BF16_TO_F32(x) (SIMSIMD_DEREFERENCE(x))
370
+ #define SIMSIMD_F32_TO_BF16(x, y) (SIMSIMD_EXPORT(x, y))
310
371
  #else
311
- #define SIMSIMD_UNCOMPRESS_BF16(x) (simsimd_uncompress_bf16(x))
372
+ #define SIMSIMD_BF16_TO_F32(x) (simsimd_bf16_to_f32(x))
373
+ #define SIMSIMD_F32_TO_BF16(x, y) (simsimd_f32_to_bf16(x, y))
312
374
  #endif
313
375
  #endif
314
376
 
377
+ /** @brief Convenience type for half-precision floating-point type conversions. */
315
378
  typedef union {
316
379
  unsigned i;
317
380
  float f;
318
381
  } simsimd_f32i32_t;
319
382
 
320
383
  /**
321
- * @brief Computes `1/sqrt(x)` using the trick from Quake 3, replacing
322
- * magic numbers with the ones suggested by Jan Kadlec.
384
+ * @brief Computes `1/sqrt(x)` using the trick from Quake 3,
385
+ * replacing the magic numbers with the ones suggested by Jan Kadlec.
386
+ *
387
+ * Subsequent additions by hardware manufacturers have made this algorithm redundant for the most part.
388
+ * For example, on x86, Intel introduced the SSE instruction `rsqrtss` in 1999. In a 2009 benchmark on
389
+ * the Intel Core 2, this instruction took 0.85ns per float compared to 3.54ns for the fast inverse
390
+ * square root algorithm, and had less error. Carmack's Magic Number `rsqrt` had an average error
391
+ * of 0.0990%, while SSE `rsqrtss` had 0.0094%, a 10x improvement.
392
+ *
393
+ * https://web.archive.org/web/20210208132927/http://assemblyrequired.crashworks.org/timing-square-root/
394
+ * https://stackoverflow.com/a/41460625/2766161
323
395
  */
324
396
  SIMSIMD_PUBLIC simsimd_f32_t simsimd_approximate_inverse_square_root(simsimd_f32_t number) {
325
397
  simsimd_f32i32_t conv;
@@ -351,20 +423,17 @@ SIMSIMD_PUBLIC simsimd_f32_t simsimd_approximate_log(simsimd_f32_t number) {
351
423
  * https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
352
424
  * https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
353
425
  */
354
- SIMSIMD_PUBLIC simsimd_f32_t simsimd_uncompress_f16(unsigned short x) {
355
- union float_or_unsigned_int_t {
356
- float f;
357
- unsigned int i;
358
- };
426
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const* x_ptr) {
427
+ unsigned short x = *(unsigned short const*)x_ptr;
359
428
  unsigned int exponent = (x & 0x7C00) >> 10;
360
429
  unsigned int mantissa = (x & 0x03FF) << 13;
361
- union float_or_unsigned_int_t mantissa_union;
362
- mantissa_union.f = (float)mantissa;
363
- unsigned int v = (mantissa_union.i) >> 23;
364
- union float_or_unsigned_int_t result_union;
365
- result_union.i = (x & 0x8000) << 16 | (exponent != 0) * ((exponent + 112) << 23 | mantissa) |
366
- ((exponent == 0) & (mantissa != 0)) * ((v - 37) << 23 | ((mantissa << (150 - v)) & 0x007FE000));
367
- return result_union.f;
430
+ simsimd_f32i32_t mantissa_conv;
431
+ mantissa_conv.f = (float)mantissa;
432
+ unsigned int v = (mantissa_conv.i) >> 23;
433
+ simsimd_f32i32_t conv;
434
+ conv.i = (x & 0x8000) << 16 | (exponent != 0) * ((exponent + 112) << 23 | mantissa) |
435
+ ((exponent == 0) & (mantissa != 0)) * ((v - 37) << 23 | ((mantissa << (150 - v)) & 0x007FE000));
436
+ return conv.f;
368
437
  }
369
438
 
370
439
  /**
@@ -376,20 +445,16 @@ SIMSIMD_PUBLIC simsimd_f32_t simsimd_uncompress_f16(unsigned short x) {
376
445
  * https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
377
446
  * https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
378
447
  */
379
- SIMSIMD_PUBLIC unsigned short simsimd_compress_f16(simsimd_f32_t x) {
380
- union float_or_unsigned_int_t {
381
- float f;
382
- unsigned int i;
383
- } x_union;
384
- x_union.f = x;
385
-
386
- unsigned int b = x_union.i + 0x00001000;
448
+ SIMSIMD_PUBLIC void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t* result_ptr) {
449
+ simsimd_f32i32_t conv;
450
+ conv.f = x;
451
+ unsigned int b = conv.i + 0x00001000;
387
452
  unsigned int e = (b & 0x7F800000) >> 23;
388
453
  unsigned int m = b & 0x007FFFFF;
389
454
  unsigned short result = ((b & 0x80000000) >> 16) | (e > 112) * ((((e - 112) << 10) & 0x7C00) | (m >> 13)) |
390
455
  ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) |
391
456
  ((e > 143) * 0x7FFF);
392
- return result;
457
+ *(unsigned short*)result_ptr = result;
393
458
  }
394
459
 
395
460
  /**
@@ -399,31 +464,36 @@ SIMSIMD_PUBLIC unsigned short simsimd_compress_f16(simsimd_f32_t x) {
399
464
  * https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307
400
465
  * https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus
401
466
  */
402
- SIMSIMD_PUBLIC simsimd_f32_t simsimd_uncompress_bf16(unsigned short x) {
403
- union float_or_unsigned_int_t {
404
- float f;
405
- unsigned int i;
406
- };
407
- union float_or_unsigned_int_t result_union;
408
- result_union.i = x << 16; // Zero extends the mantissa
409
- return result_union.f;
467
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const* x_ptr) {
468
+ unsigned short x = *(unsigned short const*)x_ptr;
469
+ simsimd_f32i32_t conv;
470
+ conv.i = x << 16; // Zero extends the mantissa
471
+ return conv.f;
410
472
  }
411
473
 
412
474
  /**
413
475
  * @brief Compresses a `float` to a `bf16` representation.
476
+ *
477
+ * https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307
478
+ * https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus
414
479
  */
415
- SIMSIMD_PUBLIC unsigned short simsimd_compress_bf16(simsimd_f32_t x) {
416
- union float_or_unsigned_int_t {
417
- float f;
418
- unsigned int i;
419
- };
420
- union float_or_unsigned_int_t value;
421
- value.f = x;
422
- value.i >>= 16;
423
- value.i &= 0xFFFF;
424
- return (unsigned short)value.i;
480
+ SIMSIMD_PUBLIC void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t* result_ptr) {
481
+ simsimd_f32i32_t conv;
482
+ conv.f = x;
483
+ conv.i += 0x8000; // Rounding is optional
484
+ conv.i >>= 16;
485
+ // The top 16 bits will be zeroed out anyways
486
+ // conv.i &= 0xFFFF;
487
+ *(unsigned short*)result_ptr = (unsigned short)conv.i;
425
488
  }
426
489
 
490
+ SIMSIMD_PUBLIC simsimd_u32_t simsimd_u32_rol(simsimd_u32_t x, int n) { return (x << n) | (x >> (32 - n)); }
491
+ SIMSIMD_PUBLIC simsimd_u16_t simsimd_u16_rol(simsimd_u16_t x, int n) { return (x << n) | (x >> (16 - n)); }
492
+ SIMSIMD_PUBLIC simsimd_u8_t simsimd_u8_rol(simsimd_u8_t x, int n) { return (x << n) | (x >> (8 - n)); }
493
+ SIMSIMD_PUBLIC simsimd_u32_t simsimd_u32_ror(simsimd_u32_t x, int n) { return (x >> n) | (x << (32 - n)); }
494
+ SIMSIMD_PUBLIC simsimd_u16_t simsimd_u16_ror(simsimd_u16_t x, int n) { return (x >> n) | (x << (16 - n)); }
495
+ SIMSIMD_PUBLIC simsimd_u8_t simsimd_u8_ror(simsimd_u8_t x, int n) { return (x >> n) | (x << (8 - n)); }
496
+
427
497
  #ifdef __cplusplus
428
498
  } // extern "C"
429
499
  #endif
@@ -83,20 +83,20 @@ export const jaccard = (a: Uint8Array, b: Uint8Array): number => {
83
83
  };
84
84
 
85
85
  /**
86
- * @brief Computes the kullbackleibler similarity coefficient between two vectors.
86
+ * @brief Computes the Kullback-Leibler divergence between two vectors.
87
87
  * @param {Float64Array|Float32Array} a - The first vector.
88
88
  * @param {Float64Array|Float32Array} b - The second vector.
89
- * @returns {number} The Jaccard similarity coefficient between vectors a and b.
89
+ * @returns {number} The Kullback-Leibler divergence between vectors a and b.
90
90
  */
91
91
  export const kullbackleibler = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
92
92
  return compiled.kullbackleibler(a, b);
93
93
  };
94
94
 
95
95
  /**
96
- * @brief Computes the jensenshannon similarity coefficient between two vectors.
96
+ * @brief Computes the Jensen-Shannon divergence between two vectors.
97
97
  * @param {Float64Array|Float32Array} a - The first vector.
98
98
  * @param {Float64Array|Float32Array} b - The second vector.
99
- * @returns {number} The Jaccard similarity coefficient between vectors a and b.
99
+ * @returns {number} The Jensen-Shannon divergence between vectors a and b.
100
100
  */
101
101
  export const jensenshannon = (a: Float64Array | Float32Array, b: Float64Array | Float32Array): number => {
102
102
  return compiled.jensenshannon(a, b);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "5.0.0",
3
+ "version": "5.6.4",
4
4
  "description": "Fastest SIMD-Accelerated Vector Similarity Functions for x86 and Arm",
5
5
  "homepage": "https://github.com/ashvardanian/simsimd",
6
6
  "author": "Ash Vardanian",
@@ -54,24 +54,29 @@ type ComplexProduct = (f64, f64);
54
54
  extern "C" {
55
55
 
56
56
  fn simsimd_dot_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
57
+ fn simsimd_dot_bf16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
57
58
  fn simsimd_dot_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
58
59
  fn simsimd_dot_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
59
60
 
60
61
  fn simsimd_dot_f16c(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
62
+ fn simsimd_dot_bf16c(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
61
63
  fn simsimd_dot_f32c(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
62
64
  fn simsimd_dot_f64c(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
63
65
 
64
66
  fn simsimd_vdot_f16c(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
67
+ fn simsimd_vdot_bf16c(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
65
68
  fn simsimd_vdot_f32c(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
66
69
  fn simsimd_vdot_f64c(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
67
70
 
68
71
  fn simsimd_cos_i8(a: *const i8, b: *const i8, c: usize, d: *mut Distance);
69
72
  fn simsimd_cos_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
73
+ fn simsimd_cos_bf16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
70
74
  fn simsimd_cos_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
71
75
  fn simsimd_cos_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
72
76
 
73
77
  fn simsimd_l2sq_i8(a: *const i8, b: *const i8, c: usize, d: *mut Distance);
74
78
  fn simsimd_l2sq_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
79
+ fn simsimd_l2sq_bf16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
75
80
  fn simsimd_l2sq_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
76
81
  fn simsimd_l2sq_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
77
82
 
@@ -79,10 +84,12 @@ extern "C" {
79
84
  fn simsimd_jaccard_b8(a: *const u8, b: *const u8, c: usize, d: *mut Distance);
80
85
 
81
86
  fn simsimd_js_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
87
+ fn simsimd_js_bf16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
82
88
  fn simsimd_js_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
83
89
  fn simsimd_js_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
84
90
 
85
91
  fn simsimd_kl_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
92
+ fn simsimd_kl_bf16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
86
93
  fn simsimd_kl_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
87
94
  fn simsimd_kl_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
88
95
 
@@ -107,6 +114,12 @@ pub struct f16(u16);
107
114
 
108
115
  impl f16 {}
109
116
 
117
+ /// A half-precision floating point number, called brain float.
118
+ #[repr(transparent)]
119
+ pub struct bf16(u16);
120
+
121
+ impl bf16 {}
122
+
110
123
  /// The `capabilities` module provides functions for detecting the hardware features
111
124
  /// available on the current system.
112
125
  pub mod capabilities {
@@ -370,6 +383,50 @@ impl SpatialSimilarity for f16 {
370
383
  }
371
384
  }
372
385
 
386
+ impl SpatialSimilarity for bf16 {
387
+ fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
388
+ if a.len() != b.len() {
389
+ return None;
390
+ }
391
+
392
+ // Explicitly cast `*const bf16` to `*const u16`
393
+ let a_ptr = a.as_ptr() as *const u16;
394
+ let b_ptr = b.as_ptr() as *const u16;
395
+ let mut distance_value: Distance = 0.0;
396
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
397
+ unsafe { simsimd_cos_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
398
+ Some(distance_value)
399
+ }
400
+
401
+ fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
402
+ if a.len() != b.len() {
403
+ return None;
404
+ }
405
+
406
+ // Explicitly cast `*const bf16` to `*const u16`
407
+ let a_ptr = a.as_ptr() as *const u16;
408
+ let b_ptr = b.as_ptr() as *const u16;
409
+ let mut distance_value: Distance = 0.0;
410
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
411
+ unsafe { simsimd_dot_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
412
+ Some(distance_value)
413
+ }
414
+
415
+ fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
416
+ if a.len() != b.len() {
417
+ return None;
418
+ }
419
+
420
+ // Explicitly cast `*const bf16` to `*const u16`
421
+ let a_ptr = a.as_ptr() as *const u16;
422
+ let b_ptr = b.as_ptr() as *const u16;
423
+ let mut distance_value: Distance = 0.0;
424
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
425
+ unsafe { simsimd_l2sq_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
426
+ Some(distance_value)
427
+ }
428
+ }
429
+
373
430
  impl SpatialSimilarity for f32 {
374
431
  fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
375
432
  if a.len() != b.len() {
@@ -464,6 +521,36 @@ impl ProbabilitySimilarity for f16 {
464
521
  }
465
522
  }
466
523
 
524
+ impl ProbabilitySimilarity for bf16 {
525
+ fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
526
+ if a.len() != b.len() {
527
+ return None;
528
+ }
529
+
530
+ // Explicitly cast `*const bf16` to `*const u16`
531
+ let a_ptr = a.as_ptr() as *const u16;
532
+ let b_ptr = b.as_ptr() as *const u16;
533
+ let mut distance_value: Distance = 0.0;
534
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
535
+ unsafe { simsimd_js_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
536
+ Some(distance_value)
537
+ }
538
+
539
+ fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
540
+ if a.len() != b.len() {
541
+ return None;
542
+ }
543
+
544
+ // Explicitly cast `*const bf16` to `*const u16`
545
+ let a_ptr = a.as_ptr() as *const u16;
546
+ let b_ptr = b.as_ptr() as *const u16;
547
+ let mut distance_value: Distance = 0.0;
548
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
549
+ unsafe { simsimd_kl_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
550
+ Some(distance_value)
551
+ }
552
+ }
553
+
467
554
  impl ProbabilitySimilarity for f32 {
468
555
  fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
469
556
  if a.len() != b.len() {
@@ -536,6 +623,36 @@ impl ComplexProducts for f16 {
536
623
  }
537
624
  }
538
625
 
626
+ impl ComplexProducts for bf16 {
627
+ fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
628
+ if a.len() != b.len() {
629
+ return None;
630
+ }
631
+ // Prepare the output array where the real and imaginary parts will be stored
632
+ let mut product: [Distance; 2] = [0.0, 0.0];
633
+ let product_ptr: *mut Distance = &mut product[0] as *mut _;
634
+ // Explicitly cast `*const bf16` to `*const u16`
635
+ let a_ptr = a.as_ptr() as *const u16;
636
+ let b_ptr = b.as_ptr() as *const u16;
637
+ unsafe { simsimd_dot_bf16c(a_ptr, b_ptr, a.len(), product_ptr) };
638
+ Some((product[0], product[1]))
639
+ }
640
+
641
+ fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
642
+ if a.len() != b.len() {
643
+ return None;
644
+ }
645
+ // Prepare the output array where the real and imaginary parts will be stored
646
+ let mut product: [Distance; 2] = [0.0, 0.0];
647
+ let product_ptr: *mut Distance = &mut product[0] as *mut _;
648
+ // Explicitly cast `*const bf16` to `*const u16`
649
+ let a_ptr = a.as_ptr() as *const u16;
650
+ let b_ptr = b.as_ptr() as *const u16;
651
+ unsafe { simsimd_vdot_bf16c(a_ptr, b_ptr, a.len(), product_ptr) };
652
+ Some((product[0], product[1]))
653
+ }
654
+ }
655
+
539
656
  impl ComplexProducts for f32 {
540
657
  fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
541
658
  if a.len() != b.len() {
@@ -583,6 +700,7 @@ impl ComplexProducts for f64 {
583
700
  #[cfg(test)]
584
701
  mod tests {
585
702
  use super::*;
703
+ use half::bf16 as HalfBF16;
586
704
  use half::f16 as HalfF16;
587
705
 
588
706
  #[test]
@@ -786,6 +904,26 @@ mod tests {
786
904
  }
787
905
  }
788
906
 
907
+ #[test]
908
+ fn test_cos_bf16_same() {
909
+ // Assuming these u16 values represent bf16 bit patterns, and they are identical
910
+ let a_u16: &[u16] = &[15360, 16384, 17408]; // Corresponding to some bf16 values
911
+ let b_u16: &[u16] = &[15360, 16384, 17408]; // Same as above for simplicity
912
+
913
+ // Reinterpret cast from &[u16] to &[bf16]
914
+ // SAFETY: This is safe as long as the representations are guaranteed to be identical,
915
+ // which they are for transparent structs wrapping the same type.
916
+ let a_bf16: &[bf16] =
917
+ unsafe { std::slice::from_raw_parts(a_u16.as_ptr() as *const bf16, a_u16.len()) };
918
+ let b_bf16: &[bf16] =
919
+ unsafe { std::slice::from_raw_parts(b_u16.as_ptr() as *const bf16, b_u16.len()) };
920
+
921
+ if let Some(result) = SpatialSimilarity::cosine(a_bf16, b_bf16) {
922
+ println!("The result of cos_bf16 is {:.8}", result);
923
+ assert_almost_equal(0.0, result, 0.01); // Example value, adjust according to actual expected value
924
+ }
925
+ }
926
+
789
927
  #[test]
790
928
  fn test_cos_f16_interop() {
791
929
  let a_half: Vec<HalfF16> = vec![1.0, 2.0, 3.0]
@@ -812,4 +950,31 @@ mod tests {
812
950
  assert_almost_equal(0.025, result, 0.01);
813
951
  }
814
952
  }
953
+
954
+ #[test]
955
+ fn test_cos_bf16_interop() {
956
+ let a_half: Vec<HalfBF16> = vec![1.0, 2.0, 3.0]
957
+ .iter()
958
+ .map(|&x| HalfBF16::from_f32(x))
959
+ .collect();
960
+ let b_half: Vec<HalfBF16> = vec![4.0, 5.0, 6.0]
961
+ .iter()
962
+ .map(|&x| HalfBF16::from_f32(x))
963
+ .collect();
964
+
965
+ // SAFETY: This is safe as long as the memory representations are guaranteed to be identical,
966
+ // which they are due to both being #[repr(transparent)] wrappers around u16.
967
+ let a_simsimd: &[bf16] =
968
+ unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const bf16, a_half.len()) };
969
+ let b_simsimd: &[bf16] =
970
+ unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const bf16, b_half.len()) };
971
+
972
+ // Use the reinterpret-casted slices with your SpatialSimilarity implementation
973
+ if let Some(result) = SpatialSimilarity::cosine(a_simsimd, b_simsimd) {
974
+ // Expected value might need adjustment depending on actual cosine functionality
975
+ // Assuming identical vectors yield cosine similarity of 1.0
976
+ println!("The result of cos_bf16 (interop) is {:.8}", result);
977
+ assert_almost_equal(0.025, result, 0.01);
978
+ }
979
+ }
815
980
  }