usearch 0.18.3 → 0.18.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/Cargo.toml +1 -1
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_punned_dense.hpp +13 -21
- package/include/usearch/index_punned_helpers.hpp +23 -16
- package/package.json +1 -1
- package/python/lib.cpp +3 -1
- package/python/scripts/bench.py +0 -1
- package/python/scripts/test.py +6 -3
- package/setup.py +1 -1
package/Cargo.toml
CHANGED
package/conanfile.py
CHANGED
|
@@ -4,7 +4,7 @@ from conans import ConanFile
|
|
|
4
4
|
class USearchConan(ConanFile):
|
|
5
5
|
|
|
6
6
|
name = 'USearch'
|
|
7
|
-
version = '0.18.
|
|
7
|
+
version = '0.18.5'
|
|
8
8
|
license = 'Apache 2.0'
|
|
9
9
|
url = 'https://github.com/unum-cloud/usearch'
|
|
10
10
|
description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
|
|
@@ -269,7 +269,7 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct cos
|
|
|
269
269
|
ab += result_t(a[i]) * result_t(b[i]), //
|
|
270
270
|
a2 += square<result_t>(a[i]), //
|
|
271
271
|
b2 += square<result_t>(b[i]);
|
|
272
|
-
return ab ? (1 - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
|
|
272
|
+
return (ab != 0) ? (1 - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
|
|
273
273
|
}
|
|
274
274
|
};
|
|
275
275
|
|
|
@@ -36,7 +36,7 @@ struct cos_f8_t {
|
|
|
36
36
|
a2 += square(ai);
|
|
37
37
|
b2 += square(bi);
|
|
38
38
|
}
|
|
39
|
-
return 1.f - ab / (std::sqrt(a2) * std::sqrt(b2));
|
|
39
|
+
return (ab != 0) ? (1.f - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
42
|
|
|
@@ -328,7 +328,7 @@ class index_punned_dense_gt {
|
|
|
328
328
|
std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
|
|
329
329
|
|
|
330
330
|
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
|
|
331
|
-
bool casted = cast(vector_data,
|
|
331
|
+
bool casted = cast(vector_data, dimensions_, casted_data);
|
|
332
332
|
if (casted)
|
|
333
333
|
vector_data = casted_data, vector_bytes = casted_vector_bytes_, config.store_vector = true;
|
|
334
334
|
|
|
@@ -349,7 +349,7 @@ class index_punned_dense_gt {
|
|
|
349
349
|
std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
|
|
350
350
|
|
|
351
351
|
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
|
|
352
|
-
bool casted = cast(vector_data,
|
|
352
|
+
bool casted = cast(vector_data, dimensions_, casted_data);
|
|
353
353
|
if (casted)
|
|
354
354
|
vector_data = casted_data, vector_bytes = casted_vector_bytes_;
|
|
355
355
|
|
|
@@ -365,7 +365,7 @@ class index_punned_dense_gt {
|
|
|
365
365
|
std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
|
|
366
366
|
|
|
367
367
|
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
|
|
368
|
-
bool casted = cast(vector_data,
|
|
368
|
+
bool casted = cast(vector_data, dimensions_, casted_data);
|
|
369
369
|
if (casted)
|
|
370
370
|
vector_data = casted_data, vector_bytes = casted_vector_bytes_;
|
|
371
371
|
|
|
@@ -388,10 +388,10 @@ class index_punned_dense_gt {
|
|
|
388
388
|
}
|
|
389
389
|
member_citerator_t iterator = typed_->cbegin() + id;
|
|
390
390
|
member_cref_t member = *iterator;
|
|
391
|
-
byte_t const*
|
|
392
|
-
bool casted = cast(
|
|
391
|
+
byte_t const* punned_vector = reinterpret_cast<byte_t const*>(member.vector.data());
|
|
392
|
+
bool casted = cast(punned_vector, dimensions_, (byte_t*)reconstructed);
|
|
393
393
|
if (!casted)
|
|
394
|
-
std::memcpy(reconstructed,
|
|
394
|
+
std::memcpy(reconstructed, punned_vector, casted_vector_bytes_);
|
|
395
395
|
return true;
|
|
396
396
|
}
|
|
397
397
|
|
|
@@ -505,30 +505,27 @@ class index_punned_dense_gt {
|
|
|
505
505
|
static metric_and_meta_t ip_metric_f32_(std::size_t dimensions) {
|
|
506
506
|
(void)dimensions;
|
|
507
507
|
#if USEARCH_USE_SIMSIMD
|
|
508
|
-
|
|
509
|
-
if (dimensions % 4 == 0)
|
|
508
|
+
if (hardware_supports(isa_t::avx2_k) && dimensions % 4 == 0)
|
|
510
509
|
return {
|
|
511
510
|
pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
|
|
512
511
|
return 1.f - simsimd_dot_f32x4avx2(a, b, dimensions);
|
|
513
512
|
}),
|
|
514
513
|
isa_t::avx2_k,
|
|
515
514
|
};
|
|
516
|
-
|
|
517
|
-
if (supports_arm_sve())
|
|
515
|
+
if (hardware_supports(isa_t::sve_k))
|
|
518
516
|
return {
|
|
519
517
|
pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
|
|
520
518
|
return 1.f - simsimd_dot_f32sve(a, b, dimensions);
|
|
521
519
|
}),
|
|
522
520
|
isa_t::sve_k,
|
|
523
521
|
};
|
|
524
|
-
if (dimensions % 4 == 0)
|
|
522
|
+
if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
|
|
525
523
|
return {
|
|
526
524
|
pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
|
|
527
525
|
return 1.f - simsimd_dot_f32x4neon(a, b, dimensions);
|
|
528
526
|
}),
|
|
529
527
|
isa_t::neon_k,
|
|
530
528
|
};
|
|
531
|
-
#endif
|
|
532
529
|
#endif
|
|
533
530
|
return pun_metric_<f32_t>(ip_gt<f32_t>{});
|
|
534
531
|
}
|
|
@@ -536,23 +533,20 @@ class index_punned_dense_gt {
|
|
|
536
533
|
static metric_and_meta_t cos_metric_f16_(std::size_t dimensions) {
|
|
537
534
|
(void)dimensions;
|
|
538
535
|
#if USEARCH_USE_SIMSIMD
|
|
539
|
-
|
|
540
|
-
if (dimensions % 16 == 0)
|
|
536
|
+
if (hardware_supports(isa_t::avx512_k) && dimensions % 16 == 0)
|
|
541
537
|
return {
|
|
542
538
|
pun_metric_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
|
|
543
539
|
return 1.f - simsimd_cos_f16x16avx512(a, b, dimensions);
|
|
544
540
|
}),
|
|
545
541
|
isa_t::avx512_k,
|
|
546
542
|
};
|
|
547
|
-
|
|
548
|
-
if (dimensions % 4 == 0)
|
|
543
|
+
if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
|
|
549
544
|
return {
|
|
550
545
|
pun_metric_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
|
|
551
546
|
return 1.f - simsimd_cos_f16x4neon(a, b, dimensions);
|
|
552
547
|
}),
|
|
553
548
|
isa_t::neon_k,
|
|
554
549
|
};
|
|
555
|
-
#endif
|
|
556
550
|
#endif
|
|
557
551
|
return pun_metric_<f16_t>(cos_gt<f16_t, f32_t>{});
|
|
558
552
|
}
|
|
@@ -560,14 +554,12 @@ class index_punned_dense_gt {
|
|
|
560
554
|
static metric_and_meta_t cos_metric_f8_(std::size_t dimensions) {
|
|
561
555
|
(void)dimensions;
|
|
562
556
|
#if USEARCH_USE_SIMSIMD
|
|
563
|
-
|
|
564
|
-
if (dimensions % 16 == 0)
|
|
557
|
+
if (hardware_supports(isa_t::neon_k) && dimensions % 16 == 0)
|
|
565
558
|
return {
|
|
566
559
|
pun_metric_<int8_t>(
|
|
567
560
|
[=](int8_t const* a, int8_t const* b) { return 1.f - simsimd_cos_i8x16neon(a, b, dimensions); }),
|
|
568
561
|
isa_t::neon_k,
|
|
569
562
|
};
|
|
570
|
-
#endif
|
|
571
563
|
#endif
|
|
572
564
|
return pun_metric_<f8_bits_t>(cos_f8_t{dimensions});
|
|
573
565
|
}
|
|
@@ -76,13 +76,23 @@ inline char const* isa_name(isa_t isa) noexcept {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
inline bool
|
|
80
|
-
#if defined(USEARCH_DEFINED_ARM)
|
|
81
|
-
#if defined(USEARCH_DEFINED_LINUX)
|
|
79
|
+
inline bool hardware_supports(isa_t isa) noexcept {
|
|
80
|
+
#if defined(USEARCH_DEFINED_ARM) && defined(USEARCH_DEFINED_LINUX)
|
|
82
81
|
unsigned long capabilities = getauxval(AT_HWCAP);
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
switch (isa) {
|
|
83
|
+
case isa_t::neon_k: return capabilities & HWCAP_NEON;
|
|
84
|
+
case isa_t::sve_k: return capabilities & HWCAP_SVE;
|
|
85
|
+
default: return false;
|
|
86
|
+
}
|
|
85
87
|
#endif
|
|
88
|
+
|
|
89
|
+
#if defined(USEARCH_DEFINED_X86) && defined(USEARCH_DEFINED_GCC)
|
|
90
|
+
__builtin_cpu_init();
|
|
91
|
+
switch (isa) {
|
|
92
|
+
case isa_t::avx2_k: return __builtin_cpu_supports("avx2");
|
|
93
|
+
case isa_t::avx512_k: return __builtin_cpu_supports("avx512f");
|
|
94
|
+
default: return false;
|
|
95
|
+
}
|
|
86
96
|
#endif
|
|
87
97
|
return false;
|
|
88
98
|
}
|
|
@@ -482,12 +492,11 @@ using memory_mapping_allocator_t = aligned_allocator_t;
|
|
|
482
492
|
#endif
|
|
483
493
|
|
|
484
494
|
template <typename from_scalar_at, typename to_scalar_at> struct cast_gt {
|
|
485
|
-
inline bool operator()(byte_t const* input, std::size_t
|
|
495
|
+
inline bool operator()(byte_t const* input, std::size_t dimensions, byte_t* output) const {
|
|
486
496
|
from_scalar_at const* typed_input = reinterpret_cast<from_scalar_at const*>(input);
|
|
487
497
|
to_scalar_at* typed_output = reinterpret_cast<to_scalar_at*>(output);
|
|
488
|
-
std::size_t size_input = bytes_in_input / sizeof(from_scalar_at);
|
|
489
498
|
auto converter = [](from_scalar_at from) { return to_scalar_at(from); };
|
|
490
|
-
std::transform(typed_input, typed_input +
|
|
499
|
+
std::transform(typed_input, typed_input + dimensions, typed_output, converter);
|
|
491
500
|
return true;
|
|
492
501
|
}
|
|
493
502
|
};
|
|
@@ -513,23 +522,21 @@ template <> struct cast_gt<b1x8_t, b1x8_t> {
|
|
|
513
522
|
};
|
|
514
523
|
|
|
515
524
|
template <typename from_scalar_at> struct cast_gt<from_scalar_at, b1x8_t> {
|
|
516
|
-
inline bool operator()(byte_t const* input, std::size_t
|
|
525
|
+
inline bool operator()(byte_t const* input, std::size_t dimensions, byte_t* output) const {
|
|
517
526
|
from_scalar_at const* typed_input = reinterpret_cast<from_scalar_at const*>(input);
|
|
518
527
|
unsigned char* typed_output = reinterpret_cast<unsigned char*>(output);
|
|
519
|
-
std::size_t
|
|
520
|
-
|
|
521
|
-
typed_output[i / CHAR_BIT] |= bool(typed_input[i]) ? (1 << (i & (CHAR_BIT - 1))) : 0;
|
|
528
|
+
for (std::size_t i = 0; i != dimensions; ++i)
|
|
529
|
+
typed_output[i / CHAR_BIT] |= bool(typed_input[i]) ? (128 >> (i & (CHAR_BIT - 1))) : 0;
|
|
522
530
|
return true;
|
|
523
531
|
}
|
|
524
532
|
};
|
|
525
533
|
|
|
526
534
|
template <typename to_scalar_at> struct cast_gt<b1x8_t, to_scalar_at> {
|
|
527
|
-
inline bool operator()(byte_t const* input, std::size_t
|
|
535
|
+
inline bool operator()(byte_t const* input, std::size_t dimensions, byte_t* output) const {
|
|
528
536
|
unsigned char const* typed_input = reinterpret_cast<unsigned char const*>(input);
|
|
529
537
|
to_scalar_at* typed_output = reinterpret_cast<to_scalar_at*>(output);
|
|
530
|
-
std::size_t
|
|
531
|
-
|
|
532
|
-
typed_output[i] = bool(typed_input[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))));
|
|
538
|
+
for (std::size_t i = 0; i != dimensions; ++i)
|
|
539
|
+
typed_output[i] = bool(typed_input[i / CHAR_BIT] & (128 >> (i & (CHAR_BIT - 1))));
|
|
533
540
|
return true;
|
|
534
541
|
}
|
|
535
542
|
};
|
package/package.json
CHANGED
package/python/lib.cpp
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @copyright Copyright (c) 2023
|
|
14
14
|
*/
|
|
15
15
|
#define PY_SSIZE_T_CLEAN
|
|
16
|
+
#define NOMINMAX // Some of our dependencies call `std::max(x, y)`, which crashes Windows builds
|
|
16
17
|
#include <thread>
|
|
17
18
|
|
|
18
19
|
#include <pybind11/numpy.h>
|
|
@@ -393,7 +394,8 @@ template <typename index_at> void view_index(index_at& index, std::string const&
|
|
|
393
394
|
|
|
394
395
|
template <typename internal_at, typename external_at = internal_at, typename index_at = void>
|
|
395
396
|
py::object get_typed_member(index_at const& index, label_t label) {
|
|
396
|
-
|
|
397
|
+
std::size_t result_slots = std::is_same<internal_at, b1x8_t>() ? index.scalar_words() : index.dimensions();
|
|
398
|
+
py::array_t<external_at> result_py(static_cast<Py_ssize_t>(result_slots));
|
|
397
399
|
auto result_py1d = result_py.template mutable_unchecked<1>();
|
|
398
400
|
if (!index.get(label, (internal_at*)&result_py1d(0)))
|
|
399
401
|
return py::none();
|
package/python/scripts/bench.py
CHANGED
package/python/scripts/test.py
CHANGED
|
@@ -5,7 +5,7 @@ import numpy as np
|
|
|
5
5
|
from usearch.io import load_matrix, save_matrix
|
|
6
6
|
from usearch.eval import recall_members, random_vectors
|
|
7
7
|
|
|
8
|
-
from usearch.index import Index, SparseIndex, MetricKind, ScalarKind, Matches
|
|
8
|
+
from usearch.index import Index, SparseIndex, MetricKind, ScalarKind, Matches, MetricKindBitwise
|
|
9
9
|
from usearch.index import _normalize_dtype
|
|
10
10
|
from usearch.index import (
|
|
11
11
|
DEFAULT_CONNECTIVITY,
|
|
@@ -181,10 +181,13 @@ def test_bitwise_index(bits: int, metric: MetricKind, connectivity: int, batch_s
|
|
|
181
181
|
index = Index(ndim=bits, metric=metric, connectivity=connectivity)
|
|
182
182
|
|
|
183
183
|
labels = np.arange(batch_size)
|
|
184
|
-
|
|
185
|
-
bit_vectors = np.packbits(
|
|
184
|
+
byte_vectors = np.random.randint(2, size=(batch_size, bits))
|
|
185
|
+
bit_vectors = np.packbits(byte_vectors, axis=1)
|
|
186
186
|
|
|
187
187
|
index.add(labels, bit_vectors)
|
|
188
|
+
assert np.allclose(index.get_vectors(labels), byte_vectors, atol=0.1)
|
|
189
|
+
assert np.alltrue(index.get_vectors(labels, ScalarKind.B1) == bit_vectors)
|
|
190
|
+
|
|
188
191
|
index.search(bit_vectors, 10)
|
|
189
192
|
|
|
190
193
|
|