usearch 0.2.4 → 0.4.0
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/ISSUE_TEMPLATE/bug_report.yml +90 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +64 -0
- package/.github/workflows/release.yml +5 -4
- package/.vscode/settings.json +4 -1
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +12 -0
- package/Cargo.toml +1 -1
- package/README.md +13 -5
- package/VERSION +1 -1
- package/binding.gyp +13 -9
- package/build.rs +1 -0
- package/conanfile.py +1 -1
- package/docs/benchmarks.rst +5 -0
- package/docs/compilation.rst +5 -0
- package/docs/conf.dox +2668 -0
- package/docs/index.rst +7 -0
- package/include/usearch/usearch.hpp +260 -140
- package/javascript/lib.cpp +82 -8
- package/objc/USearchObjective.mm +2 -2
- package/package.json +1 -1
- package/python/lib.cpp +47 -20
- package/rust/lib.cpp +6 -2
- package/src/advanced.hpp +23 -19
- package/src/bench.cpp +14 -9
- package/swift/Index+Sugar.swift +1 -0
- package/swift/Test.swift +1 -0
- /package/docs/{Benchmarking.md → benchmarks.md} +0 -0
package/javascript/lib.cpp
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
#define NAPI_CPP_EXCEPTIONS
|
|
16
16
|
#endif
|
|
17
17
|
|
|
18
|
+
#include <new> // `std::bad_alloc`
|
|
19
|
+
|
|
18
20
|
#include <napi.h>
|
|
19
21
|
#include <node_api.h>
|
|
20
22
|
|
|
@@ -38,6 +40,10 @@ class Index : public Napi::ObjectWrap<Index> {
|
|
|
38
40
|
Napi::Value GetCapacity(Napi::CallbackInfo const& ctx);
|
|
39
41
|
Napi::Value GetConnectivity(Napi::CallbackInfo const& ctx);
|
|
40
42
|
|
|
43
|
+
void Save(Napi::CallbackInfo const& ctx);
|
|
44
|
+
void Load(Napi::CallbackInfo const& ctx);
|
|
45
|
+
void View(Napi::CallbackInfo const& ctx);
|
|
46
|
+
|
|
41
47
|
void Add(Napi::CallbackInfo const& ctx);
|
|
42
48
|
Napi::Value Search(Napi::CallbackInfo const& ctx);
|
|
43
49
|
|
|
@@ -54,6 +60,9 @@ Napi::Object Index::Init(Napi::Env env, Napi::Object exports) {
|
|
|
54
60
|
InstanceMethod("connectivity", &Index::GetConnectivity),
|
|
55
61
|
InstanceMethod("add", &Index::Add),
|
|
56
62
|
InstanceMethod("search", &Index::Search),
|
|
63
|
+
InstanceMethod("save", &Index::Save),
|
|
64
|
+
InstanceMethod("load", &Index::Load),
|
|
65
|
+
InstanceMethod("view", &Index::View),
|
|
57
66
|
});
|
|
58
67
|
|
|
59
68
|
Napi::FunctionReference* constructor = new Napi::FunctionReference();
|
|
@@ -150,6 +159,57 @@ Napi::Value Index::GetCapacity(Napi::CallbackInfo const& ctx) {
|
|
|
150
159
|
return Napi::Number::New(ctx.Env(), native_->capacity());
|
|
151
160
|
}
|
|
152
161
|
|
|
162
|
+
void Index::Save(Napi::CallbackInfo const& ctx) {
|
|
163
|
+
Napi::Env env = ctx.Env();
|
|
164
|
+
|
|
165
|
+
int length = ctx.Length();
|
|
166
|
+
if (length == 0 || !ctx[0].IsString()) {
|
|
167
|
+
Napi::TypeError::New(env, "Function expects a string path argument").ThrowAsJavaScriptException();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
std::string path = ctx[0].As<Napi::String>();
|
|
173
|
+
native_->save(path.c_str());
|
|
174
|
+
} catch (...) {
|
|
175
|
+
Napi::TypeError::New(env, "Serialization failed").ThrowAsJavaScriptException();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void Index::Load(Napi::CallbackInfo const& ctx) {
|
|
180
|
+
Napi::Env env = ctx.Env();
|
|
181
|
+
|
|
182
|
+
int length = ctx.Length();
|
|
183
|
+
if (length == 0 || !ctx[0].IsString()) {
|
|
184
|
+
Napi::TypeError::New(env, "Function expects a string path argument").ThrowAsJavaScriptException();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
std::string path = ctx[0].As<Napi::String>();
|
|
190
|
+
native_->load(path.c_str());
|
|
191
|
+
} catch (...) {
|
|
192
|
+
Napi::TypeError::New(env, "Loading failed").ThrowAsJavaScriptException();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
void Index::View(Napi::CallbackInfo const& ctx) {
|
|
197
|
+
Napi::Env env = ctx.Env();
|
|
198
|
+
|
|
199
|
+
int length = ctx.Length();
|
|
200
|
+
if (length == 0 || !ctx[0].IsString()) {
|
|
201
|
+
Napi::TypeError::New(env, "Function expects a string path argument").ThrowAsJavaScriptException();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
std::string path = ctx[0].As<Napi::String>();
|
|
207
|
+
native_->view(path.c_str());
|
|
208
|
+
} catch (...) {
|
|
209
|
+
Napi::TypeError::New(env, "Memory-mapping failed").ThrowAsJavaScriptException();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
153
213
|
void Index::Add(Napi::CallbackInfo const& ctx) {
|
|
154
214
|
Napi::Env env = ctx.Env();
|
|
155
215
|
if (ctx.Length() < 2 || !ctx[0].IsNumber() || !ctx[1].IsTypedArray())
|
|
@@ -166,7 +226,14 @@ void Index::Add(Napi::CallbackInfo const& ctx) {
|
|
|
166
226
|
|
|
167
227
|
if (native_->size() + 1 >= native_->capacity())
|
|
168
228
|
native_->reserve(ceil2(native_->size() + 1));
|
|
169
|
-
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
native_->add(label, vector);
|
|
232
|
+
} catch (std::bad_alloc const&) {
|
|
233
|
+
Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
|
|
234
|
+
} catch (...) {
|
|
235
|
+
Napi::TypeError::New(env, "Insertion failed").ThrowAsJavaScriptException();
|
|
236
|
+
}
|
|
170
237
|
}
|
|
171
238
|
|
|
172
239
|
Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
|
|
@@ -190,13 +257,20 @@ Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
|
|
|
190
257
|
|
|
191
258
|
Napi::Uint32Array matches_js = Napi::Uint32Array::New(env, wanted);
|
|
192
259
|
Napi::Float32Array distances_js = Napi::Float32Array::New(env, wanted);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
260
|
+
try {
|
|
261
|
+
std::size_t count = native_->search(vector, wanted, matches_js.Data(), distances_js.Data());
|
|
262
|
+
Napi::Object result_js = Napi::Object::New(env);
|
|
263
|
+
result_js.Set("labels", matches_js);
|
|
264
|
+
result_js.Set("distances", distances_js);
|
|
265
|
+
result_js.Set("count", Napi::Number::New(env, count));
|
|
266
|
+
return result_js;
|
|
267
|
+
} catch (std::bad_alloc const&) {
|
|
268
|
+
Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
|
|
269
|
+
return {};
|
|
270
|
+
} catch (...) {
|
|
271
|
+
Napi::TypeError::New(env, "Search failed").ThrowAsJavaScriptException();
|
|
272
|
+
return {};
|
|
273
|
+
}
|
|
200
274
|
}
|
|
201
275
|
|
|
202
276
|
Napi::Object InitAll(Napi::Env env, Napi::Object exports) { return Index::Init(env, exports); }
|
package/objc/USearchObjective.mm
CHANGED
|
@@ -107,14 +107,14 @@ using shared_index_t = std::shared_ptr<native_index_t>;
|
|
|
107
107
|
|
|
108
108
|
- (void)addImprecise:(UInt32)label
|
|
109
109
|
vector:(void const * _Nonnull)vector {
|
|
110
|
-
_native->add(label, (
|
|
110
|
+
_native->add(label, (f16_bits_t const *)vector);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
- (UInt32)searchImprecise:(void const* _Nonnull)vector
|
|
114
114
|
count:(UInt32)wanted
|
|
115
115
|
labels:(UInt32* _Nullable)labels
|
|
116
116
|
distances:(Float32* _Nullable)distances {
|
|
117
|
-
std::size_t found = _native->search((
|
|
117
|
+
std::size_t found = _native->search((f16_bits_t const *)vector, static_cast<std::size_t>(wanted), labels, distances);
|
|
118
118
|
return static_cast<UInt32>(found);
|
|
119
119
|
}
|
|
120
120
|
|
package/package.json
CHANGED
package/python/lib.cpp
CHANGED
|
@@ -153,13 +153,16 @@ static void add_one_to_index(native_index_t& index, label_t label, py::buffer ve
|
|
|
153
153
|
if (index.size() + 1 >= index.capacity())
|
|
154
154
|
index.reserve(ceil2(index.size() + 1));
|
|
155
155
|
|
|
156
|
+
add_config_t config;
|
|
157
|
+
config.store_vector = copy;
|
|
158
|
+
|
|
156
159
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
157
160
|
if (vector_info.format == "e")
|
|
158
|
-
index.add(label, reinterpret_cast<f16_bits_t const*>(vector_data),
|
|
161
|
+
index.add(label, reinterpret_cast<f16_bits_t const*>(vector_data), config);
|
|
159
162
|
else if (vector_info.format == "f")
|
|
160
|
-
index.add(label, reinterpret_cast<float const*>(vector_data),
|
|
163
|
+
index.add(label, reinterpret_cast<float const*>(vector_data), config);
|
|
161
164
|
else if (vector_info.format == "d")
|
|
162
|
-
index.add(label, reinterpret_cast<double const*>(vector_data),
|
|
165
|
+
index.add(label, reinterpret_cast<double const*>(vector_data), config);
|
|
163
166
|
else
|
|
164
167
|
throw std::invalid_argument("Incompatible scalars in the vector!");
|
|
165
168
|
}
|
|
@@ -198,28 +201,37 @@ static void add_many_to_index( //
|
|
|
198
201
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
199
202
|
if (vectors_info.format == "e")
|
|
200
203
|
multithreaded(index.concurrency(), vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
204
|
+
add_config_t config;
|
|
205
|
+
config.store_vector = copy;
|
|
206
|
+
config.thread = thread_idx;
|
|
201
207
|
label_t label = *reinterpret_cast<label_t const*>(labels_data + task_idx * labels_info.strides[0]);
|
|
202
208
|
f16_bits_t const* vector =
|
|
203
209
|
reinterpret_cast<f16_bits_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
204
|
-
index.add(label, vector,
|
|
210
|
+
index.add(label, vector, config);
|
|
205
211
|
});
|
|
206
212
|
else if (vectors_info.format == "f")
|
|
207
213
|
multithreaded(index.concurrency(), vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
214
|
+
add_config_t config;
|
|
215
|
+
config.store_vector = copy;
|
|
216
|
+
config.thread = thread_idx;
|
|
208
217
|
label_t label = *reinterpret_cast<label_t const*>(labels_data + task_idx * labels_info.strides[0]);
|
|
209
218
|
float const* vector = reinterpret_cast<float const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
210
|
-
index.add(label, vector,
|
|
219
|
+
index.add(label, vector, config);
|
|
211
220
|
});
|
|
212
221
|
else if (vectors_info.format == "d")
|
|
213
222
|
multithreaded(index.concurrency(), vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
223
|
+
add_config_t config;
|
|
224
|
+
config.store_vector = copy;
|
|
225
|
+
config.thread = thread_idx;
|
|
214
226
|
label_t label = *reinterpret_cast<label_t const*>(labels_data + task_idx * labels_info.strides[0]);
|
|
215
227
|
double const* vector = reinterpret_cast<double const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
216
|
-
index.add(label, vector,
|
|
228
|
+
index.add(label, vector, config);
|
|
217
229
|
});
|
|
218
230
|
else
|
|
219
231
|
throw std::invalid_argument("Incompatible scalars in the vectors matrix!");
|
|
220
232
|
}
|
|
221
233
|
|
|
222
|
-
static py::tuple search_one_in_index(native_index_t& index, py::buffer vector, std::size_t wanted) {
|
|
234
|
+
static py::tuple search_one_in_index(native_index_t& index, py::buffer vector, std::size_t wanted, bool exact) {
|
|
223
235
|
|
|
224
236
|
py::buffer_info vector_info = vector.request();
|
|
225
237
|
Py_ssize_t vector_dimensions = vector_info.shape[0];
|
|
@@ -233,16 +245,19 @@ static py::tuple search_one_in_index(native_index_t& index, py::buffer vector, s
|
|
|
233
245
|
auto labels_py1d = labels_py.mutable_unchecked<1>();
|
|
234
246
|
auto distances_py1d = distances_py.mutable_unchecked<1>();
|
|
235
247
|
|
|
248
|
+
search_config_t config;
|
|
249
|
+
config.exact = exact;
|
|
250
|
+
|
|
236
251
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
237
252
|
if (vector_info.format == "e")
|
|
238
253
|
count = index.search( //
|
|
239
|
-
reinterpret_cast<f16_bits_t const*>(vector_data), wanted, &labels_py1d(0), &distances_py1d(0),
|
|
254
|
+
reinterpret_cast<f16_bits_t const*>(vector_data), wanted, &labels_py1d(0), &distances_py1d(0), config);
|
|
240
255
|
else if (vector_info.format == "f")
|
|
241
256
|
count = index.search( //
|
|
242
|
-
reinterpret_cast<float const*>(vector_data), wanted, &labels_py1d(0), &distances_py1d(0),
|
|
257
|
+
reinterpret_cast<float const*>(vector_data), wanted, &labels_py1d(0), &distances_py1d(0), config);
|
|
243
258
|
else if (vector_info.format == "d")
|
|
244
259
|
count = index.search( //
|
|
245
|
-
reinterpret_cast<double const*>(vector_data), wanted, &labels_py1d(0), &distances_py1d(0),
|
|
260
|
+
reinterpret_cast<double const*>(vector_data), wanted, &labels_py1d(0), &distances_py1d(0), config);
|
|
246
261
|
else
|
|
247
262
|
throw std::invalid_argument("Incompatible scalars in the query vector!");
|
|
248
263
|
|
|
@@ -265,14 +280,14 @@ static py::tuple search_one_in_index(native_index_t& index, py::buffer vector, s
|
|
|
265
280
|
* 2. matrix of distances,
|
|
266
281
|
* 3. array with match counts.
|
|
267
282
|
*/
|
|
268
|
-
static py::tuple search_many_in_index(native_index_t& index, py::buffer vectors, std::size_t wanted) {
|
|
283
|
+
static py::tuple search_many_in_index(native_index_t& index, py::buffer vectors, std::size_t wanted, bool exact) {
|
|
269
284
|
|
|
270
285
|
if (wanted == 0)
|
|
271
286
|
return py::tuple(3);
|
|
272
287
|
|
|
273
288
|
py::buffer_info vectors_info = vectors.request();
|
|
274
289
|
if (vectors_info.ndim == 1)
|
|
275
|
-
return search_one_in_index(index, vectors, wanted);
|
|
290
|
+
return search_one_in_index(index, vectors, wanted, exact);
|
|
276
291
|
if (vectors_info.ndim != 2)
|
|
277
292
|
throw std::invalid_argument("Expects a matrix of vectors to add!");
|
|
278
293
|
|
|
@@ -292,21 +307,30 @@ static py::tuple search_many_in_index(native_index_t& index, py::buffer vectors,
|
|
|
292
307
|
// https://docs.python.org/3/library/struct.html#format-characters
|
|
293
308
|
if (vectors_info.format == "e")
|
|
294
309
|
multithreaded(index.concurrency(), vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
310
|
+
search_config_t config;
|
|
311
|
+
config.thread = thread_idx;
|
|
312
|
+
config.exact = exact;
|
|
295
313
|
f16_bits_t const* vector = (f16_bits_t const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
296
314
|
counts_py1d(task_idx) = static_cast<Py_ssize_t>(
|
|
297
|
-
index.search(vector, wanted, &labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0),
|
|
315
|
+
index.search(vector, wanted, &labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0), config));
|
|
298
316
|
});
|
|
299
317
|
else if (vectors_info.format == "f")
|
|
300
318
|
multithreaded(index.concurrency(), vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
319
|
+
search_config_t config;
|
|
320
|
+
config.thread = thread_idx;
|
|
321
|
+
config.exact = exact;
|
|
301
322
|
float const* vector = (float const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
302
323
|
counts_py1d(task_idx) = static_cast<Py_ssize_t>(
|
|
303
|
-
index.search(vector, wanted, &labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0),
|
|
324
|
+
index.search(vector, wanted, &labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0), config));
|
|
304
325
|
});
|
|
305
326
|
else if (vectors_info.format == "d")
|
|
306
327
|
multithreaded(index.concurrency(), vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
328
|
+
search_config_t config;
|
|
329
|
+
config.thread = thread_idx;
|
|
330
|
+
config.exact = exact;
|
|
307
331
|
double const* vector = (double const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
308
332
|
counts_py1d(task_idx) = static_cast<Py_ssize_t>(
|
|
309
|
-
index.search(vector, wanted, &labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0),
|
|
333
|
+
index.search(vector, wanted, &labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0), config));
|
|
310
334
|
});
|
|
311
335
|
else
|
|
312
336
|
throw std::invalid_argument("Incompatible scalars in the query matrix!");
|
|
@@ -428,7 +452,8 @@ PYBIND11_MODULE(index, m) {
|
|
|
428
452
|
i.def( //
|
|
429
453
|
"search", &search_many_in_index, //
|
|
430
454
|
py::arg("query"), //
|
|
431
|
-
py::arg("count") = 10
|
|
455
|
+
py::arg("count") = 10, //
|
|
456
|
+
py::arg("exact") = false //
|
|
432
457
|
);
|
|
433
458
|
|
|
434
459
|
i.def("__len__", &native_index_t::size);
|
|
@@ -463,7 +488,9 @@ PYBIND11_MODULE(index, m) {
|
|
|
463
488
|
index.reserve(ceil2(index.size() + 1));
|
|
464
489
|
auto proxy = set.unchecked<1>();
|
|
465
490
|
auto view = set_view_t{proxy.data(0), static_cast<std::size_t>(proxy.shape(0))};
|
|
466
|
-
|
|
491
|
+
add_config_t config;
|
|
492
|
+
config.store_vector = copy;
|
|
493
|
+
index.add(label, view, config);
|
|
467
494
|
}, //
|
|
468
495
|
py::arg("label"), //
|
|
469
496
|
py::arg("set"), //
|
|
@@ -479,7 +506,7 @@ PYBIND11_MODULE(index, m) {
|
|
|
479
506
|
auto view = set_view_t{proxy.data(0), static_cast<std::size_t>(proxy.shape(0))};
|
|
480
507
|
auto labels_py = py::array_t<label_t>(py_shape_t{static_cast<Py_ssize_t>(count)});
|
|
481
508
|
auto labels_proxy = labels_py.mutable_unchecked<1>();
|
|
482
|
-
auto found = index.search(view, count, &labels_proxy(0), nullptr
|
|
509
|
+
auto found = index.search(view, count, &labels_proxy(0), nullptr);
|
|
483
510
|
labels_py.resize(py_shape_t{static_cast<Py_ssize_t>(found)});
|
|
484
511
|
return labels_py;
|
|
485
512
|
},
|
|
@@ -515,7 +542,7 @@ PYBIND11_MODULE(index, m) {
|
|
|
515
542
|
if (index.size() + 1 >= index.capacity())
|
|
516
543
|
index.reserve(ceil2(index.size() + 1));
|
|
517
544
|
hash_buffer(index, array);
|
|
518
|
-
index.add(label, index.buffer()
|
|
545
|
+
index.add(label, index.buffer());
|
|
519
546
|
}, //
|
|
520
547
|
py::arg("label"), //
|
|
521
548
|
py::arg("array") //
|
|
@@ -529,7 +556,7 @@ PYBIND11_MODULE(index, m) {
|
|
|
529
556
|
hash_buffer(index, array);
|
|
530
557
|
auto labels_py = py::array_t<label_t>(py_shape_t{static_cast<Py_ssize_t>(count)});
|
|
531
558
|
auto labels_proxy = labels_py.mutable_unchecked<1>();
|
|
532
|
-
auto found = index.search(index.buffer(), count, &labels_proxy(0), nullptr
|
|
559
|
+
auto found = index.search(index.buffer(), count, &labels_proxy(0), nullptr);
|
|
533
560
|
labels_py.resize(py_shape_t{static_cast<Py_ssize_t>(found)});
|
|
534
561
|
return labels_py;
|
|
535
562
|
},
|
package/rust/lib.cpp
CHANGED
|
@@ -9,7 +9,9 @@ using native_index_t = typename Index::native_index_t;
|
|
|
9
9
|
Index::Index(std::shared_ptr<native_index_t> index) : index_(index) {}
|
|
10
10
|
|
|
11
11
|
void Index::add_in_thread(uint32_t label, rust::Slice<float const> vector, size_t thread) const {
|
|
12
|
-
|
|
12
|
+
add_config_t config;
|
|
13
|
+
config.thread = thread;
|
|
14
|
+
index_->add(label, vector.data(), config);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
Matches Index::search_in_thread(rust::Slice<float const> vector, size_t count, size_t thread) const {
|
|
@@ -18,7 +20,9 @@ Matches Index::search_in_thread(rust::Slice<float const> vector, size_t count, s
|
|
|
18
20
|
results.distances.reserve(count);
|
|
19
21
|
for (size_t i = 0; i != count; ++i)
|
|
20
22
|
results.labels.push_back(0), results.distances.push_back(0);
|
|
21
|
-
|
|
23
|
+
search_config_t config;
|
|
24
|
+
config.thread = thread;
|
|
25
|
+
results.count = index_->search(vector.data(), count, results.labels.data(), results.distances.data(), config);
|
|
22
26
|
results.labels.truncate(results.count);
|
|
23
27
|
results.distances.truncate(results.count);
|
|
24
28
|
return results;
|
package/src/advanced.hpp
CHANGED
|
@@ -469,21 +469,21 @@ class auto_index_gt {
|
|
|
469
469
|
void reserve(std::size_t capacity) { index_->reserve(capacity); }
|
|
470
470
|
|
|
471
471
|
// clang-format off
|
|
472
|
-
void add(label_t label, f16_bits_t const* vector) { return add(label, vector,
|
|
473
|
-
void add(label_t label, f32_t const* vector) { return add(label, vector,
|
|
474
|
-
void add(label_t label, f64_t const* vector) { return add(label, vector,
|
|
472
|
+
void add(label_t label, f16_bits_t const* vector) { return add(label, vector, casts_.from_f16); }
|
|
473
|
+
void add(label_t label, f32_t const* vector) { return add(label, vector, casts_.from_f32); }
|
|
474
|
+
void add(label_t label, f64_t const* vector) { return add(label, vector, casts_.from_f64); }
|
|
475
475
|
|
|
476
|
-
void add(label_t label, f16_bits_t const* vector,
|
|
477
|
-
void add(label_t label, f32_t const* vector,
|
|
478
|
-
void add(label_t label, f64_t const* vector,
|
|
476
|
+
void add(label_t label, f16_bits_t const* vector, add_config_t config) { return add(label, vector, config, casts_.from_f16); }
|
|
477
|
+
void add(label_t label, f32_t const* vector, add_config_t config) { return add(label, vector, config, casts_.from_f32); }
|
|
478
|
+
void add(label_t label, f64_t const* vector, add_config_t config) { return add(label, vector, config, casts_.from_f64); }
|
|
479
479
|
|
|
480
480
|
std::size_t search(f16_bits_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances) const { return search(vector, wanted, matches, distances, casts_.from_f16); }
|
|
481
481
|
std::size_t search(f32_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances) const { return search(vector, wanted, matches, distances, casts_.from_f32); }
|
|
482
482
|
std::size_t search(f64_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances) const { return search(vector, wanted, matches, distances, casts_.from_f64); }
|
|
483
483
|
|
|
484
|
-
std::size_t search(f16_bits_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances,
|
|
485
|
-
std::size_t search(f32_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances,
|
|
486
|
-
std::size_t search(f64_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances,
|
|
484
|
+
std::size_t search(f16_bits_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances, search_config_t config) const { return search(vector, wanted, matches, distances, config, casts_.from_f16); }
|
|
485
|
+
std::size_t search(f32_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances, search_config_t config) const { return search(vector, wanted, matches, distances, config, casts_.from_f32); }
|
|
486
|
+
std::size_t search(f64_t const* vector, std::size_t wanted, label_t* matches, distance_t* distances, search_config_t config) const { return search(vector, wanted, matches, distances, config, casts_.from_f64); }
|
|
487
487
|
|
|
488
488
|
static auto_index_gt ip(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, config_t config = {}) { return make(dimensions, accuracy, ip_metric(dimensions, accuracy), make_casts(accuracy), config); }
|
|
489
489
|
static auto_index_gt l2(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, config_t config = {}) { return make(dimensions, accuracy, l2_metric(dimensions, accuracy), make_casts(accuracy), config); }
|
|
@@ -549,38 +549,40 @@ class auto_index_gt {
|
|
|
549
549
|
}
|
|
550
550
|
|
|
551
551
|
template <typename scalar_at>
|
|
552
|
-
void add(label_t label, scalar_at const* vector,
|
|
552
|
+
void add(label_t label, scalar_at const* vector, add_config_t config, cast_t const& cast) {
|
|
553
553
|
byte_t const* vector_data = reinterpret_cast<byte_t const*>(vector);
|
|
554
554
|
std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
|
|
555
555
|
|
|
556
|
-
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * thread;
|
|
556
|
+
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
|
|
557
557
|
bool casted = cast(vector_data, casted_vector_bytes_, casted_data);
|
|
558
558
|
if (casted)
|
|
559
|
-
vector_data = casted_data, vector_bytes = casted_vector_bytes_,
|
|
559
|
+
vector_data = casted_data, vector_bytes = casted_vector_bytes_, config.store_vector = true;
|
|
560
560
|
|
|
561
|
-
index_->add(label, {vector_data, vector_bytes},
|
|
561
|
+
index_->add(label, {vector_data, vector_bytes}, config);
|
|
562
562
|
}
|
|
563
563
|
|
|
564
564
|
template <typename scalar_at>
|
|
565
565
|
std::size_t search( //
|
|
566
566
|
scalar_at const* vector, std::size_t wanted, //
|
|
567
567
|
label_t* matches, distance_t* distances, //
|
|
568
|
-
|
|
568
|
+
search_config_t config, cast_t const& cast) const {
|
|
569
569
|
|
|
570
570
|
byte_t const* vector_data = reinterpret_cast<byte_t const*>(vector);
|
|
571
571
|
std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
|
|
572
572
|
|
|
573
|
-
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * thread;
|
|
573
|
+
byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
|
|
574
574
|
bool casted = cast(vector_data, casted_vector_bytes_, casted_data);
|
|
575
575
|
if (casted)
|
|
576
576
|
vector_data = casted_data, vector_bytes = casted_vector_bytes_;
|
|
577
577
|
|
|
578
|
-
return index_->search({vector_data, vector_bytes}, wanted, matches, distances,
|
|
578
|
+
return index_->search({vector_data, vector_bytes}, wanted, matches, distances, config);
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
-
template <typename scalar_at> void add(label_t label, scalar_at const* vector,
|
|
581
|
+
template <typename scalar_at> void add(label_t label, scalar_at const* vector, cast_t const& cast) {
|
|
582
582
|
thread_lock_t lock = thread_lock();
|
|
583
|
-
|
|
583
|
+
add_config_t config;
|
|
584
|
+
config.thread = lock.thread_id;
|
|
585
|
+
return add(label, vector, config, cast);
|
|
584
586
|
}
|
|
585
587
|
|
|
586
588
|
template <typename scalar_at>
|
|
@@ -589,7 +591,9 @@ class auto_index_gt {
|
|
|
589
591
|
label_t* matches, distance_t* distances, //
|
|
590
592
|
cast_t const& cast) const {
|
|
591
593
|
thread_lock_t lock = thread_lock();
|
|
592
|
-
|
|
594
|
+
search_config_t config;
|
|
595
|
+
config.thread = lock.thread_id;
|
|
596
|
+
return search(vector, wanted, matches, distances, config, cast);
|
|
593
597
|
}
|
|
594
598
|
|
|
595
599
|
static auto_index_gt make( //
|
package/src/bench.cpp
CHANGED
|
@@ -43,6 +43,7 @@ using namespace unum::usearch;
|
|
|
43
43
|
using namespace unum;
|
|
44
44
|
|
|
45
45
|
using vector_id_t = std::uint32_t;
|
|
46
|
+
using vector_view_t = span_gt<float const>;
|
|
46
47
|
|
|
47
48
|
template <typename element_at>
|
|
48
49
|
std::size_t offset_of(element_at const* begin, element_at const* end, element_at v) noexcept {
|
|
@@ -271,12 +272,14 @@ struct running_stats_printer_t {
|
|
|
271
272
|
template <typename index_at, typename vector_id_at, typename real_at>
|
|
272
273
|
void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_at const* vectors, std::size_t dims) {
|
|
273
274
|
|
|
274
|
-
using span_t = span_gt<float const>;
|
|
275
275
|
running_stats_printer_t printer{n, "Indexing"};
|
|
276
276
|
|
|
277
277
|
#pragma omp parallel for schedule(static, 32)
|
|
278
278
|
for (std::size_t i = 0; i < n; ++i) {
|
|
279
|
-
|
|
279
|
+
add_config_t config;
|
|
280
|
+
config.thread = omp_get_thread_num();
|
|
281
|
+
config.store_vector = true;
|
|
282
|
+
native.add(ids[i], vector_view_t{vectors + dims * i, dims}, config);
|
|
280
283
|
printer.progress++;
|
|
281
284
|
if (omp_get_thread_num() == 0)
|
|
282
285
|
printer.refresh();
|
|
@@ -284,18 +287,20 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
template <typename index_at, typename vector_id_at, typename real_at>
|
|
287
|
-
void search_many(
|
|
288
|
-
|
|
290
|
+
void search_many( //
|
|
291
|
+
index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted, vector_id_at* ids,
|
|
292
|
+
real_at* distances) {
|
|
289
293
|
|
|
290
|
-
using span_t = span_gt<float const>;
|
|
291
294
|
running_stats_printer_t printer{n, "Search"};
|
|
292
295
|
|
|
293
296
|
#pragma omp parallel for schedule(static, 32)
|
|
294
297
|
for (std::size_t i = 0; i < n; ++i) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
search_config_t config;
|
|
299
|
+
config.thread = omp_get_thread_num();
|
|
300
|
+
native.search( //
|
|
301
|
+
vector_view_t{vectors + dims * i, dims}, wanted, //
|
|
302
|
+
ids + wanted * i, distances + wanted * i, //
|
|
303
|
+
config);
|
|
299
304
|
printer.progress++;
|
|
300
305
|
if (omp_get_thread_num() == 0)
|
|
301
306
|
printer.refresh();
|
package/swift/Index+Sugar.swift
CHANGED
package/swift/Test.swift
CHANGED
|
File without changes
|