usearch 0.19.1 → 0.19.3
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/c/lib.cpp +1 -2
- package/conanfile.py +1 -1
- package/cpp/bench.cpp +3 -3
- package/include/usearch/index.hpp +4 -0
- package/include/usearch/index_punned_dense.hpp +1 -1
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +25 -23
- package/javascript/lib.cpp +10 -11
- package/javascript/test.js +1 -1
- package/objc/USearchObjective.mm +15 -12
- package/objc/include/USearchObjective.h +3 -3
- package/package.json +2 -2
- package/python/lib.cpp +43 -48
- package/python/scripts/bench.py +10 -10
- package/python/scripts/test.py +15 -7
- package/python/usearch/index.py +8 -26
- package/python/usearch/numba.py +28 -22
- package/rust/lib.cpp +13 -13
- package/rust/lib.hpp +10 -6
- package/rust/lib.rs +2 -2
- package/swift/Index+Sugar.swift +13 -9
- package/wolfram/lib.cpp +26 -19
package/Cargo.toml
CHANGED
package/c/lib.cpp
CHANGED
|
@@ -15,8 +15,7 @@ using index_t = index_punned_dense_gt<label_t>;
|
|
|
15
15
|
using add_result_t = index_t::add_result_t;
|
|
16
16
|
using search_result_t = index_t::search_result_t;
|
|
17
17
|
using serialization_result_t = index_t::serialization_result_t;
|
|
18
|
-
|
|
19
|
-
using span_t = span_gt<float>;
|
|
18
|
+
using vector_view_t = span_gt<float>;
|
|
20
19
|
|
|
21
20
|
// helper functions that are not part of the C ABI
|
|
22
21
|
metric_kind_t to_native_metric(usearch_metric_kind_t kind) {
|
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.19.
|
|
7
|
+
version = '0.19.3'
|
|
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'
|
package/cpp/bench.cpp
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* @brief A benchmark for the construction speed of the USearch index
|
|
3
|
+
* and the resulting accuracy (recall) of the Approximate Nearest Neighbors
|
|
4
|
+
* Search queries.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
|
@@ -48,8 +48,11 @@
|
|
|
48
48
|
// OS-specific includes
|
|
49
49
|
#if defined(USEARCH_DEFINED_WINDOWS)
|
|
50
50
|
#define _USE_MATH_DEFINES
|
|
51
|
+
#define NOMINMAX
|
|
51
52
|
#include <Windows.h>
|
|
52
53
|
#include <sys/stat.h> // `fstat` for file size
|
|
54
|
+
#undef NOMINMAX
|
|
55
|
+
#undef _USE_MATH_DEFINES
|
|
53
56
|
#else
|
|
54
57
|
#include <fcntl.h> // `fallocate`
|
|
55
58
|
#include <stdlib.h> // `posix_memalign`
|
|
@@ -1305,6 +1308,7 @@ class index_gt {
|
|
|
1305
1308
|
using id_t = id_at;
|
|
1306
1309
|
using allocator_t = allocator_at;
|
|
1307
1310
|
using point_allocator_t = point_allocator_at;
|
|
1311
|
+
static_assert(sizeof(label_t) >= sizeof(id_t), "Having tiny labels doesn't make sense.");
|
|
1308
1312
|
|
|
1309
1313
|
using scalar_t = typename metric_t::scalar_t;
|
|
1310
1314
|
using vector_view_t = span_gt<scalar_t const>;
|
|
@@ -652,7 +652,7 @@ class index_punned_dense_gt {
|
|
|
652
652
|
}
|
|
653
653
|
};
|
|
654
654
|
|
|
655
|
-
using punned_small_t = index_punned_dense_gt<std::
|
|
655
|
+
using punned_small_t = index_punned_dense_gt<std::uint64_t, std::uint32_t>;
|
|
656
656
|
using punned_big_t = index_punned_dense_gt<uuid_t, uint40_t>;
|
|
657
657
|
|
|
658
658
|
} // namespace usearch
|
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
using namespace unum::usearch;
|
|
10
10
|
using namespace unum;
|
|
11
11
|
|
|
12
|
-
using
|
|
13
|
-
using span_t = span_gt<float>;
|
|
12
|
+
using metric_t = index_punned_dense_metric_t;
|
|
14
13
|
using distance_t = punned_distance_t;
|
|
15
|
-
using
|
|
14
|
+
using index_t = punned_small_t;
|
|
16
15
|
|
|
17
|
-
using add_result_t = typename
|
|
18
|
-
using search_result_t = typename
|
|
19
|
-
using serialization_result_t = typename
|
|
16
|
+
using add_result_t = typename index_t::add_result_t;
|
|
17
|
+
using search_result_t = typename index_t::search_result_t;
|
|
18
|
+
using serialization_result_t = typename index_t::serialization_result_t;
|
|
19
|
+
using label_t = typename index_t::label_t;
|
|
20
|
+
using id_t = typename index_t::id_t;
|
|
21
|
+
using vector_view_t = unum::usearch::span_gt<float>;
|
|
20
22
|
|
|
21
23
|
JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
|
|
22
24
|
JNIEnv* env, jclass, //
|
|
@@ -41,14 +43,14 @@ JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
|
|
|
41
43
|
|
|
42
44
|
scalar_kind_t accuracy = scalar_kind_from_name(accuracy_cstr, accuracy_length);
|
|
43
45
|
metric_kind_t metric_kind = metric_from_name(metric_cstr, metric_length);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
index_t index = index_t::make( //
|
|
47
|
+
static_cast<std::size_t>(dimensions), metric_kind, config, accuracy, expansion_add, expansion_search);
|
|
46
48
|
if (!index.reserve(static_cast<std::size_t>(capacity))) {
|
|
47
49
|
jclass jc = (*env).FindClass("java/lang/Error");
|
|
48
50
|
if (jc)
|
|
49
51
|
(*env).ThrowNew(jc, "Failed to reserve desired capacity!");
|
|
50
52
|
} else {
|
|
51
|
-
|
|
53
|
+
index_t* result_ptr = new index_t(std::move(index));
|
|
52
54
|
std::memcpy(&result, &result_ptr, sizeof(jlong));
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -65,7 +67,7 @@ JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
|
|
|
65
67
|
|
|
66
68
|
JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1save(JNIEnv* env, jclass, jlong c_ptr, jstring path) {
|
|
67
69
|
char const* path_cstr = (*env).GetStringUTFChars(path, 0);
|
|
68
|
-
if (!reinterpret_cast<
|
|
70
|
+
if (!reinterpret_cast<index_t*>(c_ptr)->save(path_cstr)) {
|
|
69
71
|
jclass jc = (*env).FindClass("java/lang/Error");
|
|
70
72
|
if (jc)
|
|
71
73
|
(*env).ThrowNew(jc, "Failed to dump vector index to path!");
|
|
@@ -75,7 +77,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1save(JNIEnv* env, jclass
|
|
|
75
77
|
|
|
76
78
|
JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1load(JNIEnv* env, jclass, jlong c_ptr, jstring path) {
|
|
77
79
|
char const* path_cstr = (*env).GetStringUTFChars(path, 0);
|
|
78
|
-
if (!reinterpret_cast<
|
|
80
|
+
if (!reinterpret_cast<index_t*>(c_ptr)->load(path_cstr)) {
|
|
79
81
|
jclass jc = (*env).FindClass("java/lang/Error");
|
|
80
82
|
if (jc)
|
|
81
83
|
(*env).ThrowNew(jc, "Failed to load vector index from path!");
|
|
@@ -85,7 +87,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1load(JNIEnv* env, jclass
|
|
|
85
87
|
|
|
86
88
|
JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view(JNIEnv* env, jclass, jlong c_ptr, jstring path) {
|
|
87
89
|
char const* path_cstr = (*env).GetStringUTFChars(path, 0);
|
|
88
|
-
if (!reinterpret_cast<
|
|
90
|
+
if (!reinterpret_cast<index_t*>(c_ptr)->view(path_cstr)) {
|
|
89
91
|
jclass jc = (*env).FindClass("java/lang/Error");
|
|
90
92
|
if (jc)
|
|
91
93
|
(*env).ThrowNew(jc, "Failed to view vector index from path!");
|
|
@@ -94,27 +96,27 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view(JNIEnv* env, jclass
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1destroy(JNIEnv*, jclass, jlong c_ptr) {
|
|
97
|
-
delete reinterpret_cast<
|
|
99
|
+
delete reinterpret_cast<index_t*>(c_ptr);
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1size(JNIEnv*, jclass, jlong c_ptr) {
|
|
101
|
-
return reinterpret_cast<
|
|
103
|
+
return reinterpret_cast<index_t*>(c_ptr)->size();
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1connectivity(JNIEnv*, jclass, jlong c_ptr) {
|
|
105
|
-
return reinterpret_cast<
|
|
107
|
+
return reinterpret_cast<index_t*>(c_ptr)->connectivity();
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1dimensions(JNIEnv*, jclass, jlong c_ptr) {
|
|
109
|
-
return reinterpret_cast<
|
|
111
|
+
return reinterpret_cast<index_t*>(c_ptr)->dimensions();
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1capacity(JNIEnv*, jclass, jlong c_ptr) {
|
|
113
|
-
return reinterpret_cast<
|
|
115
|
+
return reinterpret_cast<index_t*>(c_ptr)->capacity();
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1reserve(JNIEnv* env, jclass, jlong c_ptr, jlong capacity) {
|
|
117
|
-
if (!reinterpret_cast<
|
|
119
|
+
if (!reinterpret_cast<index_t*>(c_ptr)->reserve(static_cast<std::size_t>(capacity))) {
|
|
118
120
|
jclass jc = (*env).FindClass("java/lang/Error");
|
|
119
121
|
if (jc)
|
|
120
122
|
(*env).ThrowNew(jc, "Failed to grow vector index!");
|
|
@@ -126,9 +128,9 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1add( //
|
|
|
126
128
|
|
|
127
129
|
jfloat* vector_data = (*env).GetFloatArrayElements(vector, 0);
|
|
128
130
|
jsize vector_dims = (*env).GetArrayLength(vector);
|
|
129
|
-
|
|
131
|
+
vector_view_t vector_span = vector_view_t{vector_data, static_cast<std::size_t>(vector_dims)};
|
|
130
132
|
|
|
131
|
-
if (!reinterpret_cast<
|
|
133
|
+
if (!reinterpret_cast<index_t*>(c_ptr)->add(static_cast<label_t>(label), vector_span)) {
|
|
132
134
|
jclass jc = (*env).FindClass("java/lang/Error");
|
|
133
135
|
if (jc)
|
|
134
136
|
(*env).ThrowNew(jc, "Failed to insert a new point in vector index!");
|
|
@@ -150,10 +152,10 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
|
|
|
150
152
|
|
|
151
153
|
jfloat* vector_data = (*env).GetFloatArrayElements(vector, 0);
|
|
152
154
|
jsize vector_dims = (*env).GetArrayLength(vector);
|
|
153
|
-
|
|
154
|
-
search_result_t result = reinterpret_cast<
|
|
155
|
+
vector_view_t vector_span = vector_view_t{vector_data, static_cast<std::size_t>(vector_dims)};
|
|
156
|
+
search_result_t result = reinterpret_cast<index_t*>(c_ptr)->search(vector_span, static_cast<std::size_t>(wanted));
|
|
155
157
|
if (result) {
|
|
156
|
-
std::size_t found = result.dump_to(matches_data, NULL);
|
|
158
|
+
std::size_t found = result.dump_to(reinterpret_cast<label_t*>(matches_data), NULL);
|
|
157
159
|
(*env).SetIntArrayRegion(matches, 0, found, matches_data);
|
|
158
160
|
} else {
|
|
159
161
|
jclass jc = (*env).FindClass("java/lang/Error");
|
package/javascript/lib.cpp
CHANGED
|
@@ -9,14 +9,9 @@
|
|
|
9
9
|
* @see NodeJS docs: https://nodejs.org/api/addons.html#hello-world
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
|
14
|
-
#define _USE_MATH_DEFINES
|
|
15
|
-
#define NAPI_CPP_EXCEPTIONS
|
|
16
|
-
#endif
|
|
17
|
-
|
|
18
12
|
#include <new> // `std::bad_alloc`
|
|
19
13
|
|
|
14
|
+
#define NAPI_CPP_EXCEPTIONS
|
|
20
15
|
#include <napi.h>
|
|
21
16
|
#include <node_api.h>
|
|
22
17
|
|
|
@@ -25,9 +20,13 @@
|
|
|
25
20
|
using namespace unum::usearch;
|
|
26
21
|
using namespace unum;
|
|
27
22
|
|
|
28
|
-
using
|
|
23
|
+
using metric_t = index_punned_dense_metric_t;
|
|
29
24
|
using distance_t = punned_distance_t;
|
|
30
|
-
using
|
|
25
|
+
using index_t = punned_small_t;
|
|
26
|
+
using add_result_t = typename index_t::add_result_t;
|
|
27
|
+
using search_result_t = typename index_t::search_result_t;
|
|
28
|
+
using label_t = typename index_t::label_t;
|
|
29
|
+
using id_t = typename index_t::id_t;
|
|
31
30
|
|
|
32
31
|
class Index : public Napi::ObjectWrap<Index> {
|
|
33
32
|
public:
|
|
@@ -47,7 +46,7 @@ class Index : public Napi::ObjectWrap<Index> {
|
|
|
47
46
|
void Add(Napi::CallbackInfo const& ctx);
|
|
48
47
|
Napi::Value Search(Napi::CallbackInfo const& ctx);
|
|
49
48
|
|
|
50
|
-
std::unique_ptr<
|
|
49
|
+
std::unique_ptr<index_t> native_;
|
|
51
50
|
};
|
|
52
51
|
|
|
53
52
|
Napi::Object Index::Init(Napi::Env env, Napi::Object exports) {
|
|
@@ -124,7 +123,7 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
|
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
native_.reset(
|
|
127
|
-
new
|
|
126
|
+
new index_t(index_t::make(dimensions, metric_kind, config, accuracy, expansion_add, expansion_search)));
|
|
128
127
|
native_->reserve(limits);
|
|
129
128
|
}
|
|
130
129
|
|
|
@@ -235,7 +234,7 @@ Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
|
|
|
235
234
|
return {};
|
|
236
235
|
}
|
|
237
236
|
|
|
238
|
-
Napi::
|
|
237
|
+
Napi::TypedArrayOf<label_t> matches_js = Napi::TypedArrayOf<label_t>::New(env, wanted);
|
|
239
238
|
Napi::Float32Array distances_js = Napi::Float32Array::New(env, wanted);
|
|
240
239
|
try {
|
|
241
240
|
std::size_t count = native_->search(vector, wanted).dump_to(matches_js.Data(), distances_js.Data());
|
package/javascript/test.js
CHANGED
|
@@ -11,7 +11,7 @@ index.add(16, new Float32Array([10, 25]))
|
|
|
11
11
|
assert.equal(index.size(), 2)
|
|
12
12
|
|
|
13
13
|
var results = index.search(new Float32Array([13, 14]), 2)
|
|
14
|
-
assert.deepEqual(results.labels, new
|
|
14
|
+
assert.deepEqual(results.labels, new BigUint64Array([15n, 16n]))
|
|
15
15
|
assert.deepEqual(results.distances, new Float32Array([45, 130]))
|
|
16
16
|
|
|
17
17
|
console.log('JavaScript tests passed!')
|
package/objc/USearchObjective.mm
CHANGED
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
using namespace unum::usearch;
|
|
9
9
|
using namespace unum;
|
|
10
10
|
|
|
11
|
+
using metric_t = index_punned_dense_metric_t;
|
|
11
12
|
using distance_t = punned_distance_t;
|
|
12
|
-
using
|
|
13
|
-
using add_result_t =
|
|
14
|
-
using search_result_t =
|
|
15
|
-
using serialization_result_t =
|
|
16
|
-
using
|
|
13
|
+
using index_t = punned_small_t;
|
|
14
|
+
using add_result_t = typename index_t::add_result_t;
|
|
15
|
+
using search_result_t = typename index_t::search_result_t;
|
|
16
|
+
using serialization_result_t = typename index_t::serialization_result_t;
|
|
17
|
+
using label_t = typename index_t::label_t;
|
|
18
|
+
using id_t = typename index_t::id_t;
|
|
19
|
+
using shared_index_t = std::shared_ptr<index_t>;
|
|
17
20
|
|
|
18
21
|
metric_kind_t to_native_metric(USearchMetric m) {
|
|
19
22
|
switch (m) {
|
|
@@ -117,11 +120,11 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
|
|
|
117
120
|
index_config_t config;
|
|
118
121
|
|
|
119
122
|
config.connectivity = static_cast<std::size_t>(connectivity);
|
|
120
|
-
shared_index_t ptr = std::make_shared<
|
|
123
|
+
shared_index_t ptr = std::make_shared<index_t>(index_t::make(dims, to_native_metric(metric), config, to_native_scalar(quantization)));
|
|
121
124
|
return [[USearchIndex alloc] initWithIndex:ptr];
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
- (void)addSingle:(
|
|
127
|
+
- (void)addSingle:(USearchLabel)label
|
|
125
128
|
vector:(Float32 const *_Nonnull)vector {
|
|
126
129
|
add_result_t result = _native->add(label, vector);
|
|
127
130
|
|
|
@@ -134,7 +137,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
|
|
|
134
137
|
|
|
135
138
|
- (UInt32)searchSingle:(Float32 const *_Nonnull)vector
|
|
136
139
|
count:(UInt32)wanted
|
|
137
|
-
labels:(
|
|
140
|
+
labels:(USearchLabel *_Nullable)labels
|
|
138
141
|
distances:(Float32 *_Nullable)distances {
|
|
139
142
|
search_result_t result = _native->search(vector, static_cast<std::size_t>(wanted));
|
|
140
143
|
|
|
@@ -148,7 +151,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
|
|
|
148
151
|
return static_cast<UInt32>(found);
|
|
149
152
|
}
|
|
150
153
|
|
|
151
|
-
- (void)addDouble:(
|
|
154
|
+
- (void)addDouble:(USearchLabel)label
|
|
152
155
|
vector:(Float64 const *_Nonnull)vector {
|
|
153
156
|
add_result_t result = _native->add(label, (f64_t const *)vector);
|
|
154
157
|
|
|
@@ -161,7 +164,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
|
|
|
161
164
|
|
|
162
165
|
- (UInt32)searchDouble:(Float64 const *_Nonnull)vector
|
|
163
166
|
count:(UInt32)wanted
|
|
164
|
-
labels:(
|
|
167
|
+
labels:(USearchLabel *_Nullable)labels
|
|
165
168
|
distances:(Float32 *_Nullable)distances {
|
|
166
169
|
search_result_t result = _native->search((f64_t const *)vector, static_cast<std::size_t>(wanted));
|
|
167
170
|
|
|
@@ -175,7 +178,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
|
|
|
175
178
|
return static_cast<UInt32>(found);
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
- (void)addHalf:(
|
|
181
|
+
- (void)addHalf:(USearchLabel)label
|
|
179
182
|
vector:(void const *_Nonnull)vector {
|
|
180
183
|
add_result_t result = _native->add(label, (f16_t const *)vector);
|
|
181
184
|
|
|
@@ -188,7 +191,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
|
|
|
188
191
|
|
|
189
192
|
- (UInt32)searchHalf:(void const *_Nonnull)vector
|
|
190
193
|
count:(UInt32)wanted
|
|
191
|
-
labels:(
|
|
194
|
+
labels:(USearchLabel *_Nullable)labels
|
|
192
195
|
distances:(Float32 *_Nullable)distances {
|
|
193
196
|
search_result_t result = _native->search((f16_t const *)vector, static_cast<std::size_t>(wanted));
|
|
194
197
|
|
|
@@ -25,7 +25,7 @@ typedef NS_ENUM(NSUInteger, USearchMetric) {
|
|
|
25
25
|
USearchMetricSorensen
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
typedef
|
|
28
|
+
typedef UInt64 USearchLabel;
|
|
29
29
|
|
|
30
30
|
API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
|
|
31
31
|
@interface USearchIndex : NSObject
|
|
@@ -80,7 +80,7 @@ API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
|
|
|
80
80
|
* @brief Adds a labeled vector to the index.
|
|
81
81
|
* @param vector Double-precision vector.
|
|
82
82
|
*/
|
|
83
|
-
- (void)addDouble:(
|
|
83
|
+
- (void)addDouble:(USearchLabel)label
|
|
84
84
|
vector:(Float64 const *_Nonnull)vector NS_SWIFT_NAME(addDouble(label:vector:));
|
|
85
85
|
|
|
86
86
|
/**
|
|
@@ -100,7 +100,7 @@ API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
|
|
|
100
100
|
* @brief Adds a labeled vector to the index.
|
|
101
101
|
* @param vector Half-precision vector.
|
|
102
102
|
*/
|
|
103
|
-
- (void)addHalf:(
|
|
103
|
+
- (void)addHalf:(USearchLabel)label
|
|
104
104
|
vector:(void const *_Nonnull)vector NS_SWIFT_NAME(addHalf(label:vector:));
|
|
105
105
|
|
|
106
106
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usearch",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"description": "Smaller & Faster Single-File Vector Search Engine from Unum",
|
|
5
5
|
"author": "Ashot Vardanian",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -24,6 +24,6 @@
|
|
|
24
24
|
"@semantic-release/exec": "^6.0.3",
|
|
25
25
|
"@semantic-release/git": "^10.0.1",
|
|
26
26
|
"conventional-changelog-eslint": "^3.0.9",
|
|
27
|
-
"semantic-release": "^21.0.
|
|
27
|
+
"semantic-release": "^21.0.7"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/python/lib.cpp
CHANGED
|
@@ -12,11 +12,10 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @copyright Copyright (c) 2023
|
|
14
14
|
*/
|
|
15
|
-
#define PY_SSIZE_T_CLEAN
|
|
16
|
-
#define NOMINMAX // Some of our dependencies call `std::max(x, y)`, which crashes Windows builds
|
|
17
15
|
#include <limits> // `std::numeric_limits`
|
|
18
16
|
#include <thread> // `std::thread`
|
|
19
17
|
|
|
18
|
+
#define PY_SSIZE_T_CLEAN
|
|
20
19
|
#include <pybind11/numpy.h>
|
|
21
20
|
#include <pybind11/pybind11.h>
|
|
22
21
|
|
|
@@ -38,26 +37,23 @@ enum class metric_signature_t {
|
|
|
38
37
|
|
|
39
38
|
namespace py = pybind11;
|
|
40
39
|
using py_shape_t = py::array::ShapeContainer;
|
|
41
|
-
|
|
42
|
-
using
|
|
43
|
-
using distance_t = punned_distance_t;
|
|
40
|
+
using label_t = typename punned_small_t::label_t;
|
|
41
|
+
using id_t = typename punned_small_t::id_t;
|
|
44
42
|
using metric_t = index_punned_dense_metric_t;
|
|
45
|
-
using
|
|
46
|
-
using
|
|
47
|
-
|
|
48
|
-
using
|
|
49
|
-
using punned_add_result_t = typename punned_index_t::add_result_t;
|
|
50
|
-
using punned_search_result_t = typename punned_index_t::search_result_t;
|
|
43
|
+
using distance_t = punned_distance_t;
|
|
44
|
+
using dense_index_t = punned_small_t;
|
|
45
|
+
using dense_add_result_t = typename dense_index_t::add_result_t;
|
|
46
|
+
using dense_search_result_t = typename dense_index_t::search_result_t;
|
|
51
47
|
|
|
52
|
-
struct
|
|
53
|
-
using native_t =
|
|
48
|
+
struct dense_index_py_t : public dense_index_t {
|
|
49
|
+
using native_t = dense_index_t;
|
|
54
50
|
using native_t::add;
|
|
55
51
|
using native_t::capacity;
|
|
56
52
|
using native_t::reserve;
|
|
57
53
|
using native_t::search;
|
|
58
54
|
using native_t::size;
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
dense_index_py_t(native_t&& base) : native_t(std::move(base)) {}
|
|
61
57
|
};
|
|
62
58
|
|
|
63
59
|
using set_member_t = std::uint32_t;
|
|
@@ -121,7 +117,7 @@ metric_t udf(metric_kind_t kind, metric_signature_t signature, std::uintptr_t me
|
|
|
121
117
|
}
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
static
|
|
120
|
+
static dense_index_py_t make_index( //
|
|
125
121
|
std::size_t dimensions, //
|
|
126
122
|
scalar_kind_t scalar_kind, //
|
|
127
123
|
metric_kind_t metric_kind, //
|
|
@@ -136,14 +132,14 @@ static punned_index_py_t make_index( //
|
|
|
136
132
|
config.connectivity = connectivity;
|
|
137
133
|
|
|
138
134
|
if (tune)
|
|
139
|
-
config =
|
|
135
|
+
config = dense_index_t::optimize(config);
|
|
140
136
|
|
|
141
137
|
if (metric_uintptr)
|
|
142
|
-
return
|
|
138
|
+
return dense_index_t::make( //
|
|
143
139
|
dimensions, udf(metric_kind, metric_signature, metric_uintptr, scalar_kind), config, scalar_kind,
|
|
144
140
|
expansion_add, expansion_search);
|
|
145
141
|
else
|
|
146
|
-
return
|
|
142
|
+
return dense_index_t::make(dimensions, metric_kind, config, scalar_kind, expansion_add, expansion_search);
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
static std::unique_ptr<sparse_index_py_t> make_sparse_index( //
|
|
@@ -173,7 +169,7 @@ scalar_kind_t numpy_string_to_kind(std::string const& name) {
|
|
|
173
169
|
return scalar_kind_t::unknown_k;
|
|
174
170
|
}
|
|
175
171
|
|
|
176
|
-
static void add_one_to_index(
|
|
172
|
+
static void add_one_to_index(dense_index_py_t& index, label_t label, py::buffer vector, bool copy, std::size_t) {
|
|
177
173
|
|
|
178
174
|
py::buffer_info vector_info = vector.request();
|
|
179
175
|
if (vector_info.ndim != 1)
|
|
@@ -203,7 +199,7 @@ static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer
|
|
|
203
199
|
|
|
204
200
|
template <typename scalar_at>
|
|
205
201
|
static void add_typed_to_index( //
|
|
206
|
-
|
|
202
|
+
dense_index_py_t& index, //
|
|
207
203
|
py::buffer_info const& labels_info, py::buffer_info const& vectors_info, //
|
|
208
204
|
bool copy, std::size_t threads) {
|
|
209
205
|
|
|
@@ -223,8 +219,8 @@ static void add_typed_to_index( //
|
|
|
223
219
|
});
|
|
224
220
|
}
|
|
225
221
|
|
|
226
|
-
static void add_many_to_index(
|
|
227
|
-
|
|
222
|
+
static void add_many_to_index( //
|
|
223
|
+
dense_index_py_t& index, py::buffer labels, py::buffer vectors, //
|
|
228
224
|
bool copy, std::size_t threads) {
|
|
229
225
|
|
|
230
226
|
if (index.limits().threads_add < threads)
|
|
@@ -265,7 +261,7 @@ static void add_many_to_index( //
|
|
|
265
261
|
}
|
|
266
262
|
}
|
|
267
263
|
|
|
268
|
-
static py::tuple search_one_in_index(
|
|
264
|
+
static py::tuple search_one_in_index(dense_index_py_t& index, py::buffer vector, std::size_t wanted, bool exact) {
|
|
269
265
|
|
|
270
266
|
py::buffer_info vector_info = vector.request();
|
|
271
267
|
Py_ssize_t vector_dimensions = vector_info.shape[0];
|
|
@@ -282,7 +278,7 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
|
|
|
282
278
|
search_config_t config;
|
|
283
279
|
config.exact = exact;
|
|
284
280
|
|
|
285
|
-
auto raise_and_dump = [&](
|
|
281
|
+
auto raise_and_dump = [&](dense_search_result_t result) {
|
|
286
282
|
result.error.raise();
|
|
287
283
|
count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
|
|
288
284
|
};
|
|
@@ -308,9 +304,9 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
|
|
|
308
304
|
}
|
|
309
305
|
|
|
310
306
|
template <typename scalar_at>
|
|
311
|
-
static void search_typed(
|
|
312
|
-
|
|
313
|
-
std::size_t wanted, bool exact, std::size_t threads,
|
|
307
|
+
static void search_typed( //
|
|
308
|
+
dense_index_py_t& index, py::buffer_info& vectors_info, //
|
|
309
|
+
std::size_t wanted, bool exact, std::size_t threads, //
|
|
314
310
|
py::array_t<label_t>& labels_py, py::array_t<distance_t>& distances_py, py::array_t<Py_ssize_t>& counts_py) {
|
|
315
311
|
|
|
316
312
|
auto labels_py2d = labels_py.template mutable_unchecked<2>();
|
|
@@ -325,7 +321,7 @@ static void search_typed( //
|
|
|
325
321
|
config.thread = thread_idx;
|
|
326
322
|
config.exact = exact;
|
|
327
323
|
scalar_at const* vector = (scalar_at const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
328
|
-
|
|
324
|
+
dense_search_result_t result = index.search(vector, wanted, config);
|
|
329
325
|
result.error.raise();
|
|
330
326
|
counts_py1d(task_idx) =
|
|
331
327
|
static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
|
|
@@ -344,7 +340,7 @@ static void search_typed( //
|
|
|
344
340
|
* 3. array with match counts.
|
|
345
341
|
*/
|
|
346
342
|
static py::tuple search_many_in_index( //
|
|
347
|
-
|
|
343
|
+
dense_index_py_t& index, py::buffer vectors, std::size_t wanted, bool exact, std::size_t threads) {
|
|
348
344
|
|
|
349
345
|
if (wanted == 0)
|
|
350
346
|
return py::tuple(3);
|
|
@@ -591,7 +587,7 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
591
587
|
h.def_readonly("bytes_for_vectors", &file_head_result_t::bytes_for_vectors);
|
|
592
588
|
h.def_readonly("bytes_checksum", &file_head_result_t::bytes_checksum);
|
|
593
589
|
|
|
594
|
-
auto i = py::class_<
|
|
590
|
+
auto i = py::class_<dense_index_py_t>(m, "Index");
|
|
595
591
|
|
|
596
592
|
i.def(py::init(&make_index), //
|
|
597
593
|
py::kw_only(), //
|
|
@@ -632,30 +628,29 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
632
628
|
py::arg("threads") = 0 //
|
|
633
629
|
);
|
|
634
630
|
|
|
635
|
-
i.def("__len__", &
|
|
636
|
-
i.def_property_readonly("size", &
|
|
637
|
-
i.def_property_readonly("ndim", &
|
|
638
|
-
i.def_property_readonly("connectivity", &
|
|
639
|
-
i.def_property_readonly("capacity", &
|
|
631
|
+
i.def("__len__", &dense_index_py_t::size);
|
|
632
|
+
i.def_property_readonly("size", &dense_index_py_t::size);
|
|
633
|
+
i.def_property_readonly("ndim", &dense_index_py_t::dimensions);
|
|
634
|
+
i.def_property_readonly("connectivity", &dense_index_py_t::connectivity);
|
|
635
|
+
i.def_property_readonly("capacity", &dense_index_py_t::capacity);
|
|
640
636
|
i.def_property_readonly( //
|
|
641
|
-
"dtype", [](
|
|
637
|
+
"dtype", [](dense_index_py_t const& index) -> scalar_kind_t { return index.metric().scalar_kind_; });
|
|
642
638
|
i.def_property_readonly( //
|
|
643
|
-
"memory_usage", [](
|
|
639
|
+
"memory_usage", [](dense_index_py_t const& index) -> std::size_t { return index.memory_usage(); },
|
|
644
640
|
py::call_guard<py::gil_scoped_release>());
|
|
645
641
|
|
|
646
|
-
i.def_property("expansion_add", &
|
|
647
|
-
i.def_property("expansion_search", &
|
|
648
|
-
&punned_index_py_t::change_expansion_search);
|
|
642
|
+
i.def_property("expansion_add", &dense_index_py_t::expansion_add, &dense_index_py_t::change_expansion_add);
|
|
643
|
+
i.def_property("expansion_search", &dense_index_py_t::expansion_search, &dense_index_py_t::change_expansion_search);
|
|
649
644
|
|
|
650
|
-
i.def_property_readonly("labels", &get_all_labels<
|
|
651
|
-
i.def("get_labels", &get_labels<
|
|
652
|
-
i.def("__contains__", &
|
|
653
|
-
i.def("__getitem__", &get_member<
|
|
645
|
+
i.def_property_readonly("labels", &get_all_labels<dense_index_py_t>);
|
|
646
|
+
i.def("get_labels", &get_labels<dense_index_py_t>, py::arg("limit") = std::numeric_limits<std::size_t>::max());
|
|
647
|
+
i.def("__contains__", &dense_index_py_t::contains);
|
|
648
|
+
i.def("__getitem__", &get_member<dense_index_py_t>, py::arg("label"), py::arg("dtype") = scalar_kind_t::f32_k);
|
|
654
649
|
|
|
655
|
-
i.def("save", &save_index<
|
|
656
|
-
i.def("load", &load_index<
|
|
657
|
-
i.def("view", &view_index<
|
|
658
|
-
i.def("clear", &
|
|
650
|
+
i.def("save", &save_index<dense_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
|
|
651
|
+
i.def("load", &load_index<dense_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
|
|
652
|
+
i.def("view", &view_index<dense_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
|
|
653
|
+
i.def("clear", &dense_index_py_t::clear, py::call_guard<py::gil_scoped_release>());
|
|
659
654
|
|
|
660
655
|
auto si = py::class_<sparse_index_py_t>(m, "SparseIndex");
|
|
661
656
|
|
package/python/scripts/bench.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import logging
|
|
3
1
|
import itertools
|
|
4
|
-
from typing import List
|
|
2
|
+
from typing import List
|
|
5
3
|
from dataclasses import asdict
|
|
6
4
|
|
|
7
5
|
import numpy as np
|
|
8
6
|
import pandas as pd
|
|
9
7
|
|
|
10
|
-
from usearch.index import Index, Label
|
|
11
|
-
from usearch.
|
|
8
|
+
from usearch.index import Index, Label, MetricKind, ScalarKind
|
|
9
|
+
from usearch.numba import jit as njit
|
|
10
|
+
from usearch.eval import Evaluation, AddTask
|
|
12
11
|
from usearch.index import (
|
|
13
12
|
DEFAULT_CONNECTIVITY,
|
|
14
13
|
DEFAULT_EXPANSION_ADD,
|
|
@@ -26,14 +25,15 @@ def bench_speed(
|
|
|
26
25
|
) -> pd.DataFrame:
|
|
27
26
|
# Build various indexes:
|
|
28
27
|
indexes = []
|
|
29
|
-
jit_options = [False]
|
|
30
|
-
|
|
31
|
-
jit_options.append(jit)
|
|
32
|
-
dtype_options = ["f32", "f16", "f8"]
|
|
28
|
+
jit_options = [False, True] if jit else [False]
|
|
29
|
+
dtype_options = [ScalarKind.F32, ScalarKind.F16, ScalarKind.F8]
|
|
33
30
|
for jit, dtype in itertools.product(jit_options, dtype_options):
|
|
31
|
+
metric = MetricKind.IP
|
|
32
|
+
if jit:
|
|
33
|
+
metric = njit(eval.ndim, metric, dtype)
|
|
34
34
|
index = Index(
|
|
35
35
|
ndim=eval.ndim,
|
|
36
|
-
|
|
36
|
+
metric=metric,
|
|
37
37
|
dtype=dtype,
|
|
38
38
|
expansion_add=expansion_add,
|
|
39
39
|
expansion_search=expansion_search,
|
package/python/scripts/test.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import importlib.util
|
|
3
|
+
|
|
2
4
|
import pytest
|
|
3
5
|
import numpy as np
|
|
4
6
|
|
|
5
7
|
from usearch.io import load_matrix, save_matrix
|
|
6
|
-
from usearch.eval import
|
|
8
|
+
from usearch.eval import random_vectors
|
|
9
|
+
from usearch.numba import jit as njit
|
|
7
10
|
|
|
8
11
|
from usearch.index import (
|
|
9
12
|
Index,
|
|
@@ -13,9 +16,7 @@ from usearch.index import (
|
|
|
13
16
|
MetricSignature,
|
|
14
17
|
CompiledMetric,
|
|
15
18
|
Matches,
|
|
16
|
-
MetricKindBitwise,
|
|
17
19
|
)
|
|
18
|
-
from usearch.index import _normalize_dtype
|
|
19
20
|
from usearch.index import (
|
|
20
21
|
DEFAULT_CONNECTIVITY,
|
|
21
22
|
DEFAULT_EXPANSION_ADD,
|
|
@@ -34,7 +35,7 @@ index_types = [
|
|
|
34
35
|
numpy_types = [np.float32, np.float64, np.float16]
|
|
35
36
|
|
|
36
37
|
connectivity_options = [3, 13, 50, DEFAULT_CONNECTIVITY]
|
|
37
|
-
jit_options = [False]
|
|
38
|
+
jit_options = [False, True] if importlib.util.find_spec("spam") is not None else [False]
|
|
38
39
|
continuous_metrics = [
|
|
39
40
|
MetricKind.Cos,
|
|
40
41
|
MetricKind.L2sq,
|
|
@@ -80,6 +81,8 @@ def test_index(
|
|
|
80
81
|
connectivity: int,
|
|
81
82
|
jit: bool,
|
|
82
83
|
):
|
|
84
|
+
if jit:
|
|
85
|
+
metric = njit(ndim, metric, index_type)
|
|
83
86
|
index = Index(
|
|
84
87
|
metric=metric,
|
|
85
88
|
ndim=ndim,
|
|
@@ -87,7 +90,6 @@ def test_index(
|
|
|
87
90
|
connectivity=connectivity,
|
|
88
91
|
expansion_add=DEFAULT_EXPANSION_ADD,
|
|
89
92
|
expansion_search=DEFAULT_EXPANSION_SEARCH,
|
|
90
|
-
jit=jit,
|
|
91
93
|
)
|
|
92
94
|
assert index.ndim == ndim
|
|
93
95
|
assert index.connectivity == connectivity
|
|
@@ -176,7 +178,10 @@ def test_index_batch(
|
|
|
176
178
|
@pytest.mark.parametrize("index_type", index_types)
|
|
177
179
|
@pytest.mark.parametrize("numpy_type", numpy_types)
|
|
178
180
|
def test_exact_recall(
|
|
179
|
-
metric: MetricKind,
|
|
181
|
+
metric: MetricKind,
|
|
182
|
+
batch_size: int,
|
|
183
|
+
index_type: ScalarKind,
|
|
184
|
+
numpy_type: str,
|
|
180
185
|
):
|
|
181
186
|
ndim: int = batch_size
|
|
182
187
|
index = Index(ndim=ndim, metric=metric, dtype=index_type)
|
|
@@ -436,7 +441,10 @@ def test_index_peachpy(ndim: int, batch_size: int):
|
|
|
436
441
|
@pytest.mark.parametrize("connectivity", connectivity_options)
|
|
437
442
|
@pytest.mark.parametrize("batch_size", batch_sizes)
|
|
438
443
|
def test_bitwise_index(
|
|
439
|
-
bits: int,
|
|
444
|
+
bits: int,
|
|
445
|
+
metric: MetricKind,
|
|
446
|
+
connectivity: int,
|
|
447
|
+
batch_size: int,
|
|
440
448
|
):
|
|
441
449
|
index = Index(ndim=bits, metric=metric, connectivity=connectivity)
|
|
442
450
|
|
package/python/usearch/index.py
CHANGED
|
@@ -31,7 +31,7 @@ MetricKindBitwise = (
|
|
|
31
31
|
|
|
32
32
|
SparseIndex = _CompiledSetsIndex
|
|
33
33
|
|
|
34
|
-
Label = np.
|
|
34
|
+
Label = np.uint64
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def _normalize_dtype(dtype, metric: MetricKind = MetricKind.IP) -> ScalarKind:
|
|
@@ -167,10 +167,9 @@ class Index:
|
|
|
167
167
|
|
|
168
168
|
def __init__(
|
|
169
169
|
self,
|
|
170
|
-
ndim: int,
|
|
170
|
+
ndim: int = 0,
|
|
171
171
|
metric: Union[str, MetricKind, CompiledMetric] = MetricKind.IP,
|
|
172
|
-
dtype: Optional[str] = None,
|
|
173
|
-
jit: bool = False,
|
|
172
|
+
dtype: Optional[Union[str, ScalarKind]] = None,
|
|
174
173
|
connectivity: int = DEFAULT_CONNECTIVITY,
|
|
175
174
|
expansion_add: int = DEFAULT_EXPANSION_ADD,
|
|
176
175
|
expansion_search: int = DEFAULT_EXPANSION_SEARCH,
|
|
@@ -182,6 +181,10 @@ class Index:
|
|
|
182
181
|
|
|
183
182
|
:param ndim: Number of vector dimensions
|
|
184
183
|
:type ndim: int
|
|
184
|
+
Required for some metrics, optional for others.
|
|
185
|
+
Haversine, for example, only applies to 2-dimensional latitude/longitude
|
|
186
|
+
coordinates. Angular (Cos) and Euclidean (L2sq), obviously, apply to
|
|
187
|
+
vectors with arbitrary number of dimensions.
|
|
185
188
|
|
|
186
189
|
:param metric: Distance function, defaults to MetricKind.IP
|
|
187
190
|
:type metric: Union[MetricKind, Callable, str], optional
|
|
@@ -191,18 +194,12 @@ class Index:
|
|
|
191
194
|
Not every kind is JIT-able. For Jaccard distance, use `SparseIndex`.
|
|
192
195
|
|
|
193
196
|
:param dtype: Scalar type for internal vector storage, defaults to None
|
|
194
|
-
:type dtype: str, optional
|
|
197
|
+
:type dtype: Optional[Union[str, ScalarKind]], optional
|
|
195
198
|
For continuous metrics can be: f16, f32, f64, or f8.
|
|
196
199
|
For bitwise metrics it's implementation-defined, and can't change.
|
|
197
200
|
Example: you can use the `f16` index with `f32` vectors in Euclidean space,
|
|
198
201
|
which will be automatically downcasted.
|
|
199
202
|
|
|
200
|
-
:param jit: Enable Numba to JIT compile the metric, defaults to False
|
|
201
|
-
:type jit: bool, optional
|
|
202
|
-
This can result in up-to 3x performance difference on very large vectors
|
|
203
|
-
and very recent hardware, as the Python module is compiled with high
|
|
204
|
-
compatibility in mind and avoids very fancy assembly instructions.
|
|
205
|
-
|
|
206
203
|
:param connectivity: Connections per node in HNSW, defaults to None
|
|
207
204
|
:type connectivity: Optional[int], optional
|
|
208
205
|
Hyper-parameter for the number of Graph connections
|
|
@@ -231,21 +228,6 @@ class Index:
|
|
|
231
228
|
"""
|
|
232
229
|
|
|
233
230
|
metric = _normalize_metric(metric)
|
|
234
|
-
if isinstance(metric, MetricKind) and jit:
|
|
235
|
-
try:
|
|
236
|
-
from usearch.numba import jit
|
|
237
|
-
except ImportError:
|
|
238
|
-
raise ModuleNotFoundError(
|
|
239
|
-
"To use JIT install Numba with `pip install numba`."
|
|
240
|
-
"Alternatively, reinstall with `pip install usearch[jit]`"
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
metric = jit(
|
|
244
|
-
ndim=ndim,
|
|
245
|
-
metric=metric,
|
|
246
|
-
dtype=_normalize_dtype(dtype, metric),
|
|
247
|
-
)
|
|
248
|
-
|
|
249
231
|
if isinstance(metric, MetricKind):
|
|
250
232
|
self._metric_kind = metric
|
|
251
233
|
self._metric_jit = None
|
package/python/usearch/numba.py
CHANGED
|
@@ -4,31 +4,19 @@
|
|
|
4
4
|
# into the primary `Index` class, connecting USearch with Numba.
|
|
5
5
|
from math import sqrt
|
|
6
6
|
|
|
7
|
-
from numba import cfunc, types, carray
|
|
8
|
-
|
|
9
7
|
from usearch.index import MetricKind, ScalarKind, MetricSignature, CompiledMetric
|
|
10
8
|
|
|
11
9
|
|
|
12
|
-
signature_i8args = types.float32(types.CPointer(types.int8), types.CPointer(types.int8))
|
|
13
|
-
|
|
14
|
-
signature_f16args = types.float32(
|
|
15
|
-
types.CPointer(types.float16), types.CPointer(types.float16)
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
signature_f32args = types.float32(
|
|
19
|
-
types.CPointer(types.float32), types.CPointer(types.float32)
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
signature_f64args = types.float32(
|
|
23
|
-
types.CPointer(types.float64), types.CPointer(types.float64)
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
10
|
def jit(
|
|
28
|
-
ndim: int,
|
|
11
|
+
ndim: int,
|
|
12
|
+
metric: MetricKind = MetricKind.Cos,
|
|
13
|
+
dtype: ScalarKind = ScalarKind.F32,
|
|
29
14
|
) -> CompiledMetric:
|
|
30
|
-
"""JIT-compiles
|
|
31
|
-
|
|
15
|
+
"""JIT-compiles the metric for target hardware and number of dimensions.
|
|
16
|
+
|
|
17
|
+
This can result in up-to 3x performance difference on very large vectors
|
|
18
|
+
and very recent hardware, as the Python module is compiled with high
|
|
19
|
+
compatibility in mind and avoids very fancy assembly instructions.
|
|
32
20
|
|
|
33
21
|
Uses Numba `cfunc` functionality, annotating it with Numba `types` instead
|
|
34
22
|
of `ctypes` to support half-precision.
|
|
@@ -37,6 +25,24 @@ def jit(
|
|
|
37
25
|
assert isinstance(metric, MetricKind)
|
|
38
26
|
assert isinstance(dtype, ScalarKind)
|
|
39
27
|
|
|
28
|
+
from numba import cfunc, types, carray
|
|
29
|
+
|
|
30
|
+
signature_i8args = types.float32(
|
|
31
|
+
types.CPointer(types.int8), types.CPointer(types.int8)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
signature_f16args = types.float32(
|
|
35
|
+
types.CPointer(types.float16), types.CPointer(types.float16)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
signature_f32args = types.float32(
|
|
39
|
+
types.CPointer(types.float32), types.CPointer(types.float32)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
signature_f64args = types.float32(
|
|
43
|
+
types.CPointer(types.float64), types.CPointer(types.float64)
|
|
44
|
+
)
|
|
45
|
+
|
|
40
46
|
numba_supported_types = (
|
|
41
47
|
ScalarKind.F8,
|
|
42
48
|
# Half-precision is still unsupported
|
|
@@ -46,7 +52,7 @@ def jit(
|
|
|
46
52
|
ScalarKind.F64,
|
|
47
53
|
)
|
|
48
54
|
if dtype not in numba_supported_types:
|
|
49
|
-
return
|
|
55
|
+
return metric
|
|
50
56
|
|
|
51
57
|
scalar_kind_to_accumulator_type = {
|
|
52
58
|
ScalarKind.F8: types.int32,
|
|
@@ -109,7 +115,7 @@ def jit(
|
|
|
109
115
|
|
|
110
116
|
pointer = cfunc(scalar_kind_to_signature[dtype])(metric_kind_to_function[metric])
|
|
111
117
|
return CompiledMetric(
|
|
112
|
-
pointer=pointer,
|
|
118
|
+
pointer=pointer.address,
|
|
113
119
|
kind=metric,
|
|
114
120
|
signature=MetricSignature.ArrayArray,
|
|
115
121
|
)
|
package/rust/lib.cpp
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
using namespace unum::usearch;
|
|
5
5
|
using namespace unum;
|
|
6
6
|
|
|
7
|
-
using
|
|
8
|
-
using add_result_t = typename
|
|
9
|
-
using search_result_t = typename
|
|
10
|
-
using serialization_result_t = typename
|
|
7
|
+
using index_t = typename Index::index_t;
|
|
8
|
+
using add_result_t = typename index_t::add_result_t;
|
|
9
|
+
using search_result_t = typename index_t::search_result_t;
|
|
10
|
+
using serialization_result_t = typename index_t::serialization_result_t;
|
|
11
11
|
|
|
12
|
-
Index::Index(std::unique_ptr<
|
|
12
|
+
Index::Index(std::unique_ptr<index_t> index) : index_(std::move(index)) {}
|
|
13
13
|
|
|
14
|
-
void Index::add_in_thread(
|
|
14
|
+
void Index::add_in_thread(label_t label, rust::Slice<float const> vector, size_t thread) const {
|
|
15
15
|
add_config_t config;
|
|
16
16
|
config.thread = thread;
|
|
17
17
|
index_->add(label, vector.data(), config).error.raise();
|
|
@@ -33,7 +33,7 @@ Matches Index::search_in_thread(rust::Slice<float const> vector, size_t count, s
|
|
|
33
33
|
return matches;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
void Index::add(
|
|
36
|
+
void Index::add(label_t label, rust::Slice<float const> vector) const {
|
|
37
37
|
index_->add(label, vector.data()).error.raise();
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -64,9 +64,9 @@ void Index::view(rust::Str path) const { index_->view(std::string(path).c_str())
|
|
|
64
64
|
|
|
65
65
|
scalar_kind_t accuracy(rust::Str quant) { return scalar_kind_from_name(quant.data(), quant.size()); }
|
|
66
66
|
|
|
67
|
-
std::unique_ptr<Index> wrap(
|
|
68
|
-
std::unique_ptr<
|
|
69
|
-
punned_ptr.reset(new
|
|
67
|
+
std::unique_ptr<Index> wrap(index_t&& index) {
|
|
68
|
+
std::unique_ptr<index_t> punned_ptr;
|
|
69
|
+
punned_ptr.reset(new index_t(std::move(index)));
|
|
70
70
|
std::unique_ptr<Index> result;
|
|
71
71
|
result.reset(new Index(std::move(punned_ptr)));
|
|
72
72
|
return result;
|
|
@@ -104,7 +104,7 @@ scalar_kind_t rust_to_cpp_scalar(ScalarKind value) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
std::unique_ptr<Index> new_index(IndexOptions const& options) {
|
|
107
|
-
return wrap(
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
return wrap(index_t::make(options.dimensions, rust_to_cpp_metric(options.metric), config(options.connectivity),
|
|
108
|
+
rust_to_cpp_scalar(options.quantization), options.expansion_add,
|
|
109
|
+
options.expansion_search));
|
|
110
110
|
}
|
package/rust/lib.hpp
CHANGED
|
@@ -10,16 +10,20 @@ struct IndexOptions;
|
|
|
10
10
|
|
|
11
11
|
class Index {
|
|
12
12
|
public:
|
|
13
|
-
using
|
|
13
|
+
using metric_t = unum::usearch::index_punned_dense_metric_t;
|
|
14
14
|
using distance_t = unum::usearch::punned_distance_t;
|
|
15
|
-
using
|
|
15
|
+
using index_t = unum::usearch::punned_small_t;
|
|
16
|
+
using add_result_t = typename index_t::add_result_t;
|
|
17
|
+
using search_result_t = typename index_t::search_result_t;
|
|
18
|
+
using label_t = typename index_t::label_t;
|
|
19
|
+
using id_t = typename index_t::id_t;
|
|
16
20
|
|
|
17
|
-
Index(std::unique_ptr<
|
|
21
|
+
Index(std::unique_ptr<index_t> index);
|
|
18
22
|
|
|
19
23
|
void reserve(size_t) const;
|
|
20
24
|
|
|
21
|
-
void add(
|
|
22
|
-
void add_in_thread(
|
|
25
|
+
void add(label_t label, rust::Slice<float const> vector) const;
|
|
26
|
+
void add_in_thread(label_t label, rust::Slice<float const> vector, size_t thread) const;
|
|
23
27
|
|
|
24
28
|
Matches search(rust::Slice<float const> vector, size_t count) const;
|
|
25
29
|
Matches search_in_thread(rust::Slice<float const> vector, size_t count, size_t thread) const;
|
|
@@ -34,7 +38,7 @@ class Index {
|
|
|
34
38
|
void view(rust::Str path) const;
|
|
35
39
|
|
|
36
40
|
private:
|
|
37
|
-
std::unique_ptr<
|
|
41
|
+
std::unique_ptr<index_t> index_;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
std::unique_ptr<Index> new_index(IndexOptions const& options);
|
package/rust/lib.rs
CHANGED
|
@@ -4,7 +4,7 @@ pub mod ffi {
|
|
|
4
4
|
// Shared structs with fields visible to both languages.
|
|
5
5
|
struct Matches {
|
|
6
6
|
count: usize,
|
|
7
|
-
labels: Vec<
|
|
7
|
+
labels: Vec<u64>,
|
|
8
8
|
distances: Vec<f32>,
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -51,7 +51,7 @@ pub mod ffi {
|
|
|
51
51
|
pub fn size(self: &Index) -> usize;
|
|
52
52
|
pub fn capacity(self: &Index) -> usize;
|
|
53
53
|
|
|
54
|
-
pub fn add(self: &Index, label:
|
|
54
|
+
pub fn add(self: &Index, label: u64, vector: &[f32]) -> Result<()>;
|
|
55
55
|
pub fn search(self: &Index, query: &[f32], count: usize) -> Result<Matches>;
|
|
56
56
|
|
|
57
57
|
pub fn save(self: &Index, path: &str) -> Result<()>;
|
package/swift/Index+Sugar.swift
CHANGED
|
@@ -7,11 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
@available(iOS 13, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
9
9
|
extension USearchIndex {
|
|
10
|
+
public typealias Label = USearchLabel
|
|
11
|
+
public typealias Metric = USearchMetric
|
|
12
|
+
public typealias Scalar = USearchScalar
|
|
13
|
+
|
|
10
14
|
/// Adds a labeled vector to the index.
|
|
11
15
|
/// - Parameter label: Unique identifer for that object.
|
|
12
16
|
/// - Parameter vector: Single-precision vector.
|
|
13
17
|
/// - Throws: If runs out of memory.
|
|
14
|
-
public func add(label:
|
|
18
|
+
public func add(label: USearchLabel, vector: ArraySlice<Float32>) {
|
|
15
19
|
vector.withContiguousStorageIfAvailable {
|
|
16
20
|
addSingle(label: label, vector: $0.baseAddress!)
|
|
17
21
|
}
|
|
@@ -23,8 +27,8 @@ extension USearchIndex {
|
|
|
23
27
|
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
24
28
|
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
25
29
|
/// - Throws: If runs out of memory.
|
|
26
|
-
public func search(vector: ArraySlice<Float32>, count: Int) -> ([
|
|
27
|
-
var matches: [
|
|
30
|
+
public func search(vector: ArraySlice<Float32>, count: Int) -> ([Label], [Float]) {
|
|
31
|
+
var matches: [Label] = Array(repeating: 0, count: count)
|
|
28
32
|
var distances: [Float] = Array(repeating: 0, count: count)
|
|
29
33
|
let results = vector.withContiguousStorageIfAvailable {
|
|
30
34
|
searchSingle(vector: $0.baseAddress!, count: CUnsignedInt(count), labels: &matches, distances: &distances)
|
|
@@ -38,7 +42,7 @@ extension USearchIndex {
|
|
|
38
42
|
/// - Parameter label: Unique identifer for that object.
|
|
39
43
|
/// - Parameter vector: Double-precision vector.
|
|
40
44
|
/// - Throws: If runs out of memory.
|
|
41
|
-
public func add(label:
|
|
45
|
+
public func add(label: Label, vector: ArraySlice<Float64>) {
|
|
42
46
|
vector.withContiguousStorageIfAvailable {
|
|
43
47
|
addDouble(label: label, vector: $0.baseAddress!)
|
|
44
48
|
}
|
|
@@ -49,8 +53,8 @@ extension USearchIndex {
|
|
|
49
53
|
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
50
54
|
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
51
55
|
/// - Throws: If runs out of memory.
|
|
52
|
-
public func search(vector: ArraySlice<Float64>, count: Int) -> ([
|
|
53
|
-
var matches: [
|
|
56
|
+
public func search(vector: ArraySlice<Float64>, count: Int) -> ([Label], [Float]) {
|
|
57
|
+
var matches: [Label] = Array(repeating: 0, count: count)
|
|
54
58
|
var distances: [Float] = Array(repeating: 0, count: count)
|
|
55
59
|
let results = vector.withContiguousStorageIfAvailable {
|
|
56
60
|
searchDouble(vector: $0.baseAddress!, count: CUnsignedInt(count), labels: &matches, distances: &distances)
|
|
@@ -67,7 +71,7 @@ extension USearchIndex {
|
|
|
67
71
|
/// - Parameter vector: Half-precision vector.
|
|
68
72
|
/// - Throws: If runs out of memory.
|
|
69
73
|
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
70
|
-
public func add(label:
|
|
74
|
+
public func add(label: Label, vector: ArraySlice<Float16>) {
|
|
71
75
|
vector.withContiguousStorageIfAvailable { buffer in
|
|
72
76
|
addHalf(label: label, vector: buffer.baseAddress!)
|
|
73
77
|
}
|
|
@@ -79,8 +83,8 @@ extension USearchIndex {
|
|
|
79
83
|
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
80
84
|
/// - Throws: If runs out of memory.
|
|
81
85
|
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
82
|
-
public func search(vector: ArraySlice<Float16>, count: Int) -> ([
|
|
83
|
-
var matches: [
|
|
86
|
+
public func search(vector: ArraySlice<Float16>, count: Int) -> ([Label], [Float]) {
|
|
87
|
+
var matches: [Label] = Array(repeating: 0, count: count)
|
|
84
88
|
var distances: [Float] = Array(repeating: 0, count: count)
|
|
85
89
|
let results = vector.withContiguousStorageIfAvailable {
|
|
86
90
|
searchHalf(vector: $0.baseAddress!, count: CUnsignedInt(count), labels: &matches, distances: &distances)
|
package/wolfram/lib.cpp
CHANGED
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
#include <usearch/index_punned_dense.hpp>
|
|
3
3
|
|
|
4
4
|
using namespace unum::usearch;
|
|
5
|
+
|
|
6
|
+
using metric_t = index_punned_dense_metric_t;
|
|
5
7
|
using distance_t = punned_distance_t;
|
|
6
|
-
using
|
|
7
|
-
using
|
|
8
|
+
using index_t = punned_small_t;
|
|
9
|
+
using vector_view_t = span_gt<float>;
|
|
10
|
+
|
|
11
|
+
using add_result_t = typename index_t::add_result_t;
|
|
12
|
+
using search_result_t = typename index_t::search_result_t;
|
|
13
|
+
using label_t = typename index_t::label_t;
|
|
14
|
+
using id_t = typename index_t::id_t;
|
|
8
15
|
|
|
9
16
|
EXTERN_C DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData) { return LIBRARY_NO_ERROR; }
|
|
10
17
|
EXTERN_C DLLEXPORT void WolframLibrary_uninitialize(WolframLibraryData libData) { return; }
|
|
@@ -24,10 +31,10 @@ EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgum
|
|
|
24
31
|
|
|
25
32
|
scalar_kind_t accuracy = scalar_kind_from_name(accuracy_cstr, std::strlen(accuracy_cstr));
|
|
26
33
|
metric_kind_t metric_kind = metric_from_name(metric_cstr, std::strlen(metric_cstr));
|
|
27
|
-
|
|
34
|
+
index_t index = make_punned<index_t>(metric_kind, dimensions, accuracy, config);
|
|
28
35
|
index.reserve(capacity);
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
index_t* result_ptr = new index_t(std::move(index));
|
|
31
38
|
MArgument_setInteger(Res, (long)result_ptr);
|
|
32
39
|
|
|
33
40
|
} catch (...) {
|
|
@@ -41,7 +48,7 @@ EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgum
|
|
|
41
48
|
|
|
42
49
|
EXTERN_C DLLEXPORT int IndexSave(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
43
50
|
char* path_cstr = nullptr;
|
|
44
|
-
|
|
51
|
+
index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
|
|
45
52
|
try {
|
|
46
53
|
path_cstr = MArgument_getUTF8String(Args[1]);
|
|
47
54
|
c_ptr->save(path_cstr);
|
|
@@ -54,7 +61,7 @@ EXTERN_C DLLEXPORT int IndexSave(WolframLibraryData libData, mint Argc, MArgumen
|
|
|
54
61
|
|
|
55
62
|
EXTERN_C DLLEXPORT int IndexLoad(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
56
63
|
char* path_cstr = nullptr;
|
|
57
|
-
|
|
64
|
+
index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
|
|
58
65
|
try {
|
|
59
66
|
path_cstr = MArgument_getUTF8String(Args[1]);
|
|
60
67
|
c_ptr->load(path_cstr);
|
|
@@ -67,7 +74,7 @@ EXTERN_C DLLEXPORT int IndexLoad(WolframLibraryData libData, mint Argc, MArgumen
|
|
|
67
74
|
|
|
68
75
|
EXTERN_C DLLEXPORT int IndexView(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
69
76
|
char* path_cstr = nullptr;
|
|
70
|
-
|
|
77
|
+
index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
|
|
71
78
|
try {
|
|
72
79
|
path_cstr = MArgument_getUTF8String(Args[1]);
|
|
73
80
|
c_ptr->view(path_cstr);
|
|
@@ -79,44 +86,44 @@ EXTERN_C DLLEXPORT int IndexView(WolframLibraryData libData, mint Argc, MArgumen
|
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
EXTERN_C DLLEXPORT int IndexDestroy(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
82
|
-
delete (
|
|
89
|
+
delete (index_t*)MArgument_getUTF8String(Args[0]);
|
|
83
90
|
return LIBRARY_NO_ERROR;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
EXTERN_C DLLEXPORT int IndexSize(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
87
|
-
size_t res = ((
|
|
94
|
+
std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->size();
|
|
88
95
|
MArgument_setInteger(Res, res);
|
|
89
96
|
return LIBRARY_NO_ERROR;
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
EXTERN_C DLLEXPORT int IndexConnectivity(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
93
|
-
size_t res = ((
|
|
100
|
+
std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->connectivity();
|
|
94
101
|
MArgument_setInteger(Res, res);
|
|
95
102
|
return LIBRARY_NO_ERROR;
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
EXTERN_C DLLEXPORT int IndexDimensions(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
99
|
-
size_t res = ((
|
|
106
|
+
std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->dimensions();
|
|
100
107
|
MArgument_setInteger(Res, res);
|
|
101
108
|
return LIBRARY_NO_ERROR;
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
EXTERN_C DLLEXPORT int IndexCapacity(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
105
|
-
size_t res = ((
|
|
112
|
+
std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->capacity();
|
|
106
113
|
MArgument_setInteger(Res, res);
|
|
107
114
|
return LIBRARY_NO_ERROR;
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
EXTERN_C DLLEXPORT int IndexAdd(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
111
118
|
char* path_cstr = nullptr;
|
|
112
|
-
|
|
119
|
+
index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
|
|
113
120
|
float* vector_data = nullptr;
|
|
114
121
|
try {
|
|
115
122
|
int label = MArgument_getInteger(Args[1]);
|
|
116
123
|
MTensor tens = MArgument_getMTensor(Args[2]);
|
|
117
|
-
size_t len = libData->MTensor_getFlattenedLength(tens);
|
|
124
|
+
std::size_t len = libData->MTensor_getFlattenedLength(tens);
|
|
118
125
|
vector_data = (float*)libData->MTensor_getRealData(tens);
|
|
119
|
-
|
|
126
|
+
vector_view_t vector_span = vector_view_t{vector_data, len};
|
|
120
127
|
c_ptr->add(label, vector_span);
|
|
121
128
|
} catch (...) {
|
|
122
129
|
return LIBRARY_FUNCTION_ERROR;
|
|
@@ -125,7 +132,7 @@ EXTERN_C DLLEXPORT int IndexAdd(WolframLibraryData libData, mint Argc, MArgument
|
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
EXTERN_C DLLEXPORT int IndexSearch(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
128
|
-
|
|
135
|
+
index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
|
|
129
136
|
MTensor matches;
|
|
130
137
|
mint dims[] = {1};
|
|
131
138
|
int wanted = MArgument_getInteger(Args[2]);
|
|
@@ -137,12 +144,12 @@ EXTERN_C DLLEXPORT int IndexSearch(WolframLibraryData libData, mint Argc, MArgum
|
|
|
137
144
|
libData->MTensor_new(MType_Integer, 1, dims, &matches);
|
|
138
145
|
|
|
139
146
|
MTensor tens = MArgument_getMTensor(Args[1]);
|
|
140
|
-
size_t len = libData->MTensor_getFlattenedLength(tens);
|
|
147
|
+
std::size_t len = libData->MTensor_getFlattenedLength(tens);
|
|
141
148
|
vector_data = (float*)libData->MTensor_getRealData(tens);
|
|
142
|
-
|
|
149
|
+
vector_view_t vector_span = vector_view_t{vector_data, len};
|
|
143
150
|
|
|
144
151
|
matches_data = (int*)std::malloc(sizeof(int) * wanted);
|
|
145
|
-
found = c_ptr->search(vector_span, (
|
|
152
|
+
found = c_ptr->search(vector_span, static_cast<std::size_t>(wanted), matches_data, nullptr);
|
|
146
153
|
for (mint i = 0; i < found; i++)
|
|
147
154
|
libData->MTensor_setInteger(matches, &i, matches_data[i]);
|
|
148
155
|
|