usearch 2.13.5 → 2.14.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.
- package/README.md +11 -16
- package/include/usearch/index.hpp +12 -11
- package/include/usearch/index_dense.hpp +1 -0
- package/include/usearch/index_plugins.hpp +135 -9
- package/javascript/dist/cjs/usearch.d.ts +1 -0
- package/javascript/dist/cjs/usearch.js +1 -0
- package/javascript/dist/esm/usearch.d.ts +1 -0
- package/javascript/dist/esm/usearch.js +1 -0
- package/javascript/usearch.ts +7 -6
- 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/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">USearch</h1>
|
|
2
2
|
<h3 align="center">
|
|
3
3
|
Smaller & <a href="https://www.unum.cloud/blog/2023-11-07-scaling-vector-search-with-intel">Faster</a> Single-File<br/>
|
|
4
|
-
Similarity Search Engine for <a href="https://github.com/ashvardanian/simsimd">Vectors</a> & 🔜 <a href="https://github.com/ashvardanian/stringzilla">Texts</a>
|
|
4
|
+
Similarity Search & Clustering Engine for <a href="https://github.com/ashvardanian/simsimd">Vectors</a> & 🔜 <a href="https://github.com/ashvardanian/stringzilla">Texts</a>
|
|
5
5
|
</h3>
|
|
6
6
|
<br/>
|
|
7
7
|
|
|
@@ -71,13 +71,10 @@ Linux • MacOS • Windows • iOS • WebAssembly •
|
|
|
71
71
|
|
|
72
72
|
__Technical Insights__ and related articles:
|
|
73
73
|
|
|
74
|
-
- [Uses Horner's method for polynomial approximations, beating GCC 12 by 119x](https://ashvardanian.com/posts/gcc-12-vs-avx512fp16/).
|
|
75
74
|
- [Uses Arm SVE and x86 AVX-512's masked loads to eliminate tail `for`-loops](https://ashvardanian.com/posts/simsimd-faster-scipy/#tails-of-the-past-the-significance-of-masked-loads).
|
|
76
|
-
- [Uses
|
|
77
|
-
- [Substitutes LibC's `sqrt` calls with bithacks using Jan Kadlec's constant](https://ashvardanian.com/posts/simsimd-faster-scipy/#bonus-section-bypassing-sqrt-and-libc-dependencies).
|
|
75
|
+
- [Uses Horner's method for polynomial approximations, beating GCC 12 by 119x](https://ashvardanian.com/posts/gcc-12-vs-avx512fp16/).
|
|
78
76
|
- [For every language implements a custom separate binding](https://ashvardanian.com/posts/porting-cpp-library-to-ten-languages/).
|
|
79
|
-
|
|
80
|
-
- [For JavaScript uses typed arrays and NAPI for zero-copy calls](https://ashvardanian.com/posts/javascript-ai-vector-search/).
|
|
77
|
+
|
|
81
78
|
|
|
82
79
|
## Comparison with FAISS
|
|
83
80
|
|
|
@@ -119,17 +116,15 @@ USearch is compact and broadly compatible without sacrificing performance, prima
|
|
|
119
116
|
Base functionality is identical to FAISS, and the interface must be familiar if you have ever investigated Approximate Nearest Neighbors search:
|
|
120
117
|
|
|
121
118
|
```py
|
|
122
|
-
# pip install
|
|
119
|
+
# pip install usearch
|
|
123
120
|
|
|
124
121
|
import numpy as np
|
|
125
122
|
from usearch.index import Index
|
|
126
123
|
|
|
127
|
-
index = Index(ndim=3)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
index.
|
|
131
|
-
|
|
132
|
-
matches = index.search(vector, 10)
|
|
124
|
+
index = Index(ndim=3) # Default settings for 3D vectors
|
|
125
|
+
vector = np.array([0.2, 0.6, 0.4]) # Can be a matrix for batch operations
|
|
126
|
+
index.add(42, vector) # Add one or many vectors in parallel
|
|
127
|
+
matches = index.search(vector, 10) # Find 10 nearest neighbors
|
|
133
128
|
|
|
134
129
|
assert matches[0].key == 42
|
|
135
130
|
assert matches[0].distance <= 0.001
|
|
@@ -137,13 +132,13 @@ assert np.allclose(index[42], vector, atol=0.1) # Ensure high tolerance in mixed
|
|
|
137
132
|
```
|
|
138
133
|
|
|
139
134
|
More settings are always available, and the API is designed to be as flexible as possible.
|
|
140
|
-
The default storage/quantization level is hardware-dependant for efficiency, but `
|
|
135
|
+
The default storage/quantization level is hardware-dependant for efficiency, but `bf16` is recommended for most modern CPUs.
|
|
141
136
|
|
|
142
137
|
```py
|
|
143
138
|
index = Index(
|
|
144
139
|
ndim=3, # Define the number of dimensions in input vectors
|
|
145
140
|
metric='cos', # Choose 'l2sq', 'ip', 'haversine' or other metric, default = 'cos'
|
|
146
|
-
dtype='
|
|
141
|
+
dtype='bf16', # Store as 'f64', 'f32', 'f16', 'i8', 'b1'..., default = None
|
|
147
142
|
connectivity=16, # Optional: Limit number of neighbors per graph node
|
|
148
143
|
expansion_add=128, # Optional: Control the recall of indexing
|
|
149
144
|
expansion_search=64, # Optional: Control the quality of the search
|
|
@@ -549,7 +544,7 @@ doi = {10.5281/zenodo.7949416},
|
|
|
549
544
|
author = {Vardanian, Ash},
|
|
550
545
|
title = {{USearch by Unum Cloud}},
|
|
551
546
|
url = {https://github.com/unum-cloud/usearch},
|
|
552
|
-
version = {2.
|
|
547
|
+
version = {2.14.0},
|
|
553
548
|
year = {2023},
|
|
554
549
|
month = oct,
|
|
555
550
|
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
#define UNUM_USEARCH_HPP
|
|
9
9
|
|
|
10
10
|
#define USEARCH_VERSION_MAJOR 2
|
|
11
|
-
#define USEARCH_VERSION_MINOR
|
|
12
|
-
#define USEARCH_VERSION_PATCH
|
|
11
|
+
#define USEARCH_VERSION_MINOR 14
|
|
12
|
+
#define USEARCH_VERSION_PATCH 0
|
|
13
13
|
|
|
14
14
|
// Inferring C++ version
|
|
15
15
|
// https://stackoverflow.com/a/61552074
|
|
@@ -2870,16 +2870,16 @@ class index_gt {
|
|
|
2870
2870
|
result.computed_distances = context.computed_distances;
|
|
2871
2871
|
result.visited_members = context.iteration_cycles;
|
|
2872
2872
|
|
|
2873
|
-
// If we are updating the entry node itself, it won't contain any neighbors,
|
|
2874
|
-
// so we should traverse a level down to find the closest match.
|
|
2875
|
-
if (updated_node_level == max_level_copy)
|
|
2876
|
-
updated_node_level--;
|
|
2877
|
-
|
|
2878
2873
|
// Go down the level, tracking only the closest match;
|
|
2879
2874
|
// It may even be equal to the `updated_slot`
|
|
2880
|
-
compressed_slot_t closest_slot =
|
|
2881
|
-
|
|
2882
|
-
|
|
2875
|
+
compressed_slot_t closest_slot =
|
|
2876
|
+
// If we are updating the entry node itself, it won't contain any neighbors,
|
|
2877
|
+
// so we should traverse a level down to find the closest match.
|
|
2878
|
+
updated_node_level == max_level_copy //
|
|
2879
|
+
? entry_slot_copy
|
|
2880
|
+
: search_for_one_( //
|
|
2881
|
+
value, metric, prefetch, //
|
|
2882
|
+
entry_slot_copy, max_level_copy, updated_node_level, context);
|
|
2883
2883
|
|
|
2884
2884
|
// From `updated_node_level` down - perform proper extensive search
|
|
2885
2885
|
for (level_t level = (std::min)(updated_node_level, max_level_copy); level >= 0; --level) {
|
|
@@ -2943,7 +2943,7 @@ class index_gt {
|
|
|
2943
2943
|
config.expansion = default_expansion_search();
|
|
2944
2944
|
|
|
2945
2945
|
// Using references is cleaner, but would result in UBSan false positives
|
|
2946
|
-
context_t* context_ptr = contexts_.data() + config.thread;
|
|
2946
|
+
context_t* context_ptr = contexts_.data() ? contexts_.data() + config.thread : nullptr;
|
|
2947
2947
|
top_candidates_t* top_ptr = context_ptr ? &context_ptr->top_candidates : nullptr;
|
|
2948
2948
|
search_result_t result{*this, top_ptr};
|
|
2949
2949
|
if (!nodes_count_.load(std::memory_order_relaxed))
|
|
@@ -3697,6 +3697,7 @@ class index_gt {
|
|
|
3697
3697
|
inline neighbors_ref_t neighbors_base_(node_t node) const noexcept { return {node.neighbors_tape()}; }
|
|
3698
3698
|
|
|
3699
3699
|
inline neighbors_ref_t neighbors_non_base_(node_t node, level_t level) const noexcept {
|
|
3700
|
+
usearch_assert_m(level > 0 && level <= node.level(), "Linking to missing level");
|
|
3700
3701
|
return {node.neighbors_tape() + pre_.neighbors_base_bytes + (level - 1) * pre_.neighbors_bytes};
|
|
3701
3702
|
}
|
|
3702
3703
|
|
|
@@ -2240,6 +2240,7 @@ class index_dense_gt {
|
|
|
2240
2240
|
case scalar_kind_t::f64_k: return make_casts_<f64_t>();
|
|
2241
2241
|
case scalar_kind_t::f32_k: return make_casts_<f32_t>();
|
|
2242
2242
|
case scalar_kind_t::f16_k: return make_casts_<f16_t>();
|
|
2243
|
+
case scalar_kind_t::bf16_k: return make_casts_<bf16_t>();
|
|
2243
2244
|
case scalar_kind_t::i8_k: return make_casts_<i8_t>();
|
|
2244
2245
|
case scalar_kind_t::b1x8_k: return make_casts_<b1x8_t>();
|
|
2245
2246
|
default: return {};
|
|
@@ -70,19 +70,26 @@ struct uuid_t {
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
class f16_bits_t;
|
|
73
|
+
class bf16_bits_t;
|
|
73
74
|
|
|
74
75
|
#if !USEARCH_USE_FP16LIB
|
|
75
76
|
#if USEARCH_USE_SIMSIMD
|
|
76
77
|
using f16_native_t = simsimd_f16_t;
|
|
78
|
+
using bf16_native_t = simsimd_bf16_t;
|
|
77
79
|
#elif defined(USEARCH_DEFINED_ARM)
|
|
78
80
|
using f16_native_t = __fp16;
|
|
81
|
+
using bf16_native_t = __fp16; // No better choice on most compilers!
|
|
79
82
|
#else
|
|
80
83
|
using f16_native_t = _Float16;
|
|
84
|
+
using bf16_native_t = _Float16; // No better choice on most compilers!
|
|
81
85
|
#endif
|
|
82
86
|
using f16_t = f16_native_t;
|
|
87
|
+
using bf16_t = bf16_native_t;
|
|
83
88
|
#else
|
|
84
89
|
using f16_native_t = void;
|
|
90
|
+
using bf16_native_t = void;
|
|
85
91
|
using f16_t = f16_bits_t;
|
|
92
|
+
using bf16_t = bf16_bits_t;
|
|
86
93
|
#endif
|
|
87
94
|
|
|
88
95
|
using f64_t = double;
|
|
@@ -132,6 +139,7 @@ enum class scalar_kind_t : std::uint8_t {
|
|
|
132
139
|
b1x8_k = 1,
|
|
133
140
|
u40_k = 2,
|
|
134
141
|
uuid_k = 3,
|
|
142
|
+
bf16_k = 4,
|
|
135
143
|
// Common:
|
|
136
144
|
f64_k = 10,
|
|
137
145
|
f32_k = 11,
|
|
@@ -167,6 +175,8 @@ template <typename scalar_at> scalar_kind_t scalar_kind() noexcept {
|
|
|
167
175
|
return scalar_kind_t::f32_k;
|
|
168
176
|
if (std::is_same<scalar_at, f16_t>())
|
|
169
177
|
return scalar_kind_t::f16_k;
|
|
178
|
+
if (std::is_same<scalar_at, bf16_t>())
|
|
179
|
+
return scalar_kind_t::bf16_k;
|
|
170
180
|
if (std::is_same<scalar_at, i8_t>())
|
|
171
181
|
return scalar_kind_t::i8_k;
|
|
172
182
|
if (std::is_same<scalar_at, u64_t>())
|
|
@@ -208,6 +218,7 @@ inline std::size_t bits_per_scalar(scalar_kind_t scalar_kind) noexcept {
|
|
|
208
218
|
switch (scalar_kind) {
|
|
209
219
|
case scalar_kind_t::uuid_k: return 128;
|
|
210
220
|
case scalar_kind_t::u40_k: return 40;
|
|
221
|
+
case scalar_kind_t::bf16_k: return 16;
|
|
211
222
|
case scalar_kind_t::b1x8_k: return 1;
|
|
212
223
|
case scalar_kind_t::u64_k: return 64;
|
|
213
224
|
case scalar_kind_t::i64_k: return 64;
|
|
@@ -229,6 +240,7 @@ inline std::size_t bits_per_scalar_word(scalar_kind_t scalar_kind) noexcept {
|
|
|
229
240
|
switch (scalar_kind) {
|
|
230
241
|
case scalar_kind_t::uuid_k: return 128;
|
|
231
242
|
case scalar_kind_t::u40_k: return 40;
|
|
243
|
+
case scalar_kind_t::bf16_k: return 16;
|
|
232
244
|
case scalar_kind_t::b1x8_k: return 8;
|
|
233
245
|
case scalar_kind_t::u64_k: return 64;
|
|
234
246
|
case scalar_kind_t::i64_k: return 64;
|
|
@@ -250,6 +262,7 @@ inline char const* scalar_kind_name(scalar_kind_t scalar_kind) noexcept {
|
|
|
250
262
|
switch (scalar_kind) {
|
|
251
263
|
case scalar_kind_t::uuid_k: return "uuid";
|
|
252
264
|
case scalar_kind_t::u40_k: return "u40";
|
|
265
|
+
case scalar_kind_t::bf16_k: return "bf16";
|
|
253
266
|
case scalar_kind_t::b1x8_k: return "b1x8";
|
|
254
267
|
case scalar_kind_t::u64_k: return "u64";
|
|
255
268
|
case scalar_kind_t::i64_k: return "i64";
|
|
@@ -291,10 +304,14 @@ inline expected_gt<scalar_kind_t> scalar_kind_from_name(char const* name, std::s
|
|
|
291
304
|
parsed.result = scalar_kind_t::f64_k;
|
|
292
305
|
else if (str_equals(name, len, "f16"))
|
|
293
306
|
parsed.result = scalar_kind_t::f16_k;
|
|
307
|
+
else if (str_equals(name, len, "bf16"))
|
|
308
|
+
parsed.result = scalar_kind_t::bf16_k;
|
|
294
309
|
else if (str_equals(name, len, "i8"))
|
|
295
310
|
parsed.result = scalar_kind_t::i8_k;
|
|
311
|
+
else if (str_equals(name, len, "b1"))
|
|
312
|
+
parsed.result = scalar_kind_t::b1x8_k;
|
|
296
313
|
else
|
|
297
|
-
parsed.failed("Unknown type, choose: f32, f16,
|
|
314
|
+
parsed.failed("Unknown type, choose: f64, f32, f16, bf16, i8, b1");
|
|
298
315
|
return parsed;
|
|
299
316
|
}
|
|
300
317
|
|
|
@@ -327,11 +344,13 @@ inline expected_gt<metric_kind_t> metric_from_name(char const* name, std::size_t
|
|
|
327
344
|
"tanimoto, sorensen");
|
|
328
345
|
return parsed;
|
|
329
346
|
}
|
|
330
|
-
|
|
331
347
|
inline expected_gt<metric_kind_t> metric_from_name(char const* name) {
|
|
332
348
|
return metric_from_name(name, std::strlen(name));
|
|
333
349
|
}
|
|
334
350
|
|
|
351
|
+
/**
|
|
352
|
+
* @brief Convenience function to upcast a half-precision floating point number to a single-precision one.
|
|
353
|
+
*/
|
|
335
354
|
inline float f16_to_f32(std::uint16_t u16) noexcept {
|
|
336
355
|
#if !USEARCH_USE_FP16LIB
|
|
337
356
|
f16_native_t f16;
|
|
@@ -342,6 +361,9 @@ inline float f16_to_f32(std::uint16_t u16) noexcept {
|
|
|
342
361
|
#endif
|
|
343
362
|
}
|
|
344
363
|
|
|
364
|
+
/**
|
|
365
|
+
* @brief Convenience function to downcast a single-precision floating point number to a half-precision one.
|
|
366
|
+
*/
|
|
345
367
|
inline std::uint16_t f32_to_f16(float f32) noexcept {
|
|
346
368
|
#if !USEARCH_USE_FP16LIB
|
|
347
369
|
f16_native_t f16 = f16_native_t(f32);
|
|
@@ -353,6 +375,36 @@ inline std::uint16_t f32_to_f16(float f32) noexcept {
|
|
|
353
375
|
#endif
|
|
354
376
|
}
|
|
355
377
|
|
|
378
|
+
/**
|
|
379
|
+
* @brief Convenience function to upcast a brain-floating point number to a single-precision one.
|
|
380
|
+
* https://github.com/ashvardanian/SimSIMD/blob/ff51434d90c66f916e94ff05b24530b127aa4cff/include/simsimd/types.h#L395-L410
|
|
381
|
+
*/
|
|
382
|
+
inline float bf16_to_f32(std::uint16_t u16) noexcept {
|
|
383
|
+
union float_or_unsigned_int_t {
|
|
384
|
+
float f;
|
|
385
|
+
unsigned int i;
|
|
386
|
+
};
|
|
387
|
+
union float_or_unsigned_int_t result_union;
|
|
388
|
+
result_union.i = u16 << 16; // Zero extends the mantissa
|
|
389
|
+
return result_union.f;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @brief Convenience function to downcast a single-precision floating point number to a brain-floating point one.
|
|
394
|
+
* https://github.com/ashvardanian/SimSIMD/blob/ff51434d90c66f916e94ff05b24530b127aa4cff/include/simsimd/types.h#L412-L425
|
|
395
|
+
*/
|
|
396
|
+
inline std::uint16_t f32_to_bf16(float f32) noexcept {
|
|
397
|
+
union float_or_unsigned_int_t {
|
|
398
|
+
float f;
|
|
399
|
+
unsigned int i;
|
|
400
|
+
};
|
|
401
|
+
union float_or_unsigned_int_t value;
|
|
402
|
+
value.f = f32;
|
|
403
|
+
value.i >>= 16;
|
|
404
|
+
value.i &= 0xFFFF;
|
|
405
|
+
return (unsigned short)value.i;
|
|
406
|
+
}
|
|
407
|
+
|
|
356
408
|
/**
|
|
357
409
|
* @brief Numeric type for the IEEE 754 half-precision floating point.
|
|
358
410
|
* If hardware support isn't available, falls back to a hardware
|
|
@@ -412,6 +464,65 @@ class f16_bits_t {
|
|
|
412
464
|
}
|
|
413
465
|
};
|
|
414
466
|
|
|
467
|
+
/**
|
|
468
|
+
* @brief Numeric type for brain-floating point half-precision floating point.
|
|
469
|
+
* If hardware support isn't available, falls back to a hardware
|
|
470
|
+
* agnostic in-software implementation.
|
|
471
|
+
*/
|
|
472
|
+
class bf16_bits_t {
|
|
473
|
+
std::uint16_t uint16_{};
|
|
474
|
+
|
|
475
|
+
public:
|
|
476
|
+
inline bf16_bits_t() noexcept : uint16_(0) {}
|
|
477
|
+
inline bf16_bits_t(bf16_bits_t&&) = default;
|
|
478
|
+
inline bf16_bits_t& operator=(bf16_bits_t&&) = default;
|
|
479
|
+
inline bf16_bits_t(bf16_bits_t const&) = default;
|
|
480
|
+
inline bf16_bits_t& operator=(bf16_bits_t const&) = default;
|
|
481
|
+
|
|
482
|
+
inline operator float() const noexcept { return bf16_to_f32(uint16_); }
|
|
483
|
+
inline explicit operator bool() const noexcept { return bf16_to_f32(uint16_) > 0.5f; }
|
|
484
|
+
|
|
485
|
+
inline bf16_bits_t(std::int8_t v) noexcept : uint16_(f32_to_bf16(v)) {}
|
|
486
|
+
inline bf16_bits_t(bool v) noexcept : uint16_(f32_to_bf16(v)) {}
|
|
487
|
+
inline bf16_bits_t(float v) noexcept : uint16_(f32_to_bf16(v)) {}
|
|
488
|
+
inline bf16_bits_t(double v) noexcept : uint16_(f32_to_bf16(static_cast<float>(v))) {}
|
|
489
|
+
|
|
490
|
+
inline bool operator<(bf16_bits_t const& other) const noexcept { return float(*this) < float(other); }
|
|
491
|
+
|
|
492
|
+
inline bf16_bits_t operator+(bf16_bits_t other) const noexcept { return {float(*this) + float(other)}; }
|
|
493
|
+
inline bf16_bits_t operator-(bf16_bits_t other) const noexcept { return {float(*this) - float(other)}; }
|
|
494
|
+
inline bf16_bits_t operator*(bf16_bits_t other) const noexcept { return {float(*this) * float(other)}; }
|
|
495
|
+
inline bf16_bits_t operator/(bf16_bits_t other) const noexcept { return {float(*this) / float(other)}; }
|
|
496
|
+
inline float operator+(float other) const noexcept { return float(*this) + other; }
|
|
497
|
+
inline float operator-(float other) const noexcept { return float(*this) - other; }
|
|
498
|
+
inline float operator*(float other) const noexcept { return float(*this) * other; }
|
|
499
|
+
inline float operator/(float other) const noexcept { return float(*this) / other; }
|
|
500
|
+
inline double operator+(double other) const noexcept { return float(*this) + other; }
|
|
501
|
+
inline double operator-(double other) const noexcept { return float(*this) - other; }
|
|
502
|
+
inline double operator*(double other) const noexcept { return float(*this) * other; }
|
|
503
|
+
inline double operator/(double other) const noexcept { return float(*this) / other; }
|
|
504
|
+
|
|
505
|
+
inline bf16_bits_t& operator+=(float v) noexcept {
|
|
506
|
+
uint16_ = f32_to_bf16(v + bf16_to_f32(uint16_));
|
|
507
|
+
return *this;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
inline bf16_bits_t& operator-=(float v) noexcept {
|
|
511
|
+
uint16_ = f32_to_bf16(v - bf16_to_f32(uint16_));
|
|
512
|
+
return *this;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
inline bf16_bits_t& operator*=(float v) noexcept {
|
|
516
|
+
uint16_ = f32_to_bf16(v * bf16_to_f32(uint16_));
|
|
517
|
+
return *this;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
inline bf16_bits_t& operator/=(float v) noexcept {
|
|
521
|
+
uint16_ = f32_to_bf16(v / bf16_to_f32(uint16_));
|
|
522
|
+
return *this;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
415
526
|
/**
|
|
416
527
|
* @brief An STL-based executor or a "thread-pool" for parallel execution.
|
|
417
528
|
* Isn't efficient for small batches, as it recreates the threads on every call.
|
|
@@ -1009,18 +1120,22 @@ template <typename to_scalar_at> struct cast_from_i8_gt {
|
|
|
1009
1120
|
};
|
|
1010
1121
|
|
|
1011
1122
|
template <> struct cast_gt<i8_t, f16_t> : public cast_from_i8_gt<f16_t> {};
|
|
1123
|
+
template <> struct cast_gt<i8_t, bf16_t> : public cast_from_i8_gt<bf16_t> {};
|
|
1012
1124
|
template <> struct cast_gt<i8_t, f32_t> : public cast_from_i8_gt<f32_t> {};
|
|
1013
1125
|
template <> struct cast_gt<i8_t, f64_t> : public cast_from_i8_gt<f64_t> {};
|
|
1014
1126
|
|
|
1015
1127
|
template <> struct cast_gt<f16_t, i8_t> : public cast_to_i8_gt<f16_t> {};
|
|
1128
|
+
template <> struct cast_gt<bf16_t, i8_t> : public cast_to_i8_gt<bf16_t> {};
|
|
1016
1129
|
template <> struct cast_gt<f32_t, i8_t> : public cast_to_i8_gt<f32_t> {};
|
|
1017
1130
|
template <> struct cast_gt<f64_t, i8_t> : public cast_to_i8_gt<f64_t> {};
|
|
1018
1131
|
|
|
1019
1132
|
template <> struct cast_gt<b1x8_t, f16_t> : public cast_from_b1x8_gt<f16_t> {};
|
|
1133
|
+
template <> struct cast_gt<b1x8_t, bf16_t> : public cast_from_b1x8_gt<bf16_t> {};
|
|
1020
1134
|
template <> struct cast_gt<b1x8_t, f32_t> : public cast_from_b1x8_gt<f32_t> {};
|
|
1021
1135
|
template <> struct cast_gt<b1x8_t, f64_t> : public cast_from_b1x8_gt<f64_t> {};
|
|
1022
1136
|
|
|
1023
1137
|
template <> struct cast_gt<f16_t, b1x8_t> : public cast_to_b1x8_gt<f16_t> {};
|
|
1138
|
+
template <> struct cast_gt<bf16_t, b1x8_t> : public cast_to_b1x8_gt<bf16_t> {};
|
|
1024
1139
|
template <> struct cast_gt<f32_t, b1x8_t> : public cast_to_b1x8_gt<f32_t> {};
|
|
1025
1140
|
template <> struct cast_gt<f64_t, b1x8_t> : public cast_to_b1x8_gt<f64_t> {};
|
|
1026
1141
|
|
|
@@ -1602,6 +1717,7 @@ class metric_punned_t {
|
|
|
1602
1717
|
case scalar_kind_t::f32_k: datatype = simsimd_datatype_f32_k; break;
|
|
1603
1718
|
case scalar_kind_t::f64_k: datatype = simsimd_datatype_f64_k; break;
|
|
1604
1719
|
case scalar_kind_t::f16_k: datatype = simsimd_datatype_f16_k; break;
|
|
1720
|
+
case scalar_kind_t::bf16_k: datatype = simsimd_datatype_bf16_k; break;
|
|
1605
1721
|
case scalar_kind_t::i8_k: datatype = simsimd_datatype_i8_k; break;
|
|
1606
1722
|
case scalar_kind_t::b1x8_k: datatype = simsimd_datatype_b8_k; break;
|
|
1607
1723
|
default: break;
|
|
@@ -1654,9 +1770,10 @@ class metric_punned_t {
|
|
|
1654
1770
|
switch (metric_kind_) {
|
|
1655
1771
|
case metric_kind_t::ip_k: {
|
|
1656
1772
|
switch (scalar_kind_) {
|
|
1657
|
-
case scalar_kind_t::
|
|
1658
|
-
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_ip_gt<f16_t, f32_t>>; break;
|
|
1773
|
+
case scalar_kind_t::bf16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_ip_gt<bf16_t, f32_t>>; break;
|
|
1659
1774
|
case scalar_kind_t::i8_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_ip_gt<i8_t, f32_t>>; break;
|
|
1775
|
+
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_ip_gt<f16_t, f32_t>>; break;
|
|
1776
|
+
case scalar_kind_t::f32_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_ip_gt<f32_t>>; break;
|
|
1660
1777
|
case scalar_kind_t::f64_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_ip_gt<f64_t>>; break;
|
|
1661
1778
|
default: metric_ptr_ = 0; break;
|
|
1662
1779
|
}
|
|
@@ -1664,9 +1781,10 @@ class metric_punned_t {
|
|
|
1664
1781
|
}
|
|
1665
1782
|
case metric_kind_t::cos_k: {
|
|
1666
1783
|
switch (scalar_kind_) {
|
|
1667
|
-
case scalar_kind_t::
|
|
1668
|
-
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_cos_gt<f16_t, f32_t>>; break;
|
|
1784
|
+
case scalar_kind_t::bf16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_cos_gt<bf16_t, f32_t>>; break;
|
|
1669
1785
|
case scalar_kind_t::i8_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_cos_gt<i8_t, f32_t>>; break;
|
|
1786
|
+
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_cos_gt<f16_t, f32_t>>; break;
|
|
1787
|
+
case scalar_kind_t::f32_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_cos_gt<f32_t>>; break;
|
|
1670
1788
|
case scalar_kind_t::f64_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_cos_gt<f64_t>>; break;
|
|
1671
1789
|
default: metric_ptr_ = 0; break;
|
|
1672
1790
|
}
|
|
@@ -1674,9 +1792,10 @@ class metric_punned_t {
|
|
|
1674
1792
|
}
|
|
1675
1793
|
case metric_kind_t::l2sq_k: {
|
|
1676
1794
|
switch (scalar_kind_) {
|
|
1677
|
-
case scalar_kind_t::
|
|
1678
|
-
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_l2sq_gt<f16_t, f32_t>>; break;
|
|
1795
|
+
case scalar_kind_t::bf16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_l2sq_gt<bf16_t, f32_t>>; break;
|
|
1679
1796
|
case scalar_kind_t::i8_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_l2sq_gt<i8_t, f32_t>>; break;
|
|
1797
|
+
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_l2sq_gt<f16_t, f32_t>>; break;
|
|
1798
|
+
case scalar_kind_t::f32_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_l2sq_gt<f32_t>>; break;
|
|
1680
1799
|
case scalar_kind_t::f64_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_l2sq_gt<f64_t>>; break;
|
|
1681
1800
|
default: metric_ptr_ = 0; break;
|
|
1682
1801
|
}
|
|
@@ -1684,6 +1803,9 @@ class metric_punned_t {
|
|
|
1684
1803
|
}
|
|
1685
1804
|
case metric_kind_t::pearson_k: {
|
|
1686
1805
|
switch (scalar_kind_) {
|
|
1806
|
+
case scalar_kind_t::bf16_k:
|
|
1807
|
+
metric_ptr_ = (uptr_t)&equidimensional_<metric_pearson_gt<bf16_t, f32_t>>;
|
|
1808
|
+
break;
|
|
1687
1809
|
case scalar_kind_t::i8_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_pearson_gt<i8_t, f32_t>>; break;
|
|
1688
1810
|
case scalar_kind_t::f16_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_pearson_gt<f16_t, f32_t>>; break;
|
|
1689
1811
|
case scalar_kind_t::f32_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_pearson_gt<f32_t>>; break;
|
|
@@ -1694,7 +1816,8 @@ class metric_punned_t {
|
|
|
1694
1816
|
}
|
|
1695
1817
|
case metric_kind_t::haversine_k: {
|
|
1696
1818
|
switch (scalar_kind_) {
|
|
1697
|
-
case scalar_kind_t::
|
|
1819
|
+
case scalar_kind_t::bf16_k: metric_ptr_ = 0; break; //< Half-precision 2D vectors are silly.
|
|
1820
|
+
case scalar_kind_t::f16_k: metric_ptr_ = 0; break; //< Half-precision 2D vectors are silly.
|
|
1698
1821
|
case scalar_kind_t::f32_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_haversine_gt<f32_t>>; break;
|
|
1699
1822
|
case scalar_kind_t::f64_k: metric_ptr_ = (uptr_t)&equidimensional_<metric_haversine_gt<f64_t>>; break;
|
|
1700
1823
|
default: metric_ptr_ = 0; break;
|
|
@@ -1703,6 +1826,9 @@ class metric_punned_t {
|
|
|
1703
1826
|
}
|
|
1704
1827
|
case metric_kind_t::divergence_k: {
|
|
1705
1828
|
switch (scalar_kind_) {
|
|
1829
|
+
case scalar_kind_t::bf16_k:
|
|
1830
|
+
metric_ptr_ = (uptr_t)&equidimensional_<metric_divergence_gt<bf16_t, f32_t>>;
|
|
1831
|
+
break;
|
|
1706
1832
|
case scalar_kind_t::f16_k:
|
|
1707
1833
|
metric_ptr_ = (uptr_t)&equidimensional_<metric_divergence_gt<f16_t, f32_t>>;
|
|
1708
1834
|
break;
|
package/javascript/usearch.ts
CHANGED
|
@@ -70,6 +70,7 @@ export enum ScalarKind {
|
|
|
70
70
|
F32 = "f32",
|
|
71
71
|
F64 = "f64",
|
|
72
72
|
F16 = "f16",
|
|
73
|
+
BF16 = "bf16",
|
|
73
74
|
I8 = "i8",
|
|
74
75
|
B1 = "b1",
|
|
75
76
|
}
|
|
@@ -610,12 +611,12 @@ function exactSearch(
|
|
|
610
611
|
}
|
|
611
612
|
|
|
612
613
|
const usearch = {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
614
|
+
Index,
|
|
615
|
+
MetricKind,
|
|
616
|
+
ScalarKind,
|
|
617
|
+
Matches,
|
|
618
|
+
BatchMatches,
|
|
619
|
+
exactSearch,
|
|
619
620
|
};
|
|
620
621
|
export default usearch;
|
|
621
622
|
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|