usearch 2.7.2 → 2.7.6
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/.github/workflows/Dockerfile.libs +4 -6
- package/.github/workflows/prerelease.yml +4 -1
- package/.github/workflows/release.yml +8 -28
- package/.github/workflows/update_version.sh +1 -0
- package/CITATION.cff +2 -2
- package/CMakeLists.txt +1 -1
- package/Cargo.toml +1 -1
- package/README.md +4 -4
- package/binding.gyp +1 -1
- package/build.gradle +20 -5
- package/conanfile.py +1 -1
- package/csharp/nuget/nuget-package.props +1 -1
- package/docs/compilation.md +0 -2
- package/golang/README.md +0 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +119 -243
- package/javascript/lib.cpp +4 -3
- package/package.json +1 -1
- package/simsimd/.github/workflows/prerelease.yml +33 -58
- package/simsimd/.github/workflows/release.yml +22 -9
- package/simsimd/.github/workflows/update_version.sh +3 -1
- package/simsimd/.releaserc +9 -1
- package/simsimd/.vscode/launch.json +3 -0
- package/simsimd/.vscode/settings.json +15 -2
- package/simsimd/.vscode/tasks.json +2 -2
- package/simsimd/CMakeLists.txt +36 -55
- package/simsimd/README.md +163 -109
- package/simsimd/binding.gyp +18 -0
- package/simsimd/cpp/bench.cxx +187 -0
- package/simsimd/go.mod +3 -0
- package/simsimd/golang/bench_test.go +40 -0
- package/simsimd/golang/simsimd.go +47 -0
- package/simsimd/golang/unit_test.go +40 -0
- package/simsimd/include/simsimd/binary.h +186 -0
- package/simsimd/include/simsimd/probability.h +645 -0
- package/simsimd/include/simsimd/simsimd.h +335 -575
- package/simsimd/include/simsimd/spatial.h +924 -0
- package/simsimd/include/simsimd/types.h +196 -0
- package/simsimd/javascript/bench.js +117 -0
- package/simsimd/javascript/lib.c +102 -0
- package/simsimd/javascript/simsimd.js +45 -0
- package/simsimd/javascript/test.js +29 -0
- package/simsimd/package.json +31 -2
- package/simsimd/pyproject.toml +5 -6
- package/simsimd/python/bench.ipynb +187 -0
- package/simsimd/python/bench.py +268 -0
- package/simsimd/python/lib.c +453 -72
- package/simsimd/python/test.py +167 -35
- package/simsimd/setup.py +51 -8
- package/wasmer.toml +1 -1
- package/simsimd/bench.cxx +0 -137
- package/simsimd/include/simsimd/simsimd_bio.h +0 -54
- package/simsimd/include/simsimd/simsimd_chem.h +0 -167
- package/simsimd/include/simsimd/simsimd_text.h +0 -92
- package/simsimd/python/run.sh +0 -1
- package/simsimd/python/test.c +0 -109
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
#endif
|
|
42
42
|
|
|
43
43
|
#if USEARCH_USE_SIMSIMD
|
|
44
|
+
// Propagate the `f16` settings
|
|
45
|
+
#define SIMSIMD_NATIVE_F16 USEARCH_USE_NATIVE_F16
|
|
44
46
|
#if defined(USEARCH_DEFINED_LINUX)
|
|
45
47
|
#include <simsimd/simsimd.h>
|
|
46
48
|
#elif defined(USEARCH_DEFINED_APPLE)
|
|
@@ -128,16 +130,6 @@ enum class scalar_kind_t : std::uint8_t {
|
|
|
128
130
|
i8_k,
|
|
129
131
|
};
|
|
130
132
|
|
|
131
|
-
enum class isa_kind_t {
|
|
132
|
-
auto_k,
|
|
133
|
-
neon_k,
|
|
134
|
-
sve_k,
|
|
135
|
-
avx2_k,
|
|
136
|
-
avx2f16_k,
|
|
137
|
-
avx512_k,
|
|
138
|
-
avx512f16_k,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
133
|
enum class prefetching_kind_t {
|
|
142
134
|
none_k,
|
|
143
135
|
cpu_k,
|
|
@@ -194,95 +186,20 @@ inline bool str_equals(char const* begin, std::size_t len, char const* other_beg
|
|
|
194
186
|
return len == other_len && std::strncmp(begin, other_begin, len) == 0;
|
|
195
187
|
}
|
|
196
188
|
|
|
197
|
-
inline char const* isa_name(
|
|
189
|
+
inline char const* isa_name(simsimd_capability_t isa_kind) noexcept {
|
|
198
190
|
switch (isa_kind) {
|
|
199
|
-
case
|
|
200
|
-
case
|
|
201
|
-
case
|
|
202
|
-
case
|
|
203
|
-
case
|
|
204
|
-
case
|
|
205
|
-
case
|
|
206
|
-
|
|
191
|
+
case simsimd_cap_serial_k: return "serial";
|
|
192
|
+
case simsimd_cap_arm_neon_k: return "neon";
|
|
193
|
+
case simsimd_cap_arm_sve_k: return "sve";
|
|
194
|
+
case simsimd_cap_x86_avx2_k: return "avx2";
|
|
195
|
+
case simsimd_cap_x86_avx512_k: return "avx512";
|
|
196
|
+
case simsimd_cap_x86_avx2fp16_k: return "avx2+f16";
|
|
197
|
+
case simsimd_cap_x86_avx512fp16_k: return "avx512+f16";
|
|
198
|
+
case simsimd_cap_x86_avx512vpopcntdq_k: return "avx512+popcnt";
|
|
199
|
+
default: return "unknown";
|
|
207
200
|
}
|
|
208
201
|
}
|
|
209
202
|
|
|
210
|
-
inline bool hardware_supports(isa_kind_t isa_kind) noexcept {
|
|
211
|
-
|
|
212
|
-
// On Linux Arm machines the `getauxval` can be queried to check
|
|
213
|
-
// if SVE extensions are available. Arm Neon has no separate capability check.
|
|
214
|
-
#if defined(USEARCH_DEFINED_ARM) && defined(USEARCH_DEFINED_LINUX)
|
|
215
|
-
unsigned long capabilities = getauxval(AT_HWCAP);
|
|
216
|
-
switch (isa_kind) {
|
|
217
|
-
case isa_kind_t::neon_k: return true; // Must be supported on 64-bit Arm
|
|
218
|
-
case isa_kind_t::sve_k: return capabilities & HWCAP_SVE;
|
|
219
|
-
default: return false;
|
|
220
|
-
}
|
|
221
|
-
#endif
|
|
222
|
-
|
|
223
|
-
// When compiling with GCC, one may use the "built-ins", including ones
|
|
224
|
-
// designed for CPU capability detection, but they are not fully compatible with Clang.
|
|
225
|
-
// So instead I've decided to add a pinch of inline assembly.
|
|
226
|
-
#if defined(USEARCH_DEFINED_X86)
|
|
227
|
-
auto check_cpu_feature = [](std::uint64_t feature_mask, int function_id, int register_id) -> bool {
|
|
228
|
-
std::uint32_t eax, ebx, ecx, edx;
|
|
229
|
-
// Execute CPUID instruction and store results in eax, ebx, ecx, edx
|
|
230
|
-
// Setting %ecx to 0 as well
|
|
231
|
-
__asm__ volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(function_id), "c"(0));
|
|
232
|
-
std::uint32_t register_value;
|
|
233
|
-
switch (register_id) {
|
|
234
|
-
case 0: register_value = eax; break;
|
|
235
|
-
case 1: register_value = ebx; break;
|
|
236
|
-
case 2: register_value = ecx; break;
|
|
237
|
-
case 3: register_value = edx; break;
|
|
238
|
-
default: return false;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return (register_value & feature_mask) != 0;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
// Check for AVX2 (Function ID 7, EBX register)
|
|
245
|
-
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L148
|
|
246
|
-
bool supports_avx2 = check_cpu_feature(1 << 5, 7, 1);
|
|
247
|
-
|
|
248
|
-
// Check for F16C (Function ID 1, ECX register)
|
|
249
|
-
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L107
|
|
250
|
-
bool supports_f16c = check_cpu_feature(1 << 29, 1, 2);
|
|
251
|
-
|
|
252
|
-
// Check for AVX512F (Function ID 7, EBX register)
|
|
253
|
-
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L155
|
|
254
|
-
bool supports_avx512f = check_cpu_feature(1 << 16, 7, 1);
|
|
255
|
-
|
|
256
|
-
// Check for AVX512FP16 (Function ID 7, EDX register)
|
|
257
|
-
// https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L198C9-L198C23
|
|
258
|
-
bool supports_avx512fp16 = check_cpu_feature(1 << 23, 7, 3);
|
|
259
|
-
|
|
260
|
-
switch (isa_kind) {
|
|
261
|
-
case isa_kind_t::avx2f16_k: return supports_avx2 && supports_f16c;
|
|
262
|
-
case isa_kind_t::avx512f16_k: return supports_avx512fp16 && supports_avx512f;
|
|
263
|
-
case isa_kind_t::avx2_k: return supports_avx2;
|
|
264
|
-
case isa_kind_t::avx512_k: return supports_avx512f;
|
|
265
|
-
default: return false;
|
|
266
|
-
}
|
|
267
|
-
#endif
|
|
268
|
-
|
|
269
|
-
// On Apple we can expect Arm devices to support Neon extesions,
|
|
270
|
-
// and the x86 machines to support AVX2 extensions.
|
|
271
|
-
#if defined(USEARCH_DEFINED_APPLE)
|
|
272
|
-
switch (isa_kind) {
|
|
273
|
-
#if defined(USEARCH_DEFINED_ARM)
|
|
274
|
-
case isa_kind_t::neon_k: return true;
|
|
275
|
-
#else
|
|
276
|
-
case isa_kind_t::avx2_k: return true;
|
|
277
|
-
#endif
|
|
278
|
-
default: return false;
|
|
279
|
-
}
|
|
280
|
-
#endif
|
|
281
|
-
|
|
282
|
-
(void)isa_kind;
|
|
283
|
-
return false;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
203
|
inline std::size_t bits_per_scalar(scalar_kind_t scalar_kind) noexcept {
|
|
287
204
|
switch (scalar_kind) {
|
|
288
205
|
case scalar_kind_t::f64_k: return 64;
|
|
@@ -653,9 +570,7 @@ class aligned_allocator_gt {
|
|
|
653
570
|
using size_type = std::size_t;
|
|
654
571
|
using pointer = element_at*;
|
|
655
572
|
using const_pointer = element_at const*;
|
|
656
|
-
template <typename other_element_at> struct rebind {
|
|
657
|
-
using other = aligned_allocator_gt<other_element_at>;
|
|
658
|
-
};
|
|
573
|
+
template <typename other_element_at> struct rebind { using other = aligned_allocator_gt<other_element_at>; };
|
|
659
574
|
|
|
660
575
|
constexpr std::size_t alignment() const { return alignment_ak; }
|
|
661
576
|
|
|
@@ -716,7 +631,7 @@ class page_allocator_t {
|
|
|
716
631
|
* @b Thread-safe, @b except constructors and destructors.
|
|
717
632
|
*
|
|
718
633
|
* Using this memory allocator won't affect your overall speed much, as that is not the bottleneck.
|
|
719
|
-
* However, it can drastically improve memory usage
|
|
634
|
+
* However, it can drastically improve memory usage especially for huge indexes of small vectors.
|
|
720
635
|
*/
|
|
721
636
|
template <std::size_t alignment_ak = 1> class memory_mapping_allocator_gt {
|
|
722
637
|
|
|
@@ -1350,6 +1265,7 @@ enum class metric_punned_signature_t {
|
|
|
1350
1265
|
/**
|
|
1351
1266
|
* @brief Type-punned metric class, which unlike STL's `std::function` avoids any memory allocations.
|
|
1352
1267
|
* It also provides additional APIs to check, if SIMD hardware-acceleration is available.
|
|
1268
|
+
* Wraps the `simsimd_metric_punned_t`.
|
|
1353
1269
|
*/
|
|
1354
1270
|
class metric_punned_t {
|
|
1355
1271
|
public:
|
|
@@ -1358,15 +1274,16 @@ class metric_punned_t {
|
|
|
1358
1274
|
|
|
1359
1275
|
private:
|
|
1360
1276
|
using punned_arg_t = std::size_t;
|
|
1361
|
-
using
|
|
1362
|
-
|
|
1277
|
+
using punned_ptr_t = result_t (*)(std::size_t, std::size_t, std::size_t, std::size_t);
|
|
1278
|
+
|
|
1279
|
+
punned_ptr_t raw_ptr_ = nullptr;
|
|
1363
1280
|
punned_arg_t raw_arg3_ = 0;
|
|
1364
1281
|
punned_arg_t raw_arg4_ = 0;
|
|
1365
1282
|
|
|
1366
1283
|
std::size_t dimensions_ = 0;
|
|
1367
1284
|
metric_kind_t metric_kind_ = metric_kind_t::unknown_k;
|
|
1368
1285
|
scalar_kind_t scalar_kind_ = scalar_kind_t::unknown_k;
|
|
1369
|
-
|
|
1286
|
+
simsimd_capability_t isa_kind_ = simsimd_cap_serial_k;
|
|
1370
1287
|
|
|
1371
1288
|
public:
|
|
1372
1289
|
/**
|
|
@@ -1386,8 +1303,14 @@ class metric_punned_t {
|
|
|
1386
1303
|
metric_kind_t metric_kind = metric_kind_t::l2sq_k, //
|
|
1387
1304
|
scalar_kind_t scalar_kind = scalar_kind_t::f32_k) noexcept
|
|
1388
1305
|
: raw_arg3_(dimensions), raw_arg4_(dimensions), dimensions_(dimensions), metric_kind_(metric_kind),
|
|
1389
|
-
scalar_kind_(scalar_kind), isa_kind_(
|
|
1390
|
-
|
|
1306
|
+
scalar_kind_(scalar_kind), isa_kind_(simsimd_cap_serial_k) {
|
|
1307
|
+
|
|
1308
|
+
#if USEARCH_USE_SIMSIMD
|
|
1309
|
+
if (configure_with_simsimd())
|
|
1310
|
+
return;
|
|
1311
|
+
#endif
|
|
1312
|
+
|
|
1313
|
+
configure_with_auto_vectorized();
|
|
1391
1314
|
}
|
|
1392
1315
|
|
|
1393
1316
|
inline metric_punned_t( //
|
|
@@ -1395,7 +1318,7 @@ class metric_punned_t {
|
|
|
1395
1318
|
std::uintptr_t metric_uintptr, metric_punned_signature_t signature, //
|
|
1396
1319
|
metric_kind_t metric_kind, //
|
|
1397
1320
|
scalar_kind_t scalar_kind) noexcept
|
|
1398
|
-
: raw_ptr_(reinterpret_cast<
|
|
1321
|
+
: raw_ptr_(reinterpret_cast<punned_ptr_t>(metric_uintptr)), dimensions_(dimensions), metric_kind_(metric_kind),
|
|
1399
1322
|
scalar_kind_(scalar_kind) {
|
|
1400
1323
|
|
|
1401
1324
|
// We don't need to explicitly parse signature, as all of them are compatible.
|
|
@@ -1405,7 +1328,7 @@ class metric_punned_t {
|
|
|
1405
1328
|
inline std::size_t dimensions() const noexcept { return dimensions_; }
|
|
1406
1329
|
inline metric_kind_t metric_kind() const noexcept { return metric_kind_; }
|
|
1407
1330
|
inline scalar_kind_t scalar_kind() const noexcept { return scalar_kind_; }
|
|
1408
|
-
inline
|
|
1331
|
+
inline simsimd_capability_t isa_kind() const noexcept { return isa_kind_; }
|
|
1409
1332
|
|
|
1410
1333
|
inline std::size_t bytes_per_vector() const noexcept {
|
|
1411
1334
|
return divide_round_up<CHAR_BIT>(dimensions_ * bits_per_scalar(scalar_kind_));
|
|
@@ -1416,24 +1339,105 @@ class metric_punned_t {
|
|
|
1416
1339
|
}
|
|
1417
1340
|
|
|
1418
1341
|
private:
|
|
1419
|
-
|
|
1342
|
+
bool configure_with_simsimd() noexcept {
|
|
1343
|
+
#if USEARCH_USE_SIMSIMD
|
|
1344
|
+
simsimd_metric_kind_t kind = simsimd_metric_unknown_k;
|
|
1345
|
+
simsimd_datatype_t datatype = simsimd_datatype_unknown_k;
|
|
1346
|
+
simsimd_capability_t allowed = simsimd_cap_any_k;
|
|
1420
1347
|
switch (metric_kind_) {
|
|
1421
|
-
case metric_kind_t::ip_k:
|
|
1422
|
-
case metric_kind_t::cos_k:
|
|
1423
|
-
case metric_kind_t::l2sq_k:
|
|
1424
|
-
case metric_kind_t::
|
|
1425
|
-
case metric_kind_t::
|
|
1348
|
+
case metric_kind_t::ip_k: kind = simsimd_metric_ip_k; break;
|
|
1349
|
+
case metric_kind_t::cos_k: kind = simsimd_metric_cos_k; break;
|
|
1350
|
+
case metric_kind_t::l2sq_k: kind = simsimd_metric_l2sq_k; break;
|
|
1351
|
+
case metric_kind_t::hamming_k: kind = simsimd_metric_hamming_k; break;
|
|
1352
|
+
case metric_kind_t::jaccard_k: kind = simsimd_metric_jaccard_k; break;
|
|
1353
|
+
}
|
|
1354
|
+
switch (scalar_kind_) {
|
|
1355
|
+
case scalar_kind_t::f32_k: datatype = simsimd_datatype_f32_k; break;
|
|
1356
|
+
case scalar_kind_t::f64_k: datatype = simsimd_datatype_f64_k; break;
|
|
1357
|
+
case scalar_kind_t::f16_k: datatype = simsimd_datatype_f16_k; break;
|
|
1358
|
+
case scalar_kind_t::i8_k: datatype = simsimd_datatype_i8_k; break;
|
|
1359
|
+
case scalar_kind_t::b1x8_k: datatype = simsimd_datatype_b8_k; break;
|
|
1360
|
+
}
|
|
1361
|
+
simsimd_metric_punned_t simd_metric = NULL;
|
|
1362
|
+
simsimd_capability_t simd_kind = simsimd_cap_any_k;
|
|
1363
|
+
simsimd_find_metric_punned(kind, datatype, simsimd_capabilities(), allowed, &simd_metric, &simd_kind);
|
|
1364
|
+
if (simd_metric == nullptr)
|
|
1365
|
+
return false;
|
|
1366
|
+
|
|
1367
|
+
raw_ptr_ = (punned_ptr_t)simd_metric;
|
|
1368
|
+
isa_kind_ = simd_kind;
|
|
1369
|
+
return true;
|
|
1370
|
+
#else
|
|
1371
|
+
return false;
|
|
1372
|
+
#endif
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
void configure_with_auto_vectorized() noexcept {
|
|
1376
|
+
switch (metric_kind_) {
|
|
1377
|
+
case metric_kind_t::ip_k: {
|
|
1378
|
+
switch (scalar_kind_) {
|
|
1379
|
+
case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f32_t>>; break;
|
|
1380
|
+
case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f16_t, f32_t>>; break;
|
|
1381
|
+
case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<i8_t, f32_t>>; break;
|
|
1382
|
+
case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_ip_gt<f64_t>>; break;
|
|
1383
|
+
default: raw_ptr_ = nullptr; break;
|
|
1384
|
+
}
|
|
1385
|
+
break;
|
|
1386
|
+
}
|
|
1387
|
+
case metric_kind_t::cos_k: {
|
|
1388
|
+
switch (scalar_kind_) {
|
|
1389
|
+
case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f32_t>>; break;
|
|
1390
|
+
case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f16_t, f32_t>>; break;
|
|
1391
|
+
case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<i8_t, f32_t>>; break;
|
|
1392
|
+
case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_cos_gt<f64_t>>; break;
|
|
1393
|
+
default: raw_ptr_ = nullptr; break;
|
|
1394
|
+
}
|
|
1395
|
+
break;
|
|
1396
|
+
}
|
|
1397
|
+
case metric_kind_t::l2sq_k: {
|
|
1398
|
+
switch (scalar_kind_) {
|
|
1399
|
+
case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f32_t>>; break;
|
|
1400
|
+
case scalar_kind_t::f16_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f16_t, f32_t>>; break;
|
|
1401
|
+
case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<i8_t, f32_t>>; break;
|
|
1402
|
+
case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_l2sq_gt<f64_t>>; break;
|
|
1403
|
+
default: raw_ptr_ = nullptr; break;
|
|
1404
|
+
}
|
|
1405
|
+
break;
|
|
1406
|
+
}
|
|
1407
|
+
case metric_kind_t::pearson_k: {
|
|
1408
|
+
switch (scalar_kind_) {
|
|
1409
|
+
case scalar_kind_t::i8_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<i8_t, f32_t>>; break;
|
|
1410
|
+
case scalar_kind_t::f16_k:
|
|
1411
|
+
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f16_t, f32_t>>;
|
|
1412
|
+
break;
|
|
1413
|
+
case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f32_t>>; break;
|
|
1414
|
+
case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_pearson_gt<f64_t>>; break;
|
|
1415
|
+
default: raw_ptr_ = nullptr; break;
|
|
1416
|
+
}
|
|
1417
|
+
break;
|
|
1418
|
+
}
|
|
1419
|
+
case metric_kind_t::haversine_k: {
|
|
1420
|
+
switch (scalar_kind_) {
|
|
1421
|
+
case scalar_kind_t::f16_k:
|
|
1422
|
+
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f16_t, f32_t>>;
|
|
1423
|
+
break;
|
|
1424
|
+
case scalar_kind_t::f32_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f32_t>>; break;
|
|
1425
|
+
case scalar_kind_t::f64_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_haversine_gt<f64_t>>; break;
|
|
1426
|
+
default: raw_ptr_ = nullptr; break;
|
|
1427
|
+
}
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1426
1430
|
case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
|
|
1427
1431
|
case metric_kind_t::tanimoto_k:
|
|
1428
|
-
raw_ptr_ = &equidimensional_<metric_tanimoto_gt<b1x8_t>>,
|
|
1432
|
+
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>,
|
|
1429
1433
|
raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
|
|
1430
1434
|
break;
|
|
1431
1435
|
case metric_kind_t::hamming_k:
|
|
1432
|
-
raw_ptr_ = &equidimensional_<metric_hamming_gt<b1x8_t>>,
|
|
1436
|
+
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>,
|
|
1433
1437
|
raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
|
|
1434
1438
|
break;
|
|
1435
1439
|
case metric_kind_t::sorensen_k:
|
|
1436
|
-
raw_ptr_ = &equidimensional_<metric_sorensen_gt<b1x8_t>>,
|
|
1440
|
+
raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>,
|
|
1437
1441
|
raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
|
|
1438
1442
|
break;
|
|
1439
1443
|
default: return;
|
|
@@ -1448,134 +1452,6 @@ class metric_punned_t {
|
|
|
1448
1452
|
(void)b_dimensions;
|
|
1449
1453
|
return typed_at{}((scalar_t const*)a, (scalar_t const*)b, a_dimensions);
|
|
1450
1454
|
}
|
|
1451
|
-
|
|
1452
|
-
template <typename scalar_at>
|
|
1453
|
-
bool reset_if_(isa_kind_t isa, result_t (*function_ptr)(scalar_at, scalar_at, size_t),
|
|
1454
|
-
bool second_condition = true) noexcept {
|
|
1455
|
-
raw_ptr_t punned_ptr = reinterpret_cast<raw_ptr_t>(function_ptr);
|
|
1456
|
-
if (hardware_supports(isa) && second_condition) {
|
|
1457
|
-
raw_ptr_ = punned_ptr;
|
|
1458
|
-
isa_kind_ = isa;
|
|
1459
|
-
return true;
|
|
1460
|
-
} else
|
|
1461
|
-
return false;
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
// clang-format off
|
|
1465
|
-
void reset_ip_metric_f32_() noexcept {
|
|
1466
|
-
#if USEARCH_USE_SIMSIMD
|
|
1467
|
-
#if SIMSIMD_TARGET_ARM_NEON
|
|
1468
|
-
if (reset_if_(isa_kind_t::neon_k, &simsimd_dot_f32x4_neon, dimensions_ % 4 == 0)) return;
|
|
1469
|
-
#elif SIMSIMD_TARGET_ARM_SVE
|
|
1470
|
-
if (reset_if_(isa_kind_t::sve_k, &simsimd_dot_f32_sve)) return;
|
|
1471
|
-
#elif SIMSIMD_TARGET_X86_AVX2
|
|
1472
|
-
if (reset_if_(isa_kind_t::avx2_k, &simsimd_dot_f32x4_avx2, dimensions_ % 4 == 0)) return;
|
|
1473
|
-
#endif
|
|
1474
|
-
#endif
|
|
1475
|
-
raw_ptr_ = &equidimensional_<metric_ip_gt<f32_t>>;
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
void reset_cos_metric_f16_() noexcept {
|
|
1479
|
-
#if USEARCH_USE_SIMSIMD
|
|
1480
|
-
#if SIMSIMD_TARGET_X86_AVX512
|
|
1481
|
-
if (reset_if_(isa_kind_t::avx512f16_k, &simsimd_cos_f16x16_avx512, dimensions_ % 16 == 0)) return;
|
|
1482
|
-
#elif SIMSIMD_TARGET_X86_AVX2
|
|
1483
|
-
if (reset_if_(isa_kind_t::avx2f16_k, &simsimd_cos_f16x8_avx2, dimensions_ % 8 == 0)) return;
|
|
1484
|
-
#elif SIMSIMD_TARGET_ARM_NEON
|
|
1485
|
-
if (reset_if_(isa_kind_t::neon_k, &simsimd_cos_f16x4_neon, dimensions_ % 4 == 0)) return;
|
|
1486
|
-
#elif SIMSIMD_TARGET_ARM_SVE
|
|
1487
|
-
if (reset_if_(isa_kind_t::sve_k, &simsimd_cos_f16_sve)) return;
|
|
1488
|
-
#endif
|
|
1489
|
-
#endif
|
|
1490
|
-
raw_ptr_ = &equidimensional_<metric_cos_gt<f16_t, f32_t>>;
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
void reset_ip_metric_f16_() noexcept {
|
|
1494
|
-
#if USEARCH_USE_SIMSIMD
|
|
1495
|
-
#if SIMSIMD_TARGET_X86_AVX512
|
|
1496
|
-
if (reset_if_(isa_kind_t::avx512f16_k, &simsimd_dot_f16x16_avx512, dimensions_ % 16 == 0)) return;
|
|
1497
|
-
#elif SIMSIMD_TARGET_X86_AVX2
|
|
1498
|
-
if (reset_if_(isa_kind_t::avx2f16_k, &simsimd_dot_f16x8_avx2, dimensions_ % 8 == 0)) return;
|
|
1499
|
-
#elif SIMSIMD_TARGET_ARM_NEON
|
|
1500
|
-
if (reset_if_(isa_kind_t::neon_k, &simsimd_dot_f16x4_neon, dimensions_ % 4 == 0)) return;
|
|
1501
|
-
#endif
|
|
1502
|
-
#endif
|
|
1503
|
-
raw_ptr_ = &equidimensional_<metric_cos_gt<f16_t, f32_t>>;
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
void reset_l2sq_metric_f16_() noexcept {
|
|
1507
|
-
#if USEARCH_USE_SIMSIMD
|
|
1508
|
-
#if SIMSIMD_TARGET_X86_AVX512
|
|
1509
|
-
if (reset_if_(isa_kind_t::avx512f16_k, &simsimd_l2sq_f16x16_avx512, dimensions_ % 16 == 0)) return;
|
|
1510
|
-
#elif SIMSIMD_TARGET_X86_AVX2
|
|
1511
|
-
if (reset_if_(isa_kind_t::avx2f16_k, &simsimd_l2sq_f16x8_avx2, dimensions_ % 8 == 0)) return;
|
|
1512
|
-
#elif SIMSIMD_TARGET_ARM_SVE
|
|
1513
|
-
if (reset_if_(isa_kind_t::sve_k, &simsimd_l2sq_f16_sve)) return;
|
|
1514
|
-
#endif
|
|
1515
|
-
#endif
|
|
1516
|
-
raw_ptr_ = &equidimensional_<metric_l2sq_gt<f16_t, f32_t>>;
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
void reset_cos_metric_i8_() noexcept {
|
|
1520
|
-
#if USEARCH_USE_SIMSIMD
|
|
1521
|
-
#if SIMSIMD_TARGET_ARM_NEON
|
|
1522
|
-
if (reset_if_(isa_kind_t::neon_k, &simsimd_cos_i8x16_neon, dimensions_ % 16 == 0)) return;
|
|
1523
|
-
#elif SIMSIMD_TARGET_X86_AVX2
|
|
1524
|
-
if (reset_if_(isa_kind_t::avx2_k, &simsimd_cos_i8x32_avx2, dimensions_ % 32 == 0)) return;
|
|
1525
|
-
#endif
|
|
1526
|
-
#endif
|
|
1527
|
-
raw_ptr_ = &equidimensional_<cos_i8_t>;
|
|
1528
|
-
}
|
|
1529
|
-
// clang-format on
|
|
1530
|
-
|
|
1531
|
-
void reset_ip_metric_() noexcept {
|
|
1532
|
-
switch (scalar_kind_) { // The two most common numeric types for the most common metric have optimized versions
|
|
1533
|
-
case scalar_kind_t::f32_k: return reset_ip_metric_f32_();
|
|
1534
|
-
case scalar_kind_t::f16_k: return reset_ip_metric_f16_();
|
|
1535
|
-
case scalar_kind_t::i8_k: return reset_cos_metric_i8_();
|
|
1536
|
-
case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_ip_gt<f64_t>>; break;
|
|
1537
|
-
default: return;
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
void reset_l2sq_metric_() noexcept {
|
|
1542
|
-
switch (scalar_kind_) {
|
|
1543
|
-
case scalar_kind_t::i8_k: raw_ptr_ = &equidimensional_<l2sq_i8_t>; break;
|
|
1544
|
-
case scalar_kind_t::f16_k: return reset_l2sq_metric_f16_();
|
|
1545
|
-
case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_l2sq_gt<f32_t>>; break;
|
|
1546
|
-
case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_l2sq_gt<f64_t>>; break;
|
|
1547
|
-
default: return;
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
void reset_cos_metric_() noexcept {
|
|
1552
|
-
switch (scalar_kind_) {
|
|
1553
|
-
case scalar_kind_t::i8_k: return reset_cos_metric_i8_();
|
|
1554
|
-
case scalar_kind_t::f16_k: return reset_cos_metric_f16_();
|
|
1555
|
-
case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_cos_gt<f32_t>>; break;
|
|
1556
|
-
case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_cos_gt<f64_t>>; break;
|
|
1557
|
-
default: return;
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
void reset_haversine_metric_() noexcept {
|
|
1562
|
-
switch (scalar_kind_) {
|
|
1563
|
-
case scalar_kind_t::f16_k: raw_ptr_ = &equidimensional_<metric_haversine_gt<f16_t, f32_t>>; break;
|
|
1564
|
-
case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_haversine_gt<f32_t>>; break;
|
|
1565
|
-
case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_haversine_gt<f64_t>>; break;
|
|
1566
|
-
default: return;
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
void reset_pearson_metric_() noexcept {
|
|
1571
|
-
switch (scalar_kind_) {
|
|
1572
|
-
case scalar_kind_t::i8_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<i8_t, f32_t>>; break;
|
|
1573
|
-
case scalar_kind_t::f16_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<f16_t, f32_t>>; break;
|
|
1574
|
-
case scalar_kind_t::f32_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<f32_t>>; break;
|
|
1575
|
-
case scalar_kind_t::f64_k: raw_ptr_ = &equidimensional_<metric_pearson_gt<f64_t>>; break;
|
|
1576
|
-
default: return;
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
1455
|
};
|
|
1580
1456
|
|
|
1581
1457
|
/**
|
package/javascript/lib.cpp
CHANGED
|
@@ -260,7 +260,7 @@ Napi::Value CompiledIndex::Count(Napi::CallbackInfo const& ctx) {
|
|
|
260
260
|
return result;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
Napi::Value
|
|
263
|
+
Napi::Value exactSearch(Napi::CallbackInfo const& ctx) {
|
|
264
264
|
Napi::Env env = ctx.Env();
|
|
265
265
|
|
|
266
266
|
// Extracting parameters directly without additional type checks.
|
|
@@ -281,6 +281,7 @@ Napi::Value compiledExactSearch(Napi::CallbackInfo const& ctx) {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
metric_punned_t metric(dimensions, metric_kind, quantization);
|
|
284
|
+
executor_default_t executor;
|
|
284
285
|
exact_search_t search;
|
|
285
286
|
|
|
286
287
|
// Performing the exact search.
|
|
@@ -293,7 +294,7 @@ Napi::Value compiledExactSearch(Napi::CallbackInfo const& ctx) {
|
|
|
293
294
|
reinterpret_cast<byte_t const*>(queriesBuffer.Data()), //
|
|
294
295
|
queries_size, //
|
|
295
296
|
dimensions * bytes_per_scalar, //
|
|
296
|
-
wanted, metric);
|
|
297
|
+
wanted, metric, executor);
|
|
297
298
|
|
|
298
299
|
if (!results)
|
|
299
300
|
Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
|
|
@@ -325,7 +326,7 @@ Napi::Value compiledExactSearch(Napi::CallbackInfo const& ctx) {
|
|
|
325
326
|
}
|
|
326
327
|
|
|
327
328
|
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
|
|
328
|
-
exports.Set("
|
|
329
|
+
exports.Set("exactSearch", Napi::Function::New(env, exactSearch));
|
|
329
330
|
return CompiledIndex::Init(env, exports);
|
|
330
331
|
}
|
|
331
332
|
|
package/package.json
CHANGED
|
@@ -17,14 +17,15 @@ permissions:
|
|
|
17
17
|
|
|
18
18
|
jobs:
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
name: Test Python
|
|
20
|
+
test_python:
|
|
21
|
+
name: Test Python ${{ matrix.python-version }} on ${{ matrix.architecture }} ${{ matrix.os }}
|
|
22
22
|
runs-on: ${{ matrix.os }}
|
|
23
23
|
strategy:
|
|
24
24
|
fail-fast: false
|
|
25
25
|
matrix:
|
|
26
|
-
os: [ubuntu-22.04, macOS-11
|
|
27
|
-
python-version: ["3.
|
|
26
|
+
os: [ubuntu-22.04, macOS-11, windows-2022]
|
|
27
|
+
python-version: ["3.12", "3.8"]
|
|
28
|
+
architecture: [x64, arm64]
|
|
28
29
|
|
|
29
30
|
steps:
|
|
30
31
|
- uses: actions/checkout@v3
|
|
@@ -33,74 +34,48 @@ jobs:
|
|
|
33
34
|
uses: actions/setup-python@v4
|
|
34
35
|
with:
|
|
35
36
|
python-version: ${{ matrix.python-version }}
|
|
36
|
-
|
|
37
|
-
- name: Set up Clang
|
|
38
|
-
run: brew install gcc@12
|
|
39
|
-
if: ${{ matrix.os == 'macOS-11' }}
|
|
40
|
-
|
|
41
|
-
- name: Set up MinGW
|
|
42
|
-
uses: egor-tensin/setup-mingw@v2
|
|
43
|
-
with:
|
|
44
|
-
platform: x64
|
|
45
|
-
if: ${{ matrix.os == 'windows-2022' }}
|
|
46
37
|
|
|
47
38
|
- name: Install dependencies
|
|
48
39
|
run: |
|
|
49
40
|
python -m pip install --no-cache-dir --upgrade pip
|
|
50
|
-
pip install --no-cache-dir pytest
|
|
51
|
-
|
|
41
|
+
pip install --no-cache-dir pytest numpy scipy py-cpuinfo pytest-repeat
|
|
42
|
+
python -c "from cpuinfo import get_cpu_info; print(get_cpu_info())"
|
|
43
|
+
|
|
52
44
|
- name: Build locally on Ubuntu
|
|
53
45
|
run: |
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
sudo apt-get update
|
|
47
|
+
sudo apt-get install gcc-12 -y
|
|
48
|
+
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12
|
|
49
|
+
sudo update-alternatives --config gcc
|
|
50
|
+
gcc --version
|
|
57
51
|
python -m pip install .
|
|
58
52
|
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
|
59
53
|
|
|
60
|
-
- name: Build locally on
|
|
61
|
-
run:
|
|
62
|
-
|
|
63
|
-
export CXX=/usr/local/bin/g++-12
|
|
64
|
-
python -m pip install .
|
|
65
|
-
if: ${{ matrix.os == 'macOS-11' }}
|
|
54
|
+
- name: Build locally on other OS
|
|
55
|
+
run: python -m pip install .
|
|
56
|
+
if: ${{ matrix.os != 'ubuntu-22.04' }}
|
|
66
57
|
|
|
67
|
-
- name: Build locally on Windows
|
|
68
|
-
run: |
|
|
69
|
-
python -m pip install .
|
|
70
|
-
if: ${{ matrix.os == 'windows-2022' }}
|
|
71
|
-
|
|
72
58
|
- name: Test with PyTest
|
|
73
|
-
run:
|
|
59
|
+
run: |
|
|
60
|
+
python -c "import simsimd; print(simsimd.get_capabilities())"
|
|
61
|
+
pytest python/test.py
|
|
74
62
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
runs-on: ${{ matrix.os }}
|
|
63
|
+
test_javascript:
|
|
64
|
+
name: Test JavaScript
|
|
65
|
+
runs-on: ubuntu-latest
|
|
79
66
|
strategy:
|
|
80
67
|
matrix:
|
|
81
|
-
|
|
82
|
-
python-version: ["3.7"]
|
|
83
|
-
|
|
68
|
+
node-version: [18.x]
|
|
84
69
|
steps:
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
uses: actions/setup-python@v4
|
|
70
|
+
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- name: Set up Node.js
|
|
73
|
+
uses: actions/setup-node@v3
|
|
90
74
|
with:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- name: Install dependencies
|
|
94
|
-
run: |
|
|
95
|
-
python -m pip install --no-cache-dir --upgrade pip
|
|
96
|
-
pip install --no-cache-dir pytest
|
|
97
|
-
|
|
75
|
+
node-version: '18.x'
|
|
76
|
+
|
|
98
77
|
- name: Build locally
|
|
99
|
-
run:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
python -m pip install .
|
|
104
|
-
|
|
105
|
-
- name: Test with PyTest
|
|
106
|
-
run: pytest python/test.py
|
|
78
|
+
run: npm i
|
|
79
|
+
|
|
80
|
+
- name: Test
|
|
81
|
+
run: npm test
|
|
@@ -56,8 +56,9 @@ jobs:
|
|
|
56
56
|
runs-on: ${{ matrix.os }}
|
|
57
57
|
needs: versioning
|
|
58
58
|
strategy:
|
|
59
|
+
fail-fast: false
|
|
59
60
|
matrix:
|
|
60
|
-
os: [ubuntu-20.04, macOS-11
|
|
61
|
+
os: [ubuntu-20.04, macOS-11, windows-2022]
|
|
61
62
|
steps:
|
|
62
63
|
- uses: actions/checkout@v3
|
|
63
64
|
with:
|
|
@@ -75,15 +76,8 @@ jobs:
|
|
|
75
76
|
- name: Install CIBuildWheel
|
|
76
77
|
run: python -m pip install --no-cache-dir cibuildwheel
|
|
77
78
|
|
|
78
|
-
- name: Build wheels
|
|
79
|
+
- name: Build wheels
|
|
79
80
|
run: python -m cibuildwheel
|
|
80
|
-
if: matrix.os == 'ubuntu-20.04'
|
|
81
|
-
|
|
82
|
-
- name: Build wheels on MacOS
|
|
83
|
-
run: python -m cibuildwheel
|
|
84
|
-
env:
|
|
85
|
-
CIBW_ENVIRONMENT: CC=/usr/local/bin/gcc-12 CXX=/usr/local/bin/g++-12
|
|
86
|
-
if: matrix.os == 'macOS-11'
|
|
87
81
|
|
|
88
82
|
- uses: actions/upload-artifact@v3
|
|
89
83
|
with:
|
|
@@ -112,3 +106,22 @@ jobs:
|
|
|
112
106
|
packages-dir: ./dist/artifact
|
|
113
107
|
verbose: true
|
|
114
108
|
print-hash: true
|
|
109
|
+
|
|
110
|
+
publish_javascript:
|
|
111
|
+
name: Publish JavaScript
|
|
112
|
+
needs: versioning
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/checkout@v3
|
|
116
|
+
with:
|
|
117
|
+
ref: 'main'
|
|
118
|
+
- run: git submodule update --init --recursive
|
|
119
|
+
- uses: actions/setup-node@v3
|
|
120
|
+
with:
|
|
121
|
+
node-version: 18
|
|
122
|
+
- run: npm install
|
|
123
|
+
- run: npm ci
|
|
124
|
+
- run: npm test
|
|
125
|
+
- uses: JS-DevTools/npm-publish@v2
|
|
126
|
+
with:
|
|
127
|
+
token: ${{ secrets.NPM_TOKEN }}
|