usearch 2.15.2 → 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.
- package/README.md +1 -1
- package/binding.gyp +1 -1
- package/c/CMakeLists.txt +10 -3
- package/cpp/README.md +1 -1
- package/cpp/bench.cpp +2 -2
- package/include/usearch/index.hpp +24 -13
- package/include/usearch/index_dense.hpp +45 -88
- package/include/usearch/index_plugins.hpp +126 -38
- package/javascript/usearch.test.js +15 -13
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/CODE_OF_CONDUCT.md +128 -0
- package/simsimd/CONTRIBUTING.md +18 -8
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/MANIFEST.in +2 -0
- package/simsimd/README.md +446 -158
- package/simsimd/build.rs +11 -1
- package/simsimd/c/lib.c +172 -40
- package/simsimd/include/simsimd/binary.h +11 -11
- package/simsimd/include/simsimd/curved.h +804 -0
- package/simsimd/include/simsimd/dot.h +333 -334
- package/simsimd/include/simsimd/geospatial.h +9 -3
- package/simsimd/include/simsimd/mesh.h +69 -0
- package/simsimd/include/simsimd/probability.h +178 -150
- package/simsimd/include/simsimd/simsimd.h +581 -107
- package/simsimd/include/simsimd/sparse.h +807 -0
- package/simsimd/include/simsimd/spatial.h +737 -502
- package/simsimd/include/simsimd/types.h +145 -75
- package/simsimd/javascript/simsimd.ts +4 -4
- package/simsimd/package.json +1 -1
- package/simsimd/rust/lib.rs +165 -0
|
@@ -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
|
|
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
|
-
#
|
|
187
|
+
#if !defined(SIMSIMD_SQRT)
|
|
177
188
|
#include <math.h>
|
|
178
|
-
#define
|
|
189
|
+
#define SIMSIMD_SQRT(x) (sqrt(x))
|
|
179
190
|
#endif
|
|
180
191
|
|
|
181
|
-
#
|
|
192
|
+
#if !defined(SIMSIMD_RSQRT)
|
|
182
193
|
#include <math.h>
|
|
183
|
-
#define
|
|
194
|
+
#define SIMSIMD_RSQRT(x) (1 / SIMSIMD_SQRT(x))
|
|
184
195
|
#endif
|
|
185
196
|
|
|
186
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
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(
|
|
244
|
+
(defined(__AVX512FP16__)))
|
|
224
245
|
typedef _Float16 simsimd_f16_t;
|
|
225
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
288
|
+
#undef SIMSIMD_NATIVE_BF16
|
|
252
289
|
#define SIMSIMD_NATIVE_BF16 1
|
|
253
|
-
|
|
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(
|
|
257
|
-
typedef
|
|
258
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
353
|
+
#if !defined(SIMSIMD_F16_TO_F32)
|
|
296
354
|
#if SIMSIMD_NATIVE_F16
|
|
297
|
-
#define
|
|
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
|
|
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
|
-
#
|
|
367
|
+
#if !defined(SIMSIMD_BF16_TO_F32)
|
|
308
368
|
#if SIMSIMD_NATIVE_BF16
|
|
309
|
-
#define
|
|
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
|
|
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,
|
|
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
|
|
355
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
unsigned int v = (
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return
|
|
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
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
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
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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);
|
package/simsimd/package.json
CHANGED
package/simsimd/rust/lib.rs
CHANGED
|
@@ -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
|
}
|