usearch 2.16.7 → 2.16.9
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 +2 -1
- package/include/usearch/index.hpp +1 -1
- 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/CONTRIBUTING.md +69 -12
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +253 -40
- package/simsimd/c/lib.c +114 -109
- package/simsimd/include/simsimd/binary.h +33 -17
- package/simsimd/include/simsimd/curved.h +867 -129
- package/simsimd/include/simsimd/dot.h +562 -569
- package/simsimd/include/simsimd/probability.h +14 -9
- package/simsimd/include/simsimd/simsimd.h +102 -46
- package/simsimd/include/simsimd/spatial.h +28 -18
- package/simsimd/include/simsimd/types.h +24 -0
- package/simsimd/javascript/fallback.ts +26 -15
- package/simsimd/javascript/simsimd.ts +1 -1
- package/simsimd/package-lock.json +10 -9
- package/simsimd/package.json +2 -2
- package/simsimd/rust/lib.rs +218 -5
- package/simsimd/scripts/bench.cxx +156 -97
- package/simsimd/scripts/test.c +14 -8
- package/simsimd/scripts/test.mjs +158 -145
- package/simsimd/scripts/test.py +71 -52
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#include <array> // `std::array`
|
|
1
2
|
#include <cmath> // `std::sqrt`
|
|
2
3
|
#include <cstdlib> // `std::aligned_alloc`
|
|
3
4
|
#include <cstring> // `std::memcpy`
|
|
@@ -34,33 +35,33 @@ constexpr simsimd_distance_t signaling_distance = std::numeric_limits<simsimd_di
|
|
|
34
35
|
/// For sub-byte data types
|
|
35
36
|
constexpr std::size_t dense_dimensions = 1536;
|
|
36
37
|
/// Has quadratic impact on the number of operations
|
|
37
|
-
constexpr std::size_t curved_dimensions =
|
|
38
|
+
constexpr std::size_t curved_dimensions = 8;
|
|
38
39
|
|
|
39
40
|
namespace bm = benchmark;
|
|
40
41
|
|
|
41
42
|
// clang-format off
|
|
42
|
-
template <simsimd_datatype_t> struct datatype_enum_to_type_gt { using value_t = void; };
|
|
43
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f64_k> { using value_t = simsimd_f64_t; };
|
|
44
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f32_k> { using value_t = simsimd_f32_t; };
|
|
45
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f16_k> { using value_t = simsimd_f16_t; };
|
|
46
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_bf16_k> { using value_t = simsimd_bf16_t; };
|
|
47
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f64c_k> { using value_t = simsimd_f64_t; };
|
|
48
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f32c_k> { using value_t = simsimd_f32_t; };
|
|
49
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f16c_k> { using value_t = simsimd_f16_t; };
|
|
50
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_bf16c_k> { using value_t = simsimd_bf16_t; };
|
|
51
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_b8_k> { using value_t = simsimd_b8_t; };
|
|
52
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i8_k> { using value_t = simsimd_i8_t; };
|
|
53
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u8_k> { using value_t = simsimd_u8_t; };
|
|
54
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i16_k> { using value_t = simsimd_i16_t; };
|
|
55
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u16_k> { using value_t = simsimd_u16_t; };
|
|
56
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i32_k> { using value_t = simsimd_i32_t; };
|
|
57
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u32_k> { using value_t = simsimd_u32_t; };
|
|
58
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i64_k> { using value_t = simsimd_i64_t; };
|
|
59
|
-
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u64_k> { using value_t = simsimd_u64_t; };
|
|
43
|
+
template <simsimd_datatype_t> struct datatype_enum_to_type_gt { using value_t = void; using scalar_t = void; static constexpr std::size_t components_k = 1; };
|
|
44
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f64_k> { using value_t = simsimd_f64_t; using scalar_t = simsimd_f64_t; static constexpr std::size_t components_k = 1; };
|
|
45
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f32_k> { using value_t = simsimd_f32_t; using scalar_t = simsimd_f32_t; static constexpr std::size_t components_k = 1; };
|
|
46
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f16_k> { using value_t = simsimd_f16_t; using scalar_t = simsimd_f16_t; static constexpr std::size_t components_k = 1; };
|
|
47
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_bf16_k> { using value_t = simsimd_bf16_t; using scalar_t = simsimd_bf16_t; static constexpr std::size_t components_k = 1; };
|
|
48
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f64c_k> { using value_t = simsimd_f64c_t; using scalar_t = simsimd_f64_t; static constexpr std::size_t components_k = 2; };
|
|
49
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f32c_k> { using value_t = simsimd_f32c_t; using scalar_t = simsimd_f32_t; static constexpr std::size_t components_k = 2; };
|
|
50
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_f16c_k> { using value_t = simsimd_f16c_t; using scalar_t = simsimd_f16_t; static constexpr std::size_t components_k = 2; };
|
|
51
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_bf16c_k> { using value_t = simsimd_bf16c_t; using scalar_t = simsimd_bf16_t; static constexpr std::size_t components_k = 2; };
|
|
52
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_b8_k> { using value_t = simsimd_b8_t; using scalar_t = simsimd_b8_t; static constexpr std::size_t components_k = 1; };
|
|
53
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i8_k> { using value_t = simsimd_i8_t; using scalar_t = simsimd_i8_t; static constexpr std::size_t components_k = 1; };
|
|
54
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u8_k> { using value_t = simsimd_u8_t; using scalar_t = simsimd_u8_t; static constexpr std::size_t components_k = 1; };
|
|
55
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i16_k> { using value_t = simsimd_i16_t; using scalar_t = simsimd_i16_t; static constexpr std::size_t components_k = 1; };
|
|
56
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u16_k> { using value_t = simsimd_u16_t; using scalar_t = simsimd_u16_t; static constexpr std::size_t components_k = 1; };
|
|
57
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i32_k> { using value_t = simsimd_i32_t; using scalar_t = simsimd_i32_t; static constexpr std::size_t components_k = 1; };
|
|
58
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u32_k> { using value_t = simsimd_u32_t; using scalar_t = simsimd_u32_t; static constexpr std::size_t components_k = 1; };
|
|
59
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_i64_k> { using value_t = simsimd_i64_t; using scalar_t = simsimd_i64_t; static constexpr std::size_t components_k = 1; };
|
|
60
|
+
template <> struct datatype_enum_to_type_gt<simsimd_datatype_u64_k> { using value_t = simsimd_u64_t; using scalar_t = simsimd_u64_t; static constexpr std::size_t components_k = 1; };
|
|
60
61
|
// clang-format on
|
|
61
62
|
|
|
62
63
|
template <std::size_t multiple>
|
|
63
|
-
std::size_t divide_round_up(std::size_t n) {
|
|
64
|
+
constexpr std::size_t divide_round_up(std::size_t n) {
|
|
64
65
|
return ((n + multiple - 1) / multiple) * multiple;
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -70,8 +71,11 @@ std::size_t divide_round_up(std::size_t n) {
|
|
|
70
71
|
*/
|
|
71
72
|
template <simsimd_datatype_t datatype_ak>
|
|
72
73
|
struct vector_gt {
|
|
73
|
-
using
|
|
74
|
-
using
|
|
74
|
+
using datatype_reflection_t = datatype_enum_to_type_gt<datatype_ak>;
|
|
75
|
+
using scalar_t = typename datatype_reflection_t::scalar_t;
|
|
76
|
+
using value_t = typename datatype_reflection_t::value_t;
|
|
77
|
+
static constexpr std::size_t components_k = datatype_reflection_t::components_k;
|
|
78
|
+
|
|
75
79
|
static constexpr bool is_integral =
|
|
76
80
|
datatype_ak == datatype_ak == simsimd_datatype_b8_k || //
|
|
77
81
|
datatype_ak == simsimd_datatype_i8_k || datatype_ak == simsimd_datatype_u8_k || //
|
|
@@ -80,49 +84,54 @@ struct vector_gt {
|
|
|
80
84
|
datatype_ak == simsimd_datatype_i64_k || datatype_ak == simsimd_datatype_u64_k;
|
|
81
85
|
static constexpr std::size_t cacheline_length = 64;
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
std::size_t
|
|
87
|
+
value_t *values_ptr_ = nullptr;
|
|
88
|
+
std::size_t values_count_ = 0;
|
|
85
89
|
|
|
86
90
|
vector_gt() = default;
|
|
87
|
-
vector_gt(std::size_t
|
|
88
|
-
:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (!
|
|
91
|
+
vector_gt(std::size_t values_count) noexcept(false)
|
|
92
|
+
: values_count_(values_count),
|
|
93
|
+
values_ptr_(static_cast<value_t *>(std::aligned_alloc(
|
|
94
|
+
cacheline_length, divide_round_up<cacheline_length>(values_count * sizeof(value_t))))) {
|
|
95
|
+
if (!values_ptr_) throw std::bad_alloc();
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
~vector_gt() noexcept { std::free(
|
|
98
|
+
~vector_gt() noexcept { std::free(values_ptr_); }
|
|
95
99
|
|
|
96
|
-
vector_gt(vector_gt const &other) : vector_gt(other.
|
|
97
|
-
std::memcpy(
|
|
100
|
+
vector_gt(vector_gt const &other) : vector_gt(other.size()) {
|
|
101
|
+
std::memcpy(values_ptr_, other.values_ptr_, divide_round_up<cacheline_length>(values_count_ * sizeof(value_t)));
|
|
98
102
|
}
|
|
99
103
|
vector_gt &operator=(vector_gt const &other) {
|
|
100
104
|
if (this != &other) {
|
|
101
|
-
if (
|
|
102
|
-
std::free(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
cacheline_length, divide_round_up<cacheline_length>(
|
|
106
|
-
if (!
|
|
105
|
+
if (values_count_ != other.size()) {
|
|
106
|
+
std::free(values_ptr_);
|
|
107
|
+
values_count_ = other.size();
|
|
108
|
+
values_ptr_ = static_cast<value_t *>(std::aligned_alloc(
|
|
109
|
+
cacheline_length, divide_round_up<cacheline_length>(values_count_ * sizeof(value_t))));
|
|
110
|
+
if (!values_ptr_) throw std::bad_alloc();
|
|
107
111
|
}
|
|
108
|
-
std::memcpy(
|
|
112
|
+
std::memcpy(values_ptr_, other.values_ptr_,
|
|
113
|
+
divide_round_up<cacheline_length>(values_count_ * sizeof(value_t)));
|
|
109
114
|
}
|
|
110
115
|
return *this;
|
|
111
116
|
}
|
|
112
117
|
|
|
113
|
-
|
|
118
|
+
value_t *data() noexcept { return values_ptr_; }
|
|
119
|
+
value_t const *data() const noexcept { return values_ptr_; }
|
|
120
|
+
std::size_t size() const noexcept { return values_count_; }
|
|
114
121
|
std::size_t size_bytes() const noexcept {
|
|
115
|
-
return divide_round_up<cacheline_length>(
|
|
122
|
+
return divide_round_up<cacheline_length>(values_count_ * sizeof(value_t));
|
|
116
123
|
}
|
|
117
|
-
|
|
118
|
-
scalar_t
|
|
124
|
+
|
|
125
|
+
scalar_t *data_scalars() noexcept { return reinterpret_cast<scalar_t *>(data()); }
|
|
126
|
+
scalar_t const *data_scalars() const noexcept { return reinterpret_cast<scalar_t *>(data()); }
|
|
127
|
+
std::size_t size_scalars() const noexcept { return size() * components_k; }
|
|
119
128
|
|
|
120
129
|
/**
|
|
121
130
|
* @brief Broadcast a scalar value to all elements of the vector.
|
|
122
131
|
* @param v The scalar value to broadcast.
|
|
123
132
|
*/
|
|
124
133
|
void set(scalar_t v) noexcept {
|
|
125
|
-
for (std::size_t i = 0; i !=
|
|
134
|
+
for (std::size_t i = 0; i != size_scalars(); ++i) data_scalars()[i] = v;
|
|
126
135
|
}
|
|
127
136
|
|
|
128
137
|
/**
|
|
@@ -193,24 +202,24 @@ struct vector_gt {
|
|
|
193
202
|
if constexpr (is_integral) {
|
|
194
203
|
std::uniform_int_distribution<scalar_t> distribution(std::numeric_limits<scalar_t>::min(),
|
|
195
204
|
std::numeric_limits<scalar_t>::max());
|
|
196
|
-
for (std::size_t i = 0; i !=
|
|
205
|
+
for (std::size_t i = 0; i != size_scalars(); ++i) { data_scalars()[i] = distribution(generator); }
|
|
197
206
|
}
|
|
198
207
|
else {
|
|
199
208
|
// Using non-uniform distribution helps detect tail errors
|
|
200
209
|
std::normal_distribution<double> distribution(0.1, 1.0);
|
|
201
210
|
double squared_sum = 0.0;
|
|
202
|
-
for (std::size_t i = 0; i !=
|
|
211
|
+
for (std::size_t i = 0; i != size_scalars(); ++i) {
|
|
203
212
|
double a_i = distribution(generator);
|
|
204
213
|
squared_sum += a_i * a_i;
|
|
205
|
-
compress(a_i,
|
|
214
|
+
compress(a_i, data_scalars()[i]);
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
// Normalize the vectors:
|
|
209
218
|
squared_sum = std::sqrt(squared_sum);
|
|
210
|
-
for (std::size_t i = 0; i !=
|
|
211
|
-
compress(uncompress(
|
|
219
|
+
for (std::size_t i = 0; i != size_scalars(); ++i) {
|
|
220
|
+
compress(uncompress(data_scalars()[i]) / squared_sum, data_scalars()[i]);
|
|
212
221
|
// Zero out NaNs
|
|
213
|
-
if (std::isnan(uncompress(
|
|
222
|
+
if (std::isnan(uncompress(data_scalars()[i]))) data_scalars()[i] = 0;
|
|
214
223
|
}
|
|
215
224
|
}
|
|
216
225
|
}
|
|
@@ -227,7 +236,7 @@ struct vectors_pair_gt {
|
|
|
227
236
|
|
|
228
237
|
vectors_pair_gt() noexcept = default;
|
|
229
238
|
vectors_pair_gt(std::size_t dimensions) noexcept : a(dimensions), b(dimensions) {}
|
|
230
|
-
vectors_pair_gt(std::size_t
|
|
239
|
+
vectors_pair_gt(std::size_t size_a, std::size_t size_b) noexcept : a(size_a), b(size_b) {}
|
|
231
240
|
vectors_pair_gt(vectors_pair_gt const &other) noexcept(false) : a(other.a), b(other.b) {}
|
|
232
241
|
vectors_pair_gt &operator=(vectors_pair_gt const &other) noexcept(false) {
|
|
233
242
|
if (this != &other) a = other.a, b = other.b;
|
|
@@ -254,14 +263,14 @@ void measure_dense(bm::State &state, metric_at metric, metric_at baseline, std::
|
|
|
254
263
|
// Output for real vectors have a single dimensions.
|
|
255
264
|
// Output for complex vectors have two dimensions.
|
|
256
265
|
simsimd_distance_t results[2] = {signaling_distance, signaling_distance};
|
|
257
|
-
baseline(pair.a.data(), pair.b.data(), pair.a.
|
|
266
|
+
baseline(pair.a.data(), pair.b.data(), pair.a.size(), &results[0]);
|
|
258
267
|
return results[0];
|
|
259
268
|
};
|
|
260
269
|
auto call_contender = [&](pair_t &pair) -> double {
|
|
261
270
|
// Output for real vectors have a single dimensions.
|
|
262
271
|
// Output for complex vectors have two dimensions.
|
|
263
272
|
simsimd_distance_t results[2] = {signaling_distance, signaling_distance};
|
|
264
|
-
metric(pair.a.data(), pair.b.data(), pair.a.
|
|
273
|
+
metric(pair.a.data(), pair.b.data(), pair.a.size(), &results[0]);
|
|
265
274
|
return results[0];
|
|
266
275
|
};
|
|
267
276
|
|
|
@@ -319,14 +328,14 @@ void measure_curved(bm::State &state, metric_at metric, metric_at baseline, std:
|
|
|
319
328
|
using vector_t = typename pair_at::vector_t;
|
|
320
329
|
|
|
321
330
|
auto call_baseline = [&](pair_t const &pair, vector_t const &tensor) -> double {
|
|
322
|
-
simsimd_distance_t
|
|
323
|
-
baseline(pair.a.data(), pair.b.data(), tensor.data(), pair.a.
|
|
324
|
-
return
|
|
331
|
+
simsimd_distance_t results[2] = {signaling_distance, 0};
|
|
332
|
+
baseline(pair.a.data(), pair.b.data(), tensor.data(), pair.a.size(), &results[0]);
|
|
333
|
+
return results[0] + results[1];
|
|
325
334
|
};
|
|
326
335
|
auto call_contender = [&](pair_t const &pair, vector_t const &tensor) -> double {
|
|
327
|
-
simsimd_distance_t
|
|
328
|
-
metric(pair.a.data(), pair.b.data(), tensor.data(), pair.a.
|
|
329
|
-
return
|
|
336
|
+
simsimd_distance_t results[2] = {signaling_distance, 0};
|
|
337
|
+
metric(pair.a.data(), pair.b.data(), tensor.data(), pair.a.size(), &results[0]);
|
|
338
|
+
return results[0] + results[1];
|
|
330
339
|
};
|
|
331
340
|
|
|
332
341
|
// Let's average the distance results over many pairs.
|
|
@@ -379,13 +388,13 @@ void measure_curved(bm::State &state, metric_at metric, metric_at baseline, std:
|
|
|
379
388
|
* @param state The benchmark state object provided by Google Benchmark.
|
|
380
389
|
* @param metric The metric function to benchmark.
|
|
381
390
|
* @param baseline The baseline function to compare against.
|
|
382
|
-
* @param
|
|
383
|
-
* @param
|
|
391
|
+
* @param size_a The number of elements in the smaller vector.
|
|
392
|
+
* @param size_b The number of elements in the larger vector.
|
|
384
393
|
* @param intersection_size The expected number of common scalars between the vectors.
|
|
385
394
|
*/
|
|
386
395
|
template <typename pair_at, typename metric_at = void>
|
|
387
|
-
void measure_sparse(bm::State &state, metric_at metric, metric_at baseline, std::size_t
|
|
388
|
-
std::size_t
|
|
396
|
+
void measure_sparse(bm::State &state, metric_at metric, metric_at baseline, std::size_t size_a, std::size_t size_b,
|
|
397
|
+
std::size_t intersection_size) {
|
|
389
398
|
|
|
390
399
|
using pair_t = pair_at;
|
|
391
400
|
using vector_t = typename pair_at::vector_t;
|
|
@@ -393,12 +402,12 @@ void measure_sparse(bm::State &state, metric_at metric, metric_at baseline, std:
|
|
|
393
402
|
|
|
394
403
|
auto call_baseline = [&](pair_t &pair) -> double {
|
|
395
404
|
simsimd_distance_t result = std::numeric_limits<simsimd_distance_t>::signaling_NaN();
|
|
396
|
-
baseline(pair.a.data(), pair.b.data(), pair.a.
|
|
405
|
+
baseline(pair.a.data(), pair.b.data(), pair.a.size(), pair.b.size(), &result);
|
|
397
406
|
return result;
|
|
398
407
|
};
|
|
399
408
|
auto call_contender = [&](pair_t &pair) -> double {
|
|
400
409
|
simsimd_distance_t result = std::numeric_limits<simsimd_distance_t>::signaling_NaN();
|
|
401
|
-
metric(pair.a.data(), pair.b.data(), pair.a.
|
|
410
|
+
metric(pair.a.data(), pair.b.data(), pair.a.size(), pair.b.size(), &result);
|
|
402
411
|
return result;
|
|
403
412
|
};
|
|
404
413
|
|
|
@@ -416,37 +425,37 @@ void measure_sparse(bm::State &state, metric_at metric, metric_at baseline, std:
|
|
|
416
425
|
// We need to ensure that the intersection is of the expected size.
|
|
417
426
|
std::unordered_set<scalar_t> intersection_set, unique_a, unique_b;
|
|
418
427
|
intersection_set.reserve(intersection_size);
|
|
419
|
-
unique_a.reserve(
|
|
420
|
-
unique_b.reserve(
|
|
428
|
+
unique_a.reserve(size_a - intersection_size);
|
|
429
|
+
unique_b.reserve(size_b - intersection_size);
|
|
421
430
|
|
|
422
431
|
for (auto &pair : pairs) {
|
|
423
|
-
pair.a = vector_t(
|
|
424
|
-
pair.b = vector_t(
|
|
432
|
+
pair.a = vector_t(size_a);
|
|
433
|
+
pair.b = vector_t(size_b);
|
|
425
434
|
|
|
426
435
|
// Step 1: Generate intersection set
|
|
427
436
|
intersection_set.clear();
|
|
428
437
|
while (intersection_set.size() < intersection_size) intersection_set.insert(distribution(generator));
|
|
429
438
|
|
|
430
439
|
unique_a.clear();
|
|
431
|
-
while (unique_a.size() <
|
|
440
|
+
while (unique_a.size() < size_a - intersection_size) {
|
|
432
441
|
scalar_t element = distribution(generator);
|
|
433
442
|
if (intersection_set.find(element) == intersection_set.end()) unique_a.insert(element);
|
|
434
443
|
}
|
|
435
444
|
|
|
436
445
|
unique_b.clear();
|
|
437
|
-
while (unique_b.size() <
|
|
446
|
+
while (unique_b.size() < size_b - intersection_size) {
|
|
438
447
|
scalar_t element = distribution(generator);
|
|
439
448
|
if (intersection_set.find(element) == intersection_set.end() && unique_a.find(element) == unique_a.end())
|
|
440
449
|
unique_b.insert(element);
|
|
441
450
|
}
|
|
442
451
|
|
|
443
452
|
// Step 2: Merge and sort
|
|
444
|
-
std::copy(intersection_set.begin(), intersection_set.end(), pair.a.
|
|
445
|
-
std::copy(intersection_set.begin(), intersection_set.end(), pair.b.
|
|
446
|
-
std::copy(unique_a.begin(), unique_a.end(), pair.a.
|
|
447
|
-
std::copy(unique_b.begin(), unique_b.end(), pair.b.
|
|
448
|
-
std::sort(pair.a.
|
|
449
|
-
std::sort(pair.b.
|
|
453
|
+
std::copy(intersection_set.begin(), intersection_set.end(), pair.a.values_ptr_);
|
|
454
|
+
std::copy(intersection_set.begin(), intersection_set.end(), pair.b.values_ptr_);
|
|
455
|
+
std::copy(unique_a.begin(), unique_a.end(), pair.a.values_ptr_ + intersection_size);
|
|
456
|
+
std::copy(unique_b.begin(), unique_b.end(), pair.b.values_ptr_ + intersection_size);
|
|
457
|
+
std::sort(pair.a.values_ptr_, pair.a.values_ptr_ + size_a);
|
|
458
|
+
std::sort(pair.b.values_ptr_, pair.b.values_ptr_ + size_b);
|
|
450
459
|
}
|
|
451
460
|
|
|
452
461
|
// Initialize the output buffers for distance calculations.
|
|
@@ -505,15 +514,15 @@ void measure_fma(bm::State &state, kernel_at kernel, kernel_at baseline, l2_metr
|
|
|
505
514
|
|
|
506
515
|
auto call_baseline = [&](vector_t const &a, vector_t const &b, vector_t const &c, vector_t &d) {
|
|
507
516
|
if constexpr (function_args_count(kernel_at {}) == 6) {
|
|
508
|
-
baseline(a.data(), c.data(), a.
|
|
517
|
+
baseline(a.data(), c.data(), a.size(), alpha, beta, d.data());
|
|
509
518
|
}
|
|
510
|
-
else { baseline(a.data(), b.data(), c.data(), a.
|
|
519
|
+
else { baseline(a.data(), b.data(), c.data(), a.size(), alpha, beta, d.data()); }
|
|
511
520
|
};
|
|
512
521
|
auto call_contender = [&](vector_t const &a, vector_t const &b, vector_t const &c, vector_t &d) {
|
|
513
522
|
if constexpr (function_args_count(kernel_at {}) == 6) {
|
|
514
|
-
kernel(a.data(), c.data(), a.
|
|
523
|
+
kernel(a.data(), c.data(), a.size(), alpha, beta, d.data());
|
|
515
524
|
}
|
|
516
|
-
else { kernel(a.data(), b.data(), c.data(), a.
|
|
525
|
+
else { kernel(a.data(), b.data(), c.data(), a.size(), alpha, beta, d.data()); }
|
|
517
526
|
};
|
|
518
527
|
|
|
519
528
|
// Let's average the distance results over many quads.
|
|
@@ -634,26 +643,61 @@ void dot_f64_blas(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t
|
|
|
634
643
|
*result = cblas_ddot((int)n, a, 1, b, 1);
|
|
635
644
|
}
|
|
636
645
|
|
|
637
|
-
void dot_f32c_blas(
|
|
646
|
+
void dot_f32c_blas(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_size_t n, simsimd_distance_t *result) {
|
|
638
647
|
simsimd_f32_t f32_result[2] = {0, 0};
|
|
639
|
-
cblas_cdotu_sub((int)n
|
|
648
|
+
cblas_cdotu_sub((int)n, (simsimd_f32_t const *)a, 1, (simsimd_f32_t const *)b, 1, f32_result);
|
|
640
649
|
result[0] = f32_result[0];
|
|
641
650
|
result[1] = f32_result[1];
|
|
642
651
|
}
|
|
643
652
|
|
|
644
|
-
void dot_f64c_blas(
|
|
645
|
-
cblas_zdotu_sub((int)n
|
|
653
|
+
void dot_f64c_blas(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_size_t n, simsimd_distance_t *result) {
|
|
654
|
+
cblas_zdotu_sub((int)n, (simsimd_f64_t const *)a, 1, (simsimd_f64_t const *)b, 1, result);
|
|
646
655
|
}
|
|
647
656
|
|
|
648
|
-
void vdot_f32c_blas(
|
|
657
|
+
void vdot_f32c_blas(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_size_t n, simsimd_distance_t *result) {
|
|
649
658
|
simsimd_f32_t f32_result[2] = {0, 0};
|
|
650
|
-
cblas_cdotc_sub((int)n
|
|
659
|
+
cblas_cdotc_sub((int)n, (simsimd_f32_t const *)a, 1, (simsimd_f32_t const *)b, 1, f32_result);
|
|
651
660
|
result[0] = f32_result[0];
|
|
652
661
|
result[1] = f32_result[1];
|
|
653
662
|
}
|
|
654
663
|
|
|
655
|
-
void vdot_f64c_blas(
|
|
656
|
-
cblas_zdotc_sub((int)n
|
|
664
|
+
void vdot_f64c_blas(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_size_t n, simsimd_distance_t *result) {
|
|
665
|
+
cblas_zdotc_sub((int)n, (simsimd_f64_t const *)a, 1, (simsimd_f64_t const *)b, 1, result);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
void bilinear_f32_blas(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c, simsimd_size_t n,
|
|
669
|
+
simsimd_distance_t *result) {
|
|
670
|
+
std::array<simsimd_f32_t, curved_dimensions> intermediate;
|
|
671
|
+
simsimd_f32_t alpha = 1.0f, beta = 0.0f;
|
|
672
|
+
cblas_sgemv(CblasRowMajor, CblasNoTrans, (int)n, (int)n, alpha, c, (int)n, b, 1, beta, intermediate.data(), 1);
|
|
673
|
+
*result = cblas_sdot((int)n, a, 1, intermediate.data(), 1);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
void bilinear_f64_blas(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c, simsimd_size_t n,
|
|
677
|
+
simsimd_distance_t *result) {
|
|
678
|
+
std::array<simsimd_f64_t, curved_dimensions> intermediate;
|
|
679
|
+
simsimd_f64_t alpha = 1.0, beta = 0.0;
|
|
680
|
+
cblas_dgemv(CblasRowMajor, CblasNoTrans, (int)n, (int)n, alpha, c, n, b, 1, beta, intermediate.data(), 1);
|
|
681
|
+
*result = cblas_ddot((int)n, a, 1, intermediate.data(), 1);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
void bilinear_f32c_blas(simsimd_f32c_t const *a, simsimd_f32c_t const *b, simsimd_f32c_t const *c, simsimd_size_t n,
|
|
685
|
+
simsimd_distance_t *results) {
|
|
686
|
+
std::array<simsimd_f32c_t, curved_dimensions> intermediate;
|
|
687
|
+
simsimd_f32c_t alpha = {1.0f, 0.0f}, beta = {0.0f, 0.0f};
|
|
688
|
+
cblas_cgemv(CblasRowMajor, CblasNoTrans, (int)n, (int)n, &alpha, c, n, b, 1, &beta, intermediate.data(), 1);
|
|
689
|
+
simsimd_f32_t f32_result[2] = {0, 0};
|
|
690
|
+
cblas_cdotu_sub((int)n, (simsimd_f32_t const *)a, 1, (simsimd_f32_t const *)intermediate.data(), 1, f32_result);
|
|
691
|
+
results[0] = f32_result[0];
|
|
692
|
+
results[1] = f32_result[1];
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
void bilinear_f64c_blas(simsimd_f64c_t const *a, simsimd_f64c_t const *b, simsimd_f64c_t const *c, simsimd_size_t n,
|
|
696
|
+
simsimd_distance_t *results) {
|
|
697
|
+
std::array<simsimd_f64c_t, curved_dimensions> intermediate;
|
|
698
|
+
simsimd_f64c_t alpha = {1.0, 0.0}, beta = {0.0, 0.0};
|
|
699
|
+
cblas_zgemv(CblasRowMajor, CblasNoTrans, (int)n, (int)n, &alpha, c, n, b, 1, &beta, intermediate.data(), 1);
|
|
700
|
+
cblas_zdotu_sub((int)n, (simsimd_f64_t const *)a, 1, (simsimd_f64_t const *)intermediate.data(), 1, results);
|
|
657
701
|
}
|
|
658
702
|
|
|
659
703
|
#endif
|
|
@@ -730,6 +774,11 @@ int main(int argc, char **argv) {
|
|
|
730
774
|
dense_<f32c_k>("vdot_f32c_blas", vdot_f32c_blas, simsimd_vdot_f32c_accurate);
|
|
731
775
|
dense_<f64c_k>("vdot_f64c_blas", vdot_f64c_blas, simsimd_vdot_f64c_serial);
|
|
732
776
|
|
|
777
|
+
curved_<f64_k>("bilinear_f64_blas", bilinear_f64_blas, simsimd_bilinear_f64_serial);
|
|
778
|
+
curved_<f64c_k>("bilinear_f64c_blas", bilinear_f64c_blas, simsimd_bilinear_f64c_serial);
|
|
779
|
+
curved_<f32_k>("bilinear_f32_blas", bilinear_f32_blas, simsimd_bilinear_f32_accurate);
|
|
780
|
+
curved_<f32c_k>("bilinear_f32c_blas", bilinear_f32c_blas, simsimd_bilinear_f32c_accurate);
|
|
781
|
+
|
|
733
782
|
#endif
|
|
734
783
|
|
|
735
784
|
#if SIMSIMD_TARGET_NEON
|
|
@@ -762,6 +811,7 @@ int main(int argc, char **argv) {
|
|
|
762
811
|
|
|
763
812
|
curved_<f32_k>("bilinear_f32_neon", simsimd_bilinear_f32_neon, simsimd_bilinear_f32_accurate);
|
|
764
813
|
curved_<f32_k>("mahalanobis_f32_neon", simsimd_mahalanobis_f32_neon, simsimd_mahalanobis_f32_accurate);
|
|
814
|
+
curved_<f32c_k>("bilinear_f32c_neon", simsimd_bilinear_f32c_neon, simsimd_bilinear_f32c_accurate);
|
|
765
815
|
|
|
766
816
|
sparse_<u16_k>("intersect_u16_neon", simsimd_intersect_u16_neon, simsimd_intersect_u16_accurate);
|
|
767
817
|
sparse_<u32_k>("intersect_u32_neon", simsimd_intersect_u32_neon, simsimd_intersect_u32_accurate);
|
|
@@ -786,6 +836,7 @@ int main(int argc, char **argv) {
|
|
|
786
836
|
|
|
787
837
|
curved_<f16_k>("bilinear_f16_neon", simsimd_bilinear_f16_neon, simsimd_bilinear_f16_accurate);
|
|
788
838
|
curved_<f16_k>("mahalanobis_f16_neon", simsimd_mahalanobis_f16_neon, simsimd_mahalanobis_f16_accurate);
|
|
839
|
+
curved_<f16c_k>("bilinear_f16c_neon", simsimd_bilinear_f16c_neon, simsimd_bilinear_f16c_accurate);
|
|
789
840
|
|
|
790
841
|
fma_<f16_k>("fma_f16_neon", simsimd_fma_f16_neon, simsimd_fma_f16_accurate, simsimd_l2_f16_accurate);
|
|
791
842
|
fma_<f16_k>("wsum_f16_neon", simsimd_wsum_f16_neon, simsimd_wsum_f16_accurate, simsimd_l2_f16_accurate);
|
|
@@ -808,17 +859,13 @@ int main(int argc, char **argv) {
|
|
|
808
859
|
|
|
809
860
|
curved_<bf16_k>("bilinear_bf16_neon", simsimd_bilinear_bf16_neon, simsimd_bilinear_bf16_accurate);
|
|
810
861
|
curved_<bf16_k>("mahalanobis_bf16_neon", simsimd_mahalanobis_bf16_neon, simsimd_mahalanobis_bf16_accurate);
|
|
862
|
+
curved_<bf16c_k>("bilinear_bf16c_neon", simsimd_bilinear_bf16c_neon, simsimd_bilinear_bf16c_accurate);
|
|
811
863
|
|
|
812
864
|
fma_<bf16_k>("fma_bf16_neon", simsimd_fma_bf16_neon, simsimd_fma_bf16_accurate, simsimd_l2_bf16_accurate);
|
|
813
865
|
fma_<bf16_k>("wsum_bf16_neon", simsimd_wsum_bf16_neon, simsimd_wsum_bf16_accurate, simsimd_l2_bf16_accurate);
|
|
814
866
|
#endif
|
|
815
867
|
|
|
816
868
|
#if SIMSIMD_TARGET_SVE
|
|
817
|
-
dense_<f16_k>("dot_f16_sve", simsimd_dot_f16_sve, simsimd_dot_f16_accurate);
|
|
818
|
-
dense_<f16_k>("cos_f16_sve", simsimd_cos_f16_sve, simsimd_cos_f16_accurate);
|
|
819
|
-
dense_<f16_k>("l2sq_f16_sve", simsimd_l2sq_f16_sve, simsimd_l2sq_f16_accurate);
|
|
820
|
-
dense_<f16_k>("l2_f16_sve", simsimd_l2_f16_sve, simsimd_l2_f16_accurate);
|
|
821
|
-
|
|
822
869
|
dense_<f32_k>("dot_f32_sve", simsimd_dot_f32_sve, simsimd_dot_f32_accurate);
|
|
823
870
|
dense_<f32_k>("cos_f32_sve", simsimd_cos_f32_sve, simsimd_cos_f32_accurate);
|
|
824
871
|
dense_<f32_k>("l2sq_f32_sve", simsimd_l2sq_f32_sve, simsimd_l2sq_f32_accurate);
|
|
@@ -914,12 +961,12 @@ int main(int argc, char **argv) {
|
|
|
914
961
|
dense_<bf16_k>("cos_bf16_genoa", simsimd_cos_bf16_genoa, simsimd_cos_bf16_accurate);
|
|
915
962
|
dense_<bf16_k>("l2sq_bf16_genoa", simsimd_l2sq_bf16_genoa, simsimd_l2sq_bf16_accurate);
|
|
916
963
|
dense_<bf16_k>("l2_bf16_genoa", simsimd_l2_bf16_genoa, simsimd_l2_bf16_accurate);
|
|
917
|
-
|
|
918
|
-
dense_<
|
|
919
|
-
dense_<bf16_k>("vdot_bf16c_genoa", simsimd_vdot_bf16c_genoa, simsimd_vdot_bf16c_accurate);
|
|
964
|
+
dense_<bf16c_k>("dot_bf16c_genoa", simsimd_dot_bf16c_genoa, simsimd_dot_bf16c_accurate);
|
|
965
|
+
dense_<bf16c_k>("vdot_bf16c_genoa", simsimd_vdot_bf16c_genoa, simsimd_vdot_bf16c_accurate);
|
|
920
966
|
|
|
921
967
|
curved_<bf16_k>("bilinear_bf16_genoa", simsimd_bilinear_bf16_genoa, simsimd_bilinear_bf16_accurate);
|
|
922
968
|
curved_<bf16_k>("mahalanobis_bf16_genoa", simsimd_mahalanobis_bf16_genoa, simsimd_mahalanobis_bf16_accurate);
|
|
969
|
+
curved_<bf16c_k>("bilinear_bf16c_genoa", simsimd_bilinear_bf16c_genoa, simsimd_bilinear_bf16c_accurate);
|
|
923
970
|
#endif
|
|
924
971
|
|
|
925
972
|
#if SIMSIMD_TARGET_SAPPHIRE
|
|
@@ -937,6 +984,10 @@ int main(int argc, char **argv) {
|
|
|
937
984
|
fma_<u8_k>("wsum_u8_sapphire", simsimd_wsum_u8_sapphire, simsimd_wsum_u8_accurate, simsimd_l2_u8_serial);
|
|
938
985
|
fma_<i8_k>("fma_i8_sapphire", simsimd_fma_i8_sapphire, simsimd_fma_i8_accurate, simsimd_l2_i8_serial);
|
|
939
986
|
fma_<i8_k>("wsum_i8_sapphire", simsimd_wsum_i8_sapphire, simsimd_wsum_i8_accurate, simsimd_l2_i8_serial);
|
|
987
|
+
|
|
988
|
+
curved_<f16_k>("bilinear_f16_sapphire", simsimd_bilinear_f16_sapphire, simsimd_bilinear_f16_accurate);
|
|
989
|
+
curved_<f16_k>("mahalanobis_f16_sapphire", simsimd_mahalanobis_f16_sapphire, simsimd_mahalanobis_f16_accurate);
|
|
990
|
+
curved_<f16c_k>("bilinear_f16c_sapphire", simsimd_bilinear_f16c_sapphire, simsimd_bilinear_f16c_accurate);
|
|
940
991
|
#endif
|
|
941
992
|
|
|
942
993
|
#if SIMSIMD_TARGET_ICE
|
|
@@ -987,6 +1038,10 @@ int main(int argc, char **argv) {
|
|
|
987
1038
|
fma_<bf16_k>("fma_bf16_skylake", simsimd_fma_bf16_skylake, simsimd_fma_bf16_accurate, simsimd_l2_bf16_accurate);
|
|
988
1039
|
fma_<bf16_k>("wsum_bf16_skylake", simsimd_wsum_bf16_skylake, simsimd_wsum_bf16_accurate, simsimd_l2_bf16_accurate);
|
|
989
1040
|
|
|
1041
|
+
curved_<f32_k>("bilinear_f32_skylake", simsimd_bilinear_f32_skylake, simsimd_bilinear_f32_serial);
|
|
1042
|
+
curved_<f32c_k>("bilinear_f32c_skylake", simsimd_bilinear_f32c_skylake, simsimd_bilinear_f32c_serial);
|
|
1043
|
+
curved_<f64_k>("bilinear_f64_skylake", simsimd_bilinear_f64_skylake, simsimd_bilinear_f64_serial);
|
|
1044
|
+
curved_<f64c_k>("bilinear_f64c_skylake", simsimd_bilinear_f64c_skylake, simsimd_bilinear_f64c_serial);
|
|
990
1045
|
#endif
|
|
991
1046
|
|
|
992
1047
|
sparse_<u16_k>("intersect_u16_serial", simsimd_intersect_u16_serial, simsimd_intersect_u16_accurate);
|
|
@@ -995,12 +1050,16 @@ int main(int argc, char **argv) {
|
|
|
995
1050
|
sparse_<u32_k>("intersect_u32_accurate", simsimd_intersect_u32_accurate, simsimd_intersect_u32_accurate);
|
|
996
1051
|
|
|
997
1052
|
curved_<f64_k>("bilinear_f64_serial", simsimd_bilinear_f64_serial, simsimd_bilinear_f64_serial);
|
|
1053
|
+
curved_<f64c_k>("bilinear_f64c_serial", simsimd_bilinear_f64c_serial, simsimd_bilinear_f64c_serial);
|
|
998
1054
|
curved_<f64_k>("mahalanobis_f64_serial", simsimd_mahalanobis_f64_serial, simsimd_mahalanobis_f64_serial);
|
|
999
1055
|
curved_<f32_k>("bilinear_f32_serial", simsimd_bilinear_f32_serial, simsimd_bilinear_f32_accurate);
|
|
1056
|
+
curved_<f32c_k>("bilinear_f32c_serial", simsimd_bilinear_f32c_serial, simsimd_bilinear_f32c_accurate);
|
|
1000
1057
|
curved_<f32_k>("mahalanobis_f32_serial", simsimd_mahalanobis_f32_serial, simsimd_mahalanobis_f32_accurate);
|
|
1001
1058
|
curved_<f16_k>("bilinear_f16_serial", simsimd_bilinear_f16_serial, simsimd_bilinear_f16_accurate);
|
|
1059
|
+
curved_<f16c_k>("bilinear_f16c_serial", simsimd_bilinear_f16c_serial, simsimd_bilinear_f16c_accurate);
|
|
1002
1060
|
curved_<f16_k>("mahalanobis_f16_serial", simsimd_mahalanobis_f16_serial, simsimd_mahalanobis_f16_accurate);
|
|
1003
1061
|
curved_<bf16_k>("bilinear_bf16_serial", simsimd_bilinear_bf16_serial, simsimd_bilinear_bf16_accurate);
|
|
1062
|
+
curved_<bf16c_k>("bilinear_bf16c_serial", simsimd_bilinear_bf16c_serial, simsimd_bilinear_bf16c_accurate);
|
|
1004
1063
|
curved_<bf16_k>("mahalanobis_bf16_serial", simsimd_mahalanobis_bf16_serial, simsimd_mahalanobis_bf16_accurate);
|
|
1005
1064
|
|
|
1006
1065
|
dense_<bf16_k>("dot_bf16_serial", simsimd_dot_bf16_serial, simsimd_dot_bf16_accurate);
|
package/simsimd/scripts/test.c
CHANGED
|
@@ -90,6 +90,12 @@ void test_distance_from_itself(void) {
|
|
|
90
90
|
simsimd_f32_t f32s[1536];
|
|
91
91
|
simsimd_f16_t f16s[1536];
|
|
92
92
|
simsimd_bf16_t bf16s[1536];
|
|
93
|
+
|
|
94
|
+
simsimd_f64c_t f64cs[768];
|
|
95
|
+
simsimd_f32c_t f32cs[768];
|
|
96
|
+
simsimd_f16c_t f16cs[768];
|
|
97
|
+
simsimd_bf16c_t bf16cs[768];
|
|
98
|
+
|
|
93
99
|
simsimd_i8_t i8s[1536];
|
|
94
100
|
simsimd_u8_t u8s[1536];
|
|
95
101
|
simsimd_b8_t b8s[1536 / 8]; // 8 bits per word
|
|
@@ -120,16 +126,16 @@ void test_distance_from_itself(void) {
|
|
|
120
126
|
simsimd_dot_f64(f64s, f64s, 1536, &distance[0]);
|
|
121
127
|
|
|
122
128
|
// Complex inner product between two vectors
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
simsimd_dot_f32c(
|
|
126
|
-
simsimd_dot_f64c(
|
|
129
|
+
simsimd_dot_bf16c(bf16cs, bf16cs, 768, &distance[0]);
|
|
130
|
+
simsimd_dot_f16c(f16cs, f16cs, 768, &distance[0]);
|
|
131
|
+
simsimd_dot_f32c(f32cs, f32cs, 768, &distance[0]);
|
|
132
|
+
simsimd_dot_f64c(f64cs, f64cs, 768, &distance[0]);
|
|
127
133
|
|
|
128
134
|
// Complex conjugate inner product between two vectors
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
simsimd_vdot_f32c(
|
|
132
|
-
simsimd_vdot_f64c(
|
|
135
|
+
simsimd_vdot_bf16c(bf16cs, bf16cs, 768, &distance[0]);
|
|
136
|
+
simsimd_vdot_f16c(f16cs, f16cs, 768, &distance[0]);
|
|
137
|
+
simsimd_vdot_f32c(f32cs, f32cs, 768, &distance[0]);
|
|
138
|
+
simsimd_vdot_f64c(f64cs, f64cs, 768, &distance[0]);
|
|
133
139
|
|
|
134
140
|
// Hamming distance between two vectors
|
|
135
141
|
simsimd_hamming_b8(b8s, b8s, 1536 / 8, &distance[0]);
|