usearch 2.13.4 → 2.13.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.
- package/README.md +11 -3
- package/c/usearch.h +11 -0
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_dense.hpp +11 -6
- package/include/usearch/index_plugins.hpp +101 -62
- 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
|
@@ -254,7 +254,7 @@ assert!(
|
|
|
254
254
|
Training a quantization model and dimension-reduction is a common approach to accelerate vector search.
|
|
255
255
|
Those, however, are only sometimes reliable, can significantly affect the statistical properties of your data, and require regular adjustments if your distribution shifts.
|
|
256
256
|
Instead, we have focused on high-precision arithmetic over low-precision downcasted vectors.
|
|
257
|
-
The same index, and `add` and `search` operations will automatically down-cast or up-cast between `f64_t`, `f32_t`, `f16_t`, `i8_t`, and single-bit representations.
|
|
257
|
+
The same index, and `add` and `search` operations will automatically down-cast or up-cast between `f64_t`, `f32_t`, `f16_t`, `i8_t`, and single-bit `b1x8_t` representations.
|
|
258
258
|
You can use the following command to check, if hardware acceleration is enabled:
|
|
259
259
|
|
|
260
260
|
```sh
|
|
@@ -264,11 +264,19 @@ $ python -c 'from usearch.index import Index; print(Index(ndim=166, metric="tani
|
|
|
264
264
|
> ice
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
+
In most cases, it's recommended to use half-precision floating-point numbers on modern hardware.
|
|
268
|
+
When quantization is enabled, the "get"-like functions won't be able to recover the original data, so you may want to replicate the original vectors elsewhere.
|
|
269
|
+
When quantizing to `i8_t` integers, note that it's only valid for cosine-like metrics.
|
|
270
|
+
As part of the quantization process, the vectors are normalized to unit length and later scaled to [-127, 127] range to occupy the full 8-bit range.
|
|
271
|
+
When quantizing to `b1x8_t` single-bit representations, note that it's only valid for binary metrics like Jaccard, Hamming, etc.
|
|
272
|
+
As part of the quantization process, the scalar components greater than zero are set to `true`, and the rest to `false`.
|
|
273
|
+
|
|
274
|
+

|
|
275
|
+
|
|
267
276
|
Using smaller numeric types will save you RAM needed to store the vectors, but you can also compress the neighbors lists forming our proximity graphs.
|
|
268
277
|
By default, 32-bit `uint32_t` is used to enumerate those, which is not enough if you need to address over 4 Billion entries.
|
|
269
278
|
For such cases we provide a custom `uint40_t` type, that will still be 37.5% more space-efficient than the commonly used 8-byte integers, and will scale up to 1 Trillion entries.
|
|
270
279
|
|
|
271
|
-

|
|
272
280
|
|
|
273
281
|
## `Indexes` for Multi-Index Lookups
|
|
274
282
|
|
|
@@ -541,7 +549,7 @@ doi = {10.5281/zenodo.7949416},
|
|
|
541
549
|
author = {Vardanian, Ash},
|
|
542
550
|
title = {{USearch by Unum Cloud}},
|
|
543
551
|
url = {https://github.com/unum-cloud/usearch},
|
|
544
|
-
version = {2.13.
|
|
552
|
+
version = {2.13.5},
|
|
545
553
|
year = {2023},
|
|
546
554
|
month = oct,
|
|
547
555
|
}
|
package/c/usearch.h
CHANGED
|
@@ -72,6 +72,17 @@ USEARCH_EXPORT typedef struct usearch_init_options_t {
|
|
|
72
72
|
usearch_metric_t metric;
|
|
73
73
|
/**
|
|
74
74
|
* @brief The scalar kind used for quantization of vector data during indexing.
|
|
75
|
+
* In most cases, on modern hardware, it's recommended to use half-precision floating-point numbers.
|
|
76
|
+
* When quantization is enabled, the "get"-like functions won't be able to recover the original data,
|
|
77
|
+
* so you may want to replicate the original vectors elsewhere.
|
|
78
|
+
*
|
|
79
|
+
* Quantizing to integers is also possible, but it's important to note that it's only valid for cosine-like
|
|
80
|
+
* metrics. As part of the quantization process, the vectors are normalized to unit length and later scaled
|
|
81
|
+
* to @b [-127,127] range to occupy the full 8-bit range.
|
|
82
|
+
*
|
|
83
|
+
* Quantizing to 1-bit booleans is also possible, but it's only valid for binary metrics like Jaccard, Hamming,
|
|
84
|
+
* etc. As part of the quantization process, the scalar components greater than zero are set to `true`, and the
|
|
85
|
+
* rest to `false`.
|
|
75
86
|
*/
|
|
76
87
|
usearch_scalar_kind_t quantization;
|
|
77
88
|
/**
|
|
@@ -95,7 +95,7 @@ struct index_dense_head_result_t {
|
|
|
95
95
|
* @brief Configuration settings for the construction of dense
|
|
96
96
|
* equidimensional vector indexes.
|
|
97
97
|
*
|
|
98
|
-
* Unlike the underlying `index_gt` class,
|
|
98
|
+
* Unlike the underlying `index_gt` class, incorporates the
|
|
99
99
|
* `::expansion_add` and `::expansion_search` parameters passed
|
|
100
100
|
* separately for the lower-level engine.
|
|
101
101
|
*/
|
|
@@ -207,8 +207,11 @@ struct index_dense_metadata_result_t {
|
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* @brief Fixes serialized scalar-kind codes for pre-v2.10 versions, until we can upgrade to v3.
|
|
212
|
+
* The old enum `scalar_kind_t` is defined without explicit constants from 0.
|
|
213
|
+
*/
|
|
210
214
|
inline scalar_kind_t convert_pre_2_10_scalar_kind(scalar_kind_t scalar_kind) noexcept {
|
|
211
|
-
// The old enum `scalar_kind_t` is defined without explicit constants from 0
|
|
212
215
|
switch (static_cast<std::underlying_type<scalar_kind_t>::type>(scalar_kind)) {
|
|
213
216
|
case 0: return scalar_kind_t::unknown_k;
|
|
214
217
|
case 1: return scalar_kind_t::b1x8_k;
|
|
@@ -229,13 +232,15 @@ inline scalar_kind_t convert_pre_2_10_scalar_kind(scalar_kind_t scalar_kind) noe
|
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
234
|
|
|
235
|
+
/**
|
|
236
|
+
* @brief Fixes the metadata for pre-v2.10 versions, until we can upgrade to v3.
|
|
237
|
+
* Originates from: https://github.com/unum-cloud/usearch/issues/423
|
|
238
|
+
*/
|
|
232
239
|
inline void fix_pre_2_10_metadata(index_dense_head_t& head) {
|
|
233
|
-
// deal with incompatibility with pre-2.10 versions: https://github.com/unum-cloud/usearch/issues/423
|
|
234
240
|
if (head.version_major == 2 && head.version_minor < 10) {
|
|
235
241
|
head.kind_scalar = convert_pre_2_10_scalar_kind(head.kind_scalar);
|
|
236
242
|
head.kind_key = convert_pre_2_10_scalar_kind(head.kind_key);
|
|
237
243
|
head.kind_compressed_slot = convert_pre_2_10_scalar_kind(head.kind_compressed_slot);
|
|
238
|
-
// update minor versions so that no repeated conversion occurs
|
|
239
244
|
head.version_minor = 10;
|
|
240
245
|
head.version_patch = 0;
|
|
241
246
|
}
|
|
@@ -316,7 +321,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
|
|
|
316
321
|
/**
|
|
317
322
|
* @brief Extracts metadata from a pre-constructed index serialized into an in-memory buffer.
|
|
318
323
|
*/
|
|
319
|
-
inline index_dense_metadata_result_t index_dense_metadata_from_buffer(memory_mapped_file_t file,
|
|
324
|
+
inline index_dense_metadata_result_t index_dense_metadata_from_buffer(memory_mapped_file_t const& file,
|
|
320
325
|
std::size_t offset = 0) noexcept {
|
|
321
326
|
index_dense_metadata_result_t result;
|
|
322
327
|
|
|
@@ -324,7 +329,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_buffer(memory_map
|
|
|
324
329
|
if (offset + sizeof(index_dense_head_buffer_t) >= file.size())
|
|
325
330
|
return result.failed("End of file reached!");
|
|
326
331
|
|
|
327
|
-
byte_t
|
|
332
|
+
byte_t const* file_data = file.data() + offset;
|
|
328
333
|
std::size_t const file_size = file.size() - offset;
|
|
329
334
|
std::memcpy(&result.head_buffer, file_data, sizeof(index_dense_head_buffer_t));
|
|
330
335
|
|
|
@@ -70,7 +70,6 @@ struct uuid_t {
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
class f16_bits_t;
|
|
73
|
-
class i8_converted_t;
|
|
74
73
|
|
|
75
74
|
#if !USEARCH_USE_FP16LIB
|
|
76
75
|
#if USEARCH_USE_SIMSIMD
|
|
@@ -99,6 +98,9 @@ using i32_t = std::int32_t;
|
|
|
99
98
|
using i16_t = std::int16_t;
|
|
100
99
|
using i8_t = std::int8_t;
|
|
101
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @brief Enumerates the most commonly used distance metrics, mostly for dense vector representations.
|
|
103
|
+
*/
|
|
102
104
|
enum class metric_kind_t : std::uint8_t {
|
|
103
105
|
unknown_k = 0,
|
|
104
106
|
// Classics:
|
|
@@ -111,13 +113,19 @@ enum class metric_kind_t : std::uint8_t {
|
|
|
111
113
|
haversine_k = 'h',
|
|
112
114
|
divergence_k = 'd',
|
|
113
115
|
|
|
114
|
-
// Sets:
|
|
115
|
-
jaccard_k = 'j',
|
|
116
|
+
// Dense Sets:
|
|
116
117
|
hamming_k = 'b',
|
|
117
118
|
tanimoto_k = 't',
|
|
118
119
|
sorensen_k = 's',
|
|
120
|
+
|
|
121
|
+
// Sparse Sets:
|
|
122
|
+
jaccard_k = 'j',
|
|
119
123
|
};
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* @brief Enumerates the most commonly used scalar types, mostly for dense vector representations.
|
|
127
|
+
* Doesn't include logical types, like complex numbers or quaternions.
|
|
128
|
+
*/
|
|
121
129
|
enum class scalar_kind_t : std::uint8_t {
|
|
122
130
|
unknown_k = 0,
|
|
123
131
|
// Custom:
|
|
@@ -198,33 +206,63 @@ inline bool str_equals(char const* begin, std::size_t len, char const* other_beg
|
|
|
198
206
|
|
|
199
207
|
inline std::size_t bits_per_scalar(scalar_kind_t scalar_kind) noexcept {
|
|
200
208
|
switch (scalar_kind) {
|
|
209
|
+
case scalar_kind_t::uuid_k: return 128;
|
|
210
|
+
case scalar_kind_t::u40_k: return 40;
|
|
211
|
+
case scalar_kind_t::b1x8_k: return 1;
|
|
212
|
+
case scalar_kind_t::u64_k: return 64;
|
|
213
|
+
case scalar_kind_t::i64_k: return 64;
|
|
201
214
|
case scalar_kind_t::f64_k: return 64;
|
|
215
|
+
case scalar_kind_t::u32_k: return 32;
|
|
216
|
+
case scalar_kind_t::i32_k: return 32;
|
|
202
217
|
case scalar_kind_t::f32_k: return 32;
|
|
218
|
+
case scalar_kind_t::u16_k: return 16;
|
|
219
|
+
case scalar_kind_t::i16_k: return 16;
|
|
203
220
|
case scalar_kind_t::f16_k: return 16;
|
|
221
|
+
case scalar_kind_t::u8_k: return 8;
|
|
204
222
|
case scalar_kind_t::i8_k: return 8;
|
|
205
|
-
case scalar_kind_t::
|
|
223
|
+
case scalar_kind_t::f8_k: return 8;
|
|
206
224
|
default: return 0;
|
|
207
225
|
}
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
inline std::size_t bits_per_scalar_word(scalar_kind_t scalar_kind) noexcept {
|
|
211
229
|
switch (scalar_kind) {
|
|
230
|
+
case scalar_kind_t::uuid_k: return 128;
|
|
231
|
+
case scalar_kind_t::u40_k: return 40;
|
|
232
|
+
case scalar_kind_t::b1x8_k: return 8;
|
|
233
|
+
case scalar_kind_t::u64_k: return 64;
|
|
234
|
+
case scalar_kind_t::i64_k: return 64;
|
|
212
235
|
case scalar_kind_t::f64_k: return 64;
|
|
236
|
+
case scalar_kind_t::u32_k: return 32;
|
|
237
|
+
case scalar_kind_t::i32_k: return 32;
|
|
213
238
|
case scalar_kind_t::f32_k: return 32;
|
|
239
|
+
case scalar_kind_t::u16_k: return 16;
|
|
240
|
+
case scalar_kind_t::i16_k: return 16;
|
|
214
241
|
case scalar_kind_t::f16_k: return 16;
|
|
242
|
+
case scalar_kind_t::u8_k: return 8;
|
|
215
243
|
case scalar_kind_t::i8_k: return 8;
|
|
216
|
-
case scalar_kind_t::
|
|
244
|
+
case scalar_kind_t::f8_k: return 8;
|
|
217
245
|
default: return 0;
|
|
218
246
|
}
|
|
219
247
|
}
|
|
220
248
|
|
|
221
249
|
inline char const* scalar_kind_name(scalar_kind_t scalar_kind) noexcept {
|
|
222
250
|
switch (scalar_kind) {
|
|
251
|
+
case scalar_kind_t::uuid_k: return "uuid";
|
|
252
|
+
case scalar_kind_t::u40_k: return "u40";
|
|
253
|
+
case scalar_kind_t::b1x8_k: return "b1x8";
|
|
254
|
+
case scalar_kind_t::u64_k: return "u64";
|
|
255
|
+
case scalar_kind_t::i64_k: return "i64";
|
|
256
|
+
case scalar_kind_t::f64_k: return "f64";
|
|
257
|
+
case scalar_kind_t::u32_k: return "u32";
|
|
258
|
+
case scalar_kind_t::i32_k: return "i32";
|
|
223
259
|
case scalar_kind_t::f32_k: return "f32";
|
|
260
|
+
case scalar_kind_t::u16_k: return "u16";
|
|
261
|
+
case scalar_kind_t::i16_k: return "i16";
|
|
224
262
|
case scalar_kind_t::f16_k: return "f16";
|
|
225
|
-
case scalar_kind_t::
|
|
263
|
+
case scalar_kind_t::u8_k: return "u8";
|
|
226
264
|
case scalar_kind_t::i8_k: return "i8";
|
|
227
|
-
case scalar_kind_t::
|
|
265
|
+
case scalar_kind_t::f8_k: return "f8";
|
|
228
266
|
default: return "";
|
|
229
267
|
}
|
|
230
268
|
}
|
|
@@ -242,8 +280,8 @@ inline char const* metric_kind_name(metric_kind_t metric) noexcept {
|
|
|
242
280
|
case metric_kind_t::hamming_k: return "hamming";
|
|
243
281
|
case metric_kind_t::tanimoto_k: return "tanimoto";
|
|
244
282
|
case metric_kind_t::sorensen_k: return "sorensen";
|
|
283
|
+
default: return "";
|
|
245
284
|
}
|
|
246
|
-
return "";
|
|
247
285
|
}
|
|
248
286
|
inline expected_gt<scalar_kind_t> scalar_kind_from_name(char const* name, std::size_t len) {
|
|
249
287
|
expected_gt<scalar_kind_t> parsed;
|
|
@@ -333,8 +371,7 @@ class f16_bits_t {
|
|
|
333
371
|
inline operator float() const noexcept { return f16_to_f32(uint16_); }
|
|
334
372
|
inline explicit operator bool() const noexcept { return f16_to_f32(uint16_) > 0.5f; }
|
|
335
373
|
|
|
336
|
-
inline f16_bits_t(
|
|
337
|
-
inline f16_bits_t(std::int8_t v) noexcept : uint16_(v) {}
|
|
374
|
+
inline f16_bits_t(std::int8_t v) noexcept : uint16_(f32_to_f16(v)) {}
|
|
338
375
|
inline f16_bits_t(bool v) noexcept : uint16_(f32_to_f16(v)) {}
|
|
339
376
|
inline f16_bits_t(float v) noexcept : uint16_(f32_to_f16(v)) {}
|
|
340
377
|
inline f16_bits_t(double v) noexcept : uint16_(f32_to_f16(static_cast<float>(v))) {}
|
|
@@ -345,14 +382,14 @@ class f16_bits_t {
|
|
|
345
382
|
inline f16_bits_t operator-(f16_bits_t other) const noexcept { return {float(*this) - float(other)}; }
|
|
346
383
|
inline f16_bits_t operator*(f16_bits_t other) const noexcept { return {float(*this) * float(other)}; }
|
|
347
384
|
inline f16_bits_t operator/(f16_bits_t other) const noexcept { return {float(*this) / float(other)}; }
|
|
348
|
-
inline
|
|
349
|
-
inline
|
|
350
|
-
inline
|
|
351
|
-
inline
|
|
352
|
-
inline
|
|
353
|
-
inline
|
|
354
|
-
inline
|
|
355
|
-
inline
|
|
385
|
+
inline float operator+(float other) const noexcept { return float(*this) + other; }
|
|
386
|
+
inline float operator-(float other) const noexcept { return float(*this) - other; }
|
|
387
|
+
inline float operator*(float other) const noexcept { return float(*this) * other; }
|
|
388
|
+
inline float operator/(float other) const noexcept { return float(*this) / other; }
|
|
389
|
+
inline double operator+(double other) const noexcept { return float(*this) + other; }
|
|
390
|
+
inline double operator-(double other) const noexcept { return float(*this) - other; }
|
|
391
|
+
inline double operator*(double other) const noexcept { return float(*this) * other; }
|
|
392
|
+
inline double operator/(double other) const noexcept { return float(*this) / other; }
|
|
356
393
|
|
|
357
394
|
inline f16_bits_t& operator+=(float v) noexcept {
|
|
358
395
|
uint16_ = f32_to_f16(v + f16_to_f32(uint16_));
|
|
@@ -907,7 +944,7 @@ template <> struct cast_gt<b1x8_t, b1x8_t> {
|
|
|
907
944
|
static bool try_(byte_t const*, std::size_t, byte_t*) noexcept { return false; }
|
|
908
945
|
};
|
|
909
946
|
|
|
910
|
-
template <typename from_scalar_at> struct
|
|
947
|
+
template <typename from_scalar_at> struct cast_to_b1x8_gt {
|
|
911
948
|
inline static bool try_(byte_t const* input, std::size_t dim, byte_t* output) noexcept {
|
|
912
949
|
from_scalar_at const* typed_input = reinterpret_cast<from_scalar_at const*>(input);
|
|
913
950
|
unsigned char* typed_output = reinterpret_cast<unsigned char*>(output);
|
|
@@ -928,7 +965,7 @@ template <typename from_scalar_at> struct cast_gt<from_scalar_at, b1x8_t> {
|
|
|
928
965
|
}
|
|
929
966
|
};
|
|
930
967
|
|
|
931
|
-
template <typename to_scalar_at> struct
|
|
968
|
+
template <typename to_scalar_at> struct cast_from_b1x8_gt {
|
|
932
969
|
static bool try_(byte_t const* input, std::size_t dim, byte_t* output) noexcept {
|
|
933
970
|
unsigned char const* typed_input = reinterpret_cast<unsigned char const*>(input);
|
|
934
971
|
to_scalar_at* typed_output = reinterpret_cast<to_scalar_at*>(output);
|
|
@@ -940,53 +977,55 @@ template <typename to_scalar_at> struct cast_gt<b1x8_t, to_scalar_at> {
|
|
|
940
977
|
}
|
|
941
978
|
};
|
|
942
979
|
|
|
943
|
-
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
980
|
+
template <typename from_scalar_at> struct cast_to_i8_gt {
|
|
981
|
+
inline static bool try_(byte_t const* input, std::size_t dim, byte_t* output) noexcept {
|
|
982
|
+
from_scalar_at const* typed_input = reinterpret_cast<from_scalar_at const*>(input);
|
|
983
|
+
std::int8_t* typed_output = reinterpret_cast<std::int8_t*>(output);
|
|
984
|
+
// Unlike other casting mechanisms, switching to small range integers is a two step procedure.
|
|
985
|
+
// First we want to estimate the magnitude of the vector to scale into [-1.0, 1.0] interval,
|
|
986
|
+
// instead of clamping. And then we scale the values into the [-127, 127] range.
|
|
987
|
+
// ! This makes an assumption, that the distance metric is dot-product-like, which may not
|
|
988
|
+
// ! be true in many cases, so it's recommended to avoid automatic casting from floats to
|
|
989
|
+
// ! integers.
|
|
990
|
+
double magnitude = 0.0;
|
|
991
|
+
for (std::size_t i = 0; i != dim; ++i)
|
|
992
|
+
magnitude += (double)typed_input[i] * (double)typed_input[i];
|
|
993
|
+
magnitude = std::sqrt(magnitude);
|
|
994
|
+
for (std::size_t i = 0; i != dim; ++i)
|
|
995
|
+
typed_output[i] =
|
|
996
|
+
static_cast<std::int8_t>(usearch::clamp<double>(typed_input[i] * 127.0 / magnitude, -127.0, 127.0));
|
|
997
|
+
return true;
|
|
998
|
+
}
|
|
999
|
+
};
|
|
949
1000
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
inline i8_converted_t(i8_converted_t&&) = default;
|
|
959
|
-
inline i8_converted_t& operator=(i8_converted_t&&) = default;
|
|
960
|
-
inline i8_converted_t(i8_converted_t const&) = default;
|
|
961
|
-
inline i8_converted_t& operator=(i8_converted_t const&) = default;
|
|
962
|
-
|
|
963
|
-
inline operator f16_t() const noexcept { return static_cast<f16_t>(f32_t(int8_) / divisor_k); }
|
|
964
|
-
inline operator f32_t() const noexcept { return f32_t(int8_) / divisor_k; }
|
|
965
|
-
inline operator f64_t() const noexcept { return f64_t(int8_) / divisor_k; }
|
|
966
|
-
inline explicit operator bool() const noexcept { return int8_ > (max_k / 2); }
|
|
967
|
-
inline explicit operator std::int8_t() const noexcept { return int8_; }
|
|
968
|
-
inline explicit operator std::int16_t() const noexcept { return int8_; }
|
|
969
|
-
inline explicit operator std::int32_t() const noexcept { return int8_; }
|
|
970
|
-
inline explicit operator std::int64_t() const noexcept { return int8_; }
|
|
971
|
-
|
|
972
|
-
// Even with `f16_t` arguments we may want to use `f32_t` for clamping and comparions.
|
|
973
|
-
inline i8_converted_t(f16_t v)
|
|
974
|
-
: int8_(static_cast<std::int8_t>(usearch::clamp<f32_t>(v * divisor_k, min_k, max_k))) {}
|
|
975
|
-
inline i8_converted_t(f32_t v)
|
|
976
|
-
: int8_(static_cast<std::int8_t>(usearch::clamp<f32_t>(v * divisor_k, min_k, max_k))) {}
|
|
977
|
-
inline i8_converted_t(f64_t v)
|
|
978
|
-
: int8_(static_cast<std::int8_t>(usearch::clamp<f64_t>(v * divisor_k, min_k, max_k))) {}
|
|
1001
|
+
template <typename to_scalar_at> struct cast_from_i8_gt {
|
|
1002
|
+
static bool try_(byte_t const* input, std::size_t dim, byte_t* output) noexcept {
|
|
1003
|
+
std::int8_t const* typed_input = reinterpret_cast<std::int8_t const*>(input);
|
|
1004
|
+
to_scalar_at* typed_output = reinterpret_cast<to_scalar_at*>(output);
|
|
1005
|
+
for (std::size_t i = 0; i != dim; ++i)
|
|
1006
|
+
typed_output[i] = static_cast<to_scalar_at>(typed_input[i]) / 127.f;
|
|
1007
|
+
return true;
|
|
1008
|
+
}
|
|
979
1009
|
};
|
|
980
1010
|
|
|
981
|
-
|
|
1011
|
+
template <> struct cast_gt<i8_t, f16_t> : public cast_from_i8_gt<f16_t> {};
|
|
1012
|
+
template <> struct cast_gt<i8_t, f32_t> : public cast_from_i8_gt<f32_t> {};
|
|
1013
|
+
template <> struct cast_gt<i8_t, f64_t> : public cast_from_i8_gt<f64_t> {};
|
|
1014
|
+
|
|
1015
|
+
template <> struct cast_gt<f16_t, i8_t> : public cast_to_i8_gt<f16_t> {};
|
|
1016
|
+
template <> struct cast_gt<f32_t, i8_t> : public cast_to_i8_gt<f32_t> {};
|
|
1017
|
+
template <> struct cast_gt<f64_t, i8_t> : public cast_to_i8_gt<f64_t> {};
|
|
1018
|
+
|
|
1019
|
+
template <> struct cast_gt<b1x8_t, f16_t> : public cast_from_b1x8_gt<f16_t> {};
|
|
1020
|
+
template <> struct cast_gt<b1x8_t, f32_t> : public cast_from_b1x8_gt<f32_t> {};
|
|
1021
|
+
template <> struct cast_gt<b1x8_t, f64_t> : public cast_from_b1x8_gt<f64_t> {};
|
|
982
1022
|
|
|
983
|
-
template <> struct cast_gt<
|
|
984
|
-
template <> struct cast_gt<
|
|
985
|
-
template <> struct cast_gt<
|
|
1023
|
+
template <> struct cast_gt<f16_t, b1x8_t> : public cast_to_b1x8_gt<f16_t> {};
|
|
1024
|
+
template <> struct cast_gt<f32_t, b1x8_t> : public cast_to_b1x8_gt<f32_t> {};
|
|
1025
|
+
template <> struct cast_gt<f64_t, b1x8_t> : public cast_to_b1x8_gt<f64_t> {};
|
|
986
1026
|
|
|
987
|
-
template <> struct cast_gt<
|
|
988
|
-
template <> struct cast_gt<
|
|
989
|
-
template <> struct cast_gt<f64_t, i8_t> : public cast_gt<f64_t, i8_converted_t> {};
|
|
1027
|
+
template <> struct cast_gt<b1x8_t, i8_t> : public cast_from_b1x8_gt<i8_t> {};
|
|
1028
|
+
template <> struct cast_gt<i8_t, b1x8_t> : public cast_to_b1x8_gt<i8_t> {};
|
|
990
1029
|
|
|
991
1030
|
/* Don't complain if the vectorization of the inner loops fails:
|
|
992
1031
|
*
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|