usearch 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +2 -1
- package/.releaserc +6 -2
- package/CITATION.cff +1 -1
- package/Cargo.toml +1 -1
- package/README.md +1 -1
- package/conanfile.py +1 -1
- package/cpp/bench.cpp +1 -0
- package/csharp/nuget/nuget-package.props +1 -1
- package/include/usearch/index.hpp +51 -15
- package/include/usearch/index_dense.hpp +43 -20
- package/include/usearch/index_plugins.hpp +9 -1
- package/package.json +1 -1
- package/python/lib.cpp +135 -63
- package/python/usearch/index.py +112 -79
- package/rust/README.md +17 -18
- package/swift/README.md +11 -7
- package/wasmer.toml +1 -1
package/python/lib.cpp
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
#define _CRT_SECURE_NO_WARNINGS
|
|
23
23
|
#define PY_SSIZE_T_CLEAN
|
|
24
|
+
#include <pybind11/functional.h>
|
|
24
25
|
#include <pybind11/numpy.h>
|
|
25
26
|
#include <pybind11/pybind11.h>
|
|
26
27
|
#include <pybind11/stl.h>
|
|
@@ -55,6 +56,18 @@ using dense_labeling_result_t = typename index_dense_t::labeling_result_t;
|
|
|
55
56
|
using dense_cluster_result_t = typename index_dense_t::cluster_result_t;
|
|
56
57
|
using dense_clustering_result_t = typename index_dense_t::clustering_result_t;
|
|
57
58
|
|
|
59
|
+
using progress_func_t = std::function<bool(std::size_t /*processed*/, std::size_t /*total*/)>;
|
|
60
|
+
|
|
61
|
+
struct progress_t {
|
|
62
|
+
inline progress_t(progress_func_t const& func) : func_(func ? func : &dummy_progress) {}
|
|
63
|
+
inline bool operator()(std::size_t processed, std::size_t total) const noexcept { return func_(processed, total); }
|
|
64
|
+
|
|
65
|
+
private:
|
|
66
|
+
static inline bool dummy_progress(std::size_t /*processed*/, std::size_t /*total*/) { return true; }
|
|
67
|
+
|
|
68
|
+
progress_func_t func_;
|
|
69
|
+
};
|
|
70
|
+
|
|
58
71
|
struct dense_index_py_t : public index_dense_t {
|
|
59
72
|
using native_t = index_dense_t;
|
|
60
73
|
using native_t::add;
|
|
@@ -208,13 +221,18 @@ template <typename scalar_at>
|
|
|
208
221
|
static void add_typed_to_index( //
|
|
209
222
|
dense_index_py_t& index, //
|
|
210
223
|
py::buffer_info const& keys_info, py::buffer_info const& vectors_info, //
|
|
211
|
-
bool force_copy, std::size_t threads
|
|
224
|
+
bool force_copy, std::size_t threads, //
|
|
225
|
+
progress_func_t const& progress) {
|
|
212
226
|
|
|
213
227
|
Py_ssize_t vectors_count = vectors_info.shape[0];
|
|
214
228
|
byte_t const* vectors_data = reinterpret_cast<byte_t const*>(vectors_info.ptr);
|
|
215
229
|
byte_t const* keys_data = reinterpret_cast<byte_t const*>(keys_info.ptr);
|
|
216
230
|
atomic_error_t atomic_error{nullptr};
|
|
217
231
|
|
|
232
|
+
// Progress status
|
|
233
|
+
progress_t progress_{progress};
|
|
234
|
+
std::atomic<std::size_t> processed{0};
|
|
235
|
+
|
|
218
236
|
executor_default_t{threads}.dynamic(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
219
237
|
dense_key_t key = *reinterpret_cast<dense_key_t const*>(keys_data + task_idx * keys_info.strides[0]);
|
|
220
238
|
scalar_at const* vector = reinterpret_cast<scalar_at const*>(vectors_data + task_idx * vectors_info.strides[0]);
|
|
@@ -225,12 +243,18 @@ static void add_typed_to_index( //
|
|
|
225
243
|
}
|
|
226
244
|
|
|
227
245
|
// We don't want to check for signals from multiple threads
|
|
246
|
+
++processed;
|
|
228
247
|
if (thread_idx == 0)
|
|
229
|
-
if (PyErr_CheckSignals() != 0)
|
|
248
|
+
if (PyErr_CheckSignals() != 0 || !progress_(processed.load(), vectors_count)) {
|
|
249
|
+
atomic_error.store("Operation has been terminated");
|
|
230
250
|
return false;
|
|
251
|
+
}
|
|
231
252
|
return true;
|
|
232
253
|
});
|
|
233
254
|
|
|
255
|
+
// At the end report the latest numbers, because the reporter thread may be finished earlier
|
|
256
|
+
progress_(processed.load(), vectors_count);
|
|
257
|
+
|
|
234
258
|
// Raise the error from a single thread
|
|
235
259
|
auto error = atomic_error.load();
|
|
236
260
|
if (error) {
|
|
@@ -242,7 +266,8 @@ static void add_typed_to_index( //
|
|
|
242
266
|
template <typename index_at>
|
|
243
267
|
static void add_many_to_index( //
|
|
244
268
|
index_at& index, py::buffer keys, py::buffer vectors, //
|
|
245
|
-
bool force_copy, std::size_t threads
|
|
269
|
+
bool force_copy, std::size_t threads, //
|
|
270
|
+
progress_func_t const& progress) {
|
|
246
271
|
|
|
247
272
|
py::buffer_info keys_info = keys.request();
|
|
248
273
|
py::buffer_info vectors_info = vectors.request();
|
|
@@ -272,11 +297,11 @@ static void add_many_to_index( //
|
|
|
272
297
|
|
|
273
298
|
// clang-format off
|
|
274
299
|
switch (numpy_string_to_kind(vectors_info.format)) {
|
|
275
|
-
case scalar_kind_t::b1x8_k: add_typed_to_index<b1x8_t>(index, keys_info, vectors_info, force_copy, threads); break;
|
|
276
|
-
case scalar_kind_t::i8_k: add_typed_to_index<i8_t>(index, keys_info, vectors_info, force_copy, threads); break;
|
|
277
|
-
case scalar_kind_t::f16_k: add_typed_to_index<f16_t>(index, keys_info, vectors_info, force_copy, threads); break;
|
|
278
|
-
case scalar_kind_t::f32_k: add_typed_to_index<f32_t>(index, keys_info, vectors_info, force_copy, threads); break;
|
|
279
|
-
case scalar_kind_t::f64_k: add_typed_to_index<f64_t>(index, keys_info, vectors_info, force_copy, threads); break;
|
|
300
|
+
case scalar_kind_t::b1x8_k: add_typed_to_index<b1x8_t>(index, keys_info, vectors_info, force_copy, threads, progress); break;
|
|
301
|
+
case scalar_kind_t::i8_k: add_typed_to_index<i8_t>(index, keys_info, vectors_info, force_copy, threads, progress); break;
|
|
302
|
+
case scalar_kind_t::f16_k: add_typed_to_index<f16_t>(index, keys_info, vectors_info, force_copy, threads, progress); break;
|
|
303
|
+
case scalar_kind_t::f32_k: add_typed_to_index<f32_t>(index, keys_info, vectors_info, force_copy, threads, progress); break;
|
|
304
|
+
case scalar_kind_t::f64_k: add_typed_to_index<f64_t>(index, keys_info, vectors_info, force_copy, threads, progress); break;
|
|
280
305
|
default: throw std::invalid_argument("Incompatible scalars in the vectors matrix: " + vectors_info.format);
|
|
281
306
|
}
|
|
282
307
|
// clang-format on
|
|
@@ -287,7 +312,8 @@ static void search_typed( //
|
|
|
287
312
|
dense_index_py_t& index, py::buffer_info& vectors_info, //
|
|
288
313
|
std::size_t wanted, bool exact, std::size_t threads, //
|
|
289
314
|
py::array_t<dense_key_t>& keys_py, py::array_t<distance_t>& distances_py, py::array_t<Py_ssize_t>& counts_py,
|
|
290
|
-
std::atomic<std::size_t>& stats_visited_members, std::atomic<std::size_t>& stats_computed_distances
|
|
315
|
+
std::atomic<std::size_t>& stats_visited_members, std::atomic<std::size_t>& stats_computed_distances,
|
|
316
|
+
progress_func_t const& progress) {
|
|
291
317
|
|
|
292
318
|
auto keys_py2d = keys_py.template mutable_unchecked<2>();
|
|
293
319
|
auto distances_py2d = distances_py.template mutable_unchecked<2>();
|
|
@@ -301,6 +327,10 @@ static void search_typed( //
|
|
|
301
327
|
if (!index.reserve(index_limits_t(index.size(), threads)))
|
|
302
328
|
throw std::invalid_argument("Out of memory!");
|
|
303
329
|
|
|
330
|
+
// Progress status
|
|
331
|
+
progress_t progress_{progress};
|
|
332
|
+
std::atomic<std::size_t> processed{0};
|
|
333
|
+
|
|
304
334
|
atomic_error_t atomic_error{nullptr};
|
|
305
335
|
executor_default_t{threads}.dynamic(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
306
336
|
scalar_at const* vector = (scalar_at const*)(vectors_data + task_idx * vectors_info.strides[0]);
|
|
@@ -317,12 +347,18 @@ static void search_typed( //
|
|
|
317
347
|
stats_computed_distances += result.computed_distances;
|
|
318
348
|
|
|
319
349
|
// We don't want to check for signals from multiple threads
|
|
350
|
+
++processed;
|
|
320
351
|
if (thread_idx == 0)
|
|
321
|
-
if (PyErr_CheckSignals() != 0)
|
|
352
|
+
if (PyErr_CheckSignals() != 0 || !progress_(processed.load(), vectors_count)) {
|
|
353
|
+
atomic_error.store("Operation has been terminated");
|
|
322
354
|
return false;
|
|
355
|
+
}
|
|
323
356
|
return true;
|
|
324
357
|
});
|
|
325
358
|
|
|
359
|
+
// At the end report the latest numbers, because the reporter thread may be finished earlier
|
|
360
|
+
progress_(processed.load(), vectors_count);
|
|
361
|
+
|
|
326
362
|
// Raise the error from a single thread
|
|
327
363
|
auto error = atomic_error.load();
|
|
328
364
|
if (error) {
|
|
@@ -336,7 +372,8 @@ static void search_typed( //
|
|
|
336
372
|
dense_indexes_py_t& indexes, py::buffer_info& vectors_info, //
|
|
337
373
|
std::size_t wanted, bool exact, std::size_t threads, //
|
|
338
374
|
py::array_t<dense_key_t>& keys_py, py::array_t<distance_t>& distances_py, py::array_t<Py_ssize_t>& counts_py,
|
|
339
|
-
std::atomic<std::size_t>& stats_visited_members, std::atomic<std::size_t>& stats_computed_distances
|
|
375
|
+
std::atomic<std::size_t>& stats_visited_members, std::atomic<std::size_t>& stats_computed_distances,
|
|
376
|
+
progress_func_t const& progress) {
|
|
340
377
|
|
|
341
378
|
auto keys_py2d = keys_py.template mutable_unchecked<2>();
|
|
342
379
|
auto distances_py2d = distances_py.template mutable_unchecked<2>();
|
|
@@ -354,6 +391,10 @@ static void search_typed( //
|
|
|
354
391
|
if (!query_mutexes)
|
|
355
392
|
throw std::bad_alloc();
|
|
356
393
|
|
|
394
|
+
// Progress status
|
|
395
|
+
progress_t progress_{progress};
|
|
396
|
+
std::atomic<std::size_t> processed{0};
|
|
397
|
+
|
|
357
398
|
atomic_error_t atomic_error{nullptr};
|
|
358
399
|
executor_default_t{threads}.dynamic(indexes.shards_.size(), [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
359
400
|
dense_index_py_t& index = *indexes.shards_[task_idx].get();
|
|
@@ -388,13 +429,19 @@ static void search_typed( //
|
|
|
388
429
|
stats_computed_distances += result.computed_distances;
|
|
389
430
|
|
|
390
431
|
// We don't want to check for signals from multiple threads
|
|
432
|
+
++processed;
|
|
391
433
|
if (thread_idx == 0)
|
|
392
|
-
if (PyErr_CheckSignals() != 0)
|
|
434
|
+
if (PyErr_CheckSignals() != 0 || !progress_(processed.load(), indexes.shards_.size())) {
|
|
435
|
+
atomic_error.store("Operation has been terminated");
|
|
393
436
|
return false;
|
|
437
|
+
}
|
|
394
438
|
return true;
|
|
395
439
|
}
|
|
396
440
|
});
|
|
397
441
|
|
|
442
|
+
// At the end report the latest numbers, because the reporter thread may be finished earlier
|
|
443
|
+
progress_(processed.load(), indexes.shards_.size());
|
|
444
|
+
|
|
398
445
|
// Raise the error from a single thread
|
|
399
446
|
auto error = atomic_error.load();
|
|
400
447
|
if (error) {
|
|
@@ -416,7 +463,8 @@ static void search_typed( //
|
|
|
416
463
|
*/
|
|
417
464
|
template <typename index_at>
|
|
418
465
|
static py::tuple search_many_in_index( //
|
|
419
|
-
index_at& index, py::buffer vectors, std::size_t wanted, bool exact, std::size_t threads
|
|
466
|
+
index_at& index, py::buffer vectors, std::size_t wanted, bool exact, std::size_t threads,
|
|
467
|
+
progress_func_t const& progress) {
|
|
420
468
|
|
|
421
469
|
if (wanted == 0)
|
|
422
470
|
return py::tuple(5);
|
|
@@ -441,11 +489,11 @@ static py::tuple search_many_in_index( //
|
|
|
441
489
|
|
|
442
490
|
// clang-format off
|
|
443
491
|
switch (numpy_string_to_kind(vectors_info.format)) {
|
|
444
|
-
case scalar_kind_t::b1x8_k: search_typed<b1x8_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances); break;
|
|
445
|
-
case scalar_kind_t::i8_k: search_typed<i8_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances); break;
|
|
446
|
-
case scalar_kind_t::f16_k: search_typed<f16_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances); break;
|
|
447
|
-
case scalar_kind_t::f32_k: search_typed<f32_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances); break;
|
|
448
|
-
case scalar_kind_t::f64_k: search_typed<f64_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances); break;
|
|
492
|
+
case scalar_kind_t::b1x8_k: search_typed<b1x8_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances, progress); break;
|
|
493
|
+
case scalar_kind_t::i8_k: search_typed<i8_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances, progress); break;
|
|
494
|
+
case scalar_kind_t::f16_k: search_typed<f16_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances, progress); break;
|
|
495
|
+
case scalar_kind_t::f32_k: search_typed<f32_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances, progress); break;
|
|
496
|
+
case scalar_kind_t::f64_k: search_typed<f64_t>(index, vectors_info, wanted, exact, threads, keys_py, distances_py, counts_py, stats_visited_members, stats_computed_distances, progress); break;
|
|
449
497
|
default: throw std::invalid_argument("Incompatible scalars in the query matrix: " + vectors_info.format);
|
|
450
498
|
}
|
|
451
499
|
// clang-format on
|
|
@@ -463,7 +511,8 @@ template <typename scalar_at>
|
|
|
463
511
|
static void search_typed_brute_force( //
|
|
464
512
|
py::buffer_info& dataset_info, py::buffer_info& queries_info, //
|
|
465
513
|
std::size_t wanted, std::size_t threads, metric_t const& metric, //
|
|
466
|
-
py::array_t<dense_key_t>& keys_py, py::array_t<distance_t>& distances_py, py::array_t<Py_ssize_t>& counts_py
|
|
514
|
+
py::array_t<dense_key_t>& keys_py, py::array_t<distance_t>& distances_py, py::array_t<Py_ssize_t>& counts_py,
|
|
515
|
+
progress_func_t const& progress) {
|
|
467
516
|
|
|
468
517
|
auto keys_py2d = keys_py.template mutable_unchecked<2>();
|
|
469
518
|
auto distances_py2d = distances_py.template mutable_unchecked<2>();
|
|
@@ -485,6 +534,10 @@ static void search_typed_brute_force( //
|
|
|
485
534
|
if (!query_mutexes)
|
|
486
535
|
throw std::bad_alloc();
|
|
487
536
|
|
|
537
|
+
// Progress status
|
|
538
|
+
progress_t progress_{progress};
|
|
539
|
+
std::atomic<std::size_t> processed{0};
|
|
540
|
+
|
|
488
541
|
executor_default_t{threads}.dynamic(tasks_count, [&](std::size_t thread_idx, std::size_t task_idx) {
|
|
489
542
|
//
|
|
490
543
|
std::size_t dataset_idx = task_idx / queries_count;
|
|
@@ -514,11 +567,15 @@ static void search_typed_brute_force( //
|
|
|
514
567
|
}
|
|
515
568
|
|
|
516
569
|
// We don't want to check for signals from multiple threads
|
|
570
|
+
++processed;
|
|
517
571
|
if (thread_idx == 0)
|
|
518
|
-
if (PyErr_CheckSignals() != 0)
|
|
572
|
+
if (PyErr_CheckSignals() != 0 || !progress_(processed.load(), tasks_count))
|
|
519
573
|
return false;
|
|
520
574
|
return true;
|
|
521
575
|
});
|
|
576
|
+
|
|
577
|
+
// At the end report the latest numbers, because the reporter thread may be finished earlier
|
|
578
|
+
progress_(processed.load(), tasks_count);
|
|
522
579
|
}
|
|
523
580
|
|
|
524
581
|
static py::tuple search_many_brute_force( //
|
|
@@ -526,7 +583,8 @@ static py::tuple search_many_brute_force( //
|
|
|
526
583
|
std::size_t wanted, std::size_t threads, //
|
|
527
584
|
metric_kind_t metric_kind, //
|
|
528
585
|
metric_signature_t metric_signature, //
|
|
529
|
-
std::uintptr_t metric_uintptr
|
|
586
|
+
std::uintptr_t metric_uintptr, //
|
|
587
|
+
progress_func_t const& progress) {
|
|
530
588
|
|
|
531
589
|
if (wanted == 0)
|
|
532
590
|
return py::tuple(5);
|
|
@@ -560,11 +618,11 @@ static py::tuple search_many_brute_force( //
|
|
|
560
618
|
|
|
561
619
|
// clang-format off
|
|
562
620
|
switch (dataset_kind) {
|
|
563
|
-
case scalar_kind_t::b1x8_k: search_typed_brute_force<b1x8_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py); break;
|
|
564
|
-
case scalar_kind_t::i8_k: search_typed_brute_force<i8_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py); break;
|
|
565
|
-
case scalar_kind_t::f16_k: search_typed_brute_force<f16_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py); break;
|
|
566
|
-
case scalar_kind_t::f32_k: search_typed_brute_force<f32_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py); break;
|
|
567
|
-
case scalar_kind_t::f64_k: search_typed_brute_force<f64_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py); break;
|
|
621
|
+
case scalar_kind_t::b1x8_k: search_typed_brute_force<b1x8_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py, progress); break;
|
|
622
|
+
case scalar_kind_t::i8_k: search_typed_brute_force<i8_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py, progress); break;
|
|
623
|
+
case scalar_kind_t::f16_k: search_typed_brute_force<f16_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py, progress); break;
|
|
624
|
+
case scalar_kind_t::f32_k: search_typed_brute_force<f32_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py, progress); break;
|
|
625
|
+
case scalar_kind_t::f64_k: search_typed_brute_force<f64_t>(dataset_info, queries_info, wanted, threads, metric, keys_py, distances_py, counts_py, progress); break;
|
|
568
626
|
default: throw std::invalid_argument("Incompatible vector types: " + dataset_info.format);
|
|
569
627
|
}
|
|
570
628
|
// clang-format on
|
|
@@ -603,7 +661,7 @@ template <typename scalar_at> struct rows_lookup_gt {
|
|
|
603
661
|
template <typename index_at>
|
|
604
662
|
static py::tuple cluster_vectors( //
|
|
605
663
|
index_at& index, py::buffer queries, //
|
|
606
|
-
std::size_t min_count, std::size_t max_count, std::size_t threads) {
|
|
664
|
+
std::size_t min_count, std::size_t max_count, std::size_t threads, progress_func_t const& progress) {
|
|
607
665
|
|
|
608
666
|
if (index.limits().threads_search < threads)
|
|
609
667
|
throw std::invalid_argument("Can't use that many threads!");
|
|
@@ -637,11 +695,11 @@ static py::tuple cluster_vectors( //
|
|
|
637
695
|
|
|
638
696
|
// clang-format off
|
|
639
697
|
switch (numpy_string_to_kind(queries_info.format)) {
|
|
640
|
-
case scalar_kind_t::b1x8_k: cluster_result = index.cluster(queries_begin.as<b1x8_t const>(), queries_end.as<b1x8_t const>(), config, keys_ptr, distances_ptr, executor); break;
|
|
641
|
-
case scalar_kind_t::i8_k: cluster_result = index.cluster(queries_begin.as<i8_t const>(), queries_end.as<i8_t const>(), config, keys_ptr, distances_ptr, executor); break;
|
|
642
|
-
case scalar_kind_t::f16_k: cluster_result = index.cluster(queries_begin.as<f16_t const>(), queries_end.as<f16_t const>(), config, keys_ptr, distances_ptr, executor); break;
|
|
643
|
-
case scalar_kind_t::f32_k: cluster_result = index.cluster(queries_begin.as<f32_t const>(), queries_end.as<f32_t const>(), config, keys_ptr, distances_ptr, executor); break;
|
|
644
|
-
case scalar_kind_t::f64_k: cluster_result = index.cluster(queries_begin.as<f64_t const>(), queries_end.as<f64_t const>(), config, keys_ptr, distances_ptr, executor); break;
|
|
698
|
+
case scalar_kind_t::b1x8_k: cluster_result = index.cluster(queries_begin.as<b1x8_t const>(), queries_end.as<b1x8_t const>(), config, keys_ptr, distances_ptr, executor, progress_t{progress}); break;
|
|
699
|
+
case scalar_kind_t::i8_k: cluster_result = index.cluster(queries_begin.as<i8_t const>(), queries_end.as<i8_t const>(), config, keys_ptr, distances_ptr, executor, progress_t{progress}); break;
|
|
700
|
+
case scalar_kind_t::f16_k: cluster_result = index.cluster(queries_begin.as<f16_t const>(), queries_end.as<f16_t const>(), config, keys_ptr, distances_ptr, executor, progress_t{progress}); break;
|
|
701
|
+
case scalar_kind_t::f32_k: cluster_result = index.cluster(queries_begin.as<f32_t const>(), queries_end.as<f32_t const>(), config, keys_ptr, distances_ptr, executor, progress_t{progress}); break;
|
|
702
|
+
case scalar_kind_t::f64_k: cluster_result = index.cluster(queries_begin.as<f64_t const>(), queries_end.as<f64_t const>(), config, keys_ptr, distances_ptr, executor, progress_t{progress}); break;
|
|
645
703
|
default: throw std::invalid_argument("Incompatible scalars in the query matrix: " + queries_info.format);
|
|
646
704
|
}
|
|
647
705
|
// clang-format on
|
|
@@ -677,7 +735,7 @@ static py::tuple cluster_vectors( //
|
|
|
677
735
|
template <typename index_at>
|
|
678
736
|
static py::tuple cluster_keys( //
|
|
679
737
|
index_at& index, py::array_t<dense_key_t> queries_py, //
|
|
680
|
-
std::size_t min_count, std::size_t max_count, std::size_t threads) {
|
|
738
|
+
std::size_t min_count, std::size_t max_count, std::size_t threads, progress_func_t const& progress) {
|
|
681
739
|
|
|
682
740
|
if (index.limits().threads_search < threads)
|
|
683
741
|
throw std::invalid_argument("Can't use that many threads!");
|
|
@@ -701,7 +759,7 @@ static py::tuple cluster_keys( //
|
|
|
701
759
|
config.max_clusters = max_count;
|
|
702
760
|
|
|
703
761
|
dense_clustering_result_t cluster_result =
|
|
704
|
-
index.cluster(queries_begin, queries_end, config, keys_ptr, distances_ptr, executor);
|
|
762
|
+
index.cluster(queries_begin, queries_end, config, keys_ptr, distances_ptr, executor, progress_t{progress});
|
|
705
763
|
cluster_result.error.raise();
|
|
706
764
|
|
|
707
765
|
// Those would be set to 1 for all entries, in case of success
|
|
@@ -721,7 +779,8 @@ static py::tuple cluster_keys( //
|
|
|
721
779
|
|
|
722
780
|
static std::unordered_map<dense_key_t, dense_key_t> join_index( //
|
|
723
781
|
dense_index_py_t const& a, dense_index_py_t const& b, //
|
|
724
|
-
std::size_t max_proposals, bool exact
|
|
782
|
+
std::size_t max_proposals, bool exact, //
|
|
783
|
+
progress_func_t const& progress) {
|
|
725
784
|
|
|
726
785
|
std::unordered_map<dense_key_t, dense_key_t> a_to_b;
|
|
727
786
|
dummy_key_to_key_mapping_t b_to_a;
|
|
@@ -733,7 +792,7 @@ static std::unordered_map<dense_key_t, dense_key_t> join_index( //
|
|
|
733
792
|
config.expansion = (std::max)(a.expansion_search(), b.expansion_search());
|
|
734
793
|
std::size_t threads = (std::min)(a.limits().threads(), b.limits().threads());
|
|
735
794
|
executor_default_t executor{threads};
|
|
736
|
-
join_result_t result = a.join(b, config, a_to_b, b_to_a, executor);
|
|
795
|
+
join_result_t result = a.join(b, config, a_to_b, b_to_a, executor, progress_t{progress});
|
|
737
796
|
forward_error(result);
|
|
738
797
|
|
|
739
798
|
return a_to_b;
|
|
@@ -749,14 +808,14 @@ static dense_index_py_t copy_index(dense_index_py_t const& index, bool force_cop
|
|
|
749
808
|
return std::move(result.index);
|
|
750
809
|
}
|
|
751
810
|
|
|
752
|
-
static void compact_index(dense_index_py_t& index, std::size_t threads) {
|
|
811
|
+
static void compact_index(dense_index_py_t& index, std::size_t threads, progress_func_t const& progress) {
|
|
753
812
|
|
|
754
813
|
if (!threads)
|
|
755
814
|
threads = std::thread::hardware_concurrency();
|
|
756
815
|
if (!index.reserve(index_limits_t(index.size(), threads)))
|
|
757
816
|
throw std::invalid_argument("Out of memory!");
|
|
758
817
|
|
|
759
|
-
index.compact(executor_default_t{threads});
|
|
818
|
+
index.compact(executor_default_t{threads}, progress_t{progress});
|
|
760
819
|
}
|
|
761
820
|
|
|
762
821
|
static py::dict index_metadata(index_dense_metadata_result_t const& meta) {
|
|
@@ -782,9 +841,9 @@ static py::dict index_metadata(index_dense_metadata_result_t const& meta) {
|
|
|
782
841
|
}
|
|
783
842
|
|
|
784
843
|
// clang-format off
|
|
785
|
-
template <typename index_at> void save_index_to_path(index_at const& index, std::string const& path) { index.save(path.c_str()).error.raise(); }
|
|
786
|
-
template <typename index_at> void load_index_from_path(index_at& index, std::string const& path) { index.load(path.c_str()).error.raise(); }
|
|
787
|
-
template <typename index_at> void view_index_from_path(index_at& index, std::string const& path) { index.view(path.c_str()).error.raise(); }
|
|
844
|
+
template <typename index_at> void save_index_to_path(index_at const& index, std::string const& path, progress_func_t const& progress) { index.save(path.c_str(), {}, progress_t{progress}).error.raise(); }
|
|
845
|
+
template <typename index_at> void load_index_from_path(index_at& index, std::string const& path, progress_func_t const& progress) { index.load(path.c_str(), {}, progress_t{progress}).error.raise(); }
|
|
846
|
+
template <typename index_at> void view_index_from_path(index_at& index, std::string const& path, progress_func_t const& progress) { index.view(path.c_str(), 0, {}, progress_t{progress}).error.raise(); }
|
|
788
847
|
template <typename index_at> void reset_index(index_at& index) { index.reset(); }
|
|
789
848
|
template <typename index_at> void clear_index(index_at& index) { index.clear(); }
|
|
790
849
|
template <typename index_at> std::size_t max_level(index_at const &index) { return index.max_level(); }
|
|
@@ -798,7 +857,7 @@ template <typename py_bytes_at> memory_mapped_file_t memory_map_from_bytes(py_by
|
|
|
798
857
|
return {(byte_t*)(info.ptr), static_cast<std::size_t>(info.size)};
|
|
799
858
|
}
|
|
800
859
|
|
|
801
|
-
template <typename index_at> py::object save_index_to_buffer(index_at const& index) {
|
|
860
|
+
template <typename index_at> py::object save_index_to_buffer(index_at const& index, progress_func_t const& progress) {
|
|
802
861
|
std::size_t serialized_length = index.serialized_length();
|
|
803
862
|
|
|
804
863
|
// Create an empty bytearray object using CPython API
|
|
@@ -814,7 +873,7 @@ template <typename index_at> py::object save_index_to_buffer(index_at const& ind
|
|
|
814
873
|
|
|
815
874
|
char* buffer = PyByteArray_AS_STRING(byte_array);
|
|
816
875
|
memory_mapped_file_t memory_map((byte_t*)buffer, serialized_length);
|
|
817
|
-
serialization_result_t result = index.save(std::move(memory_map));
|
|
876
|
+
serialization_result_t result = index.save(std::move(memory_map), {}, {}, progress_t{progress});
|
|
818
877
|
|
|
819
878
|
if (!result) {
|
|
820
879
|
Py_XDECREF(byte_array);
|
|
@@ -824,11 +883,13 @@ template <typename index_at> py::object save_index_to_buffer(index_at const& ind
|
|
|
824
883
|
return py::reinterpret_steal<py::object>(byte_array);
|
|
825
884
|
}
|
|
826
885
|
|
|
827
|
-
template <typename index_at>
|
|
828
|
-
|
|
886
|
+
template <typename index_at>
|
|
887
|
+
void load_index_from_buffer(index_at& index, py::bytes const& buffer, progress_func_t const& progress) {
|
|
888
|
+
index.load(memory_map_from_bytes(buffer), {}, {}, progress_t{progress}).error.raise();
|
|
829
889
|
}
|
|
830
|
-
template <typename index_at>
|
|
831
|
-
|
|
890
|
+
template <typename index_at>
|
|
891
|
+
void view_index_from_buffer(index_at& index, py::bytes const& buffer, progress_func_t const& progress) {
|
|
892
|
+
index.view(memory_map_from_bytes(buffer), {}, {}, progress_t{progress}).error.raise();
|
|
832
893
|
}
|
|
833
894
|
|
|
834
895
|
template <typename index_at> std::vector<typename index_at::stats_t> compute_levels_stats(index_at const& index) {
|
|
@@ -954,13 +1015,14 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
954
1015
|
py::arg("threads") = 0, //
|
|
955
1016
|
py::arg("metric_kind") = metric_kind_t::cos_k, //
|
|
956
1017
|
py::arg("metric_signature") = metric_signature_t::array_array_k, //
|
|
957
|
-
py::arg("metric_pointer") = 0
|
|
1018
|
+
py::arg("metric_pointer") = 0, //
|
|
1019
|
+
py::arg("progress") = nullptr //
|
|
958
1020
|
);
|
|
959
1021
|
|
|
960
1022
|
m.def(
|
|
961
1023
|
"hardware_acceleration",
|
|
962
|
-
[](scalar_kind_t scalar_kind, std::size_t dimensions, metric_kind_t metric_kind) ->
|
|
963
|
-
return metric_t(dimensions, metric_kind, scalar_kind).isa_kind()
|
|
1024
|
+
[](scalar_kind_t scalar_kind, std::size_t dimensions, metric_kind_t metric_kind) -> py::str {
|
|
1025
|
+
return isa_name(metric_t(dimensions, metric_kind, scalar_kind).isa_kind());
|
|
964
1026
|
},
|
|
965
1027
|
py::kw_only(), //
|
|
966
1028
|
py::arg("dtype") = scalar_kind_t::f32_k, //
|
|
@@ -989,7 +1051,8 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
989
1051
|
py::arg("vectors"), //
|
|
990
1052
|
py::kw_only(), //
|
|
991
1053
|
py::arg("copy") = true, //
|
|
992
|
-
py::arg("threads") = 0
|
|
1054
|
+
py::arg("threads") = 0, //
|
|
1055
|
+
py::arg("progress") = nullptr //
|
|
993
1056
|
);
|
|
994
1057
|
|
|
995
1058
|
i.def( //
|
|
@@ -997,7 +1060,8 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
997
1060
|
py::arg("queries"), //
|
|
998
1061
|
py::arg("count") = 10, //
|
|
999
1062
|
py::arg("exact") = false, //
|
|
1000
|
-
py::arg("threads") = 0
|
|
1063
|
+
py::arg("threads") = 0, //
|
|
1064
|
+
py::arg("progress") = nullptr //
|
|
1001
1065
|
);
|
|
1002
1066
|
|
|
1003
1067
|
i.def( //
|
|
@@ -1005,7 +1069,8 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
1005
1069
|
py::arg("queries"), //
|
|
1006
1070
|
py::arg("min_count") = 0, //
|
|
1007
1071
|
py::arg("max_count") = 0, //
|
|
1008
|
-
py::arg("threads") = 0
|
|
1072
|
+
py::arg("threads") = 0, //
|
|
1073
|
+
py::arg("progress") = nullptr //
|
|
1009
1074
|
);
|
|
1010
1075
|
|
|
1011
1076
|
i.def( //
|
|
@@ -1013,7 +1078,8 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
1013
1078
|
py::arg("queries"), //
|
|
1014
1079
|
py::arg("min_count") = 0, //
|
|
1015
1080
|
py::arg("max_count") = 0, //
|
|
1016
|
-
py::arg("threads") = 0
|
|
1081
|
+
py::arg("threads") = 0, //
|
|
1082
|
+
py::arg("progress") = nullptr //
|
|
1017
1083
|
);
|
|
1018
1084
|
|
|
1019
1085
|
i.def(
|
|
@@ -1205,19 +1271,24 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
1205
1271
|
},
|
|
1206
1272
|
py::arg("offset"));
|
|
1207
1273
|
|
|
1208
|
-
i.def("save_index_to_path", &save_index_to_path<dense_index_py_t
|
|
1209
|
-
i.def("load_index_from_path", &load_index_from_path<dense_index_py_t
|
|
1210
|
-
|
|
1274
|
+
i.def("save_index_to_path", &save_index_to_path<dense_index_py_t>, py::arg("path"), py::arg("progress") = nullptr);
|
|
1275
|
+
i.def("load_index_from_path", &load_index_from_path<dense_index_py_t>, py::arg("path"),
|
|
1276
|
+
py::arg("progress") = nullptr);
|
|
1277
|
+
i.def("view_index_from_path", &view_index_from_path<dense_index_py_t>, py::arg("path"),
|
|
1278
|
+
py::arg("progress") = nullptr);
|
|
1211
1279
|
|
|
1212
|
-
i.def("save_index_to_buffer", &save_index_to_buffer<dense_index_py_t
|
|
1213
|
-
i.def("load_index_from_buffer", &load_index_from_buffer<dense_index_py_t
|
|
1214
|
-
|
|
1280
|
+
i.def("save_index_to_buffer", &save_index_to_buffer<dense_index_py_t>, py::arg("progress") = nullptr);
|
|
1281
|
+
i.def("load_index_from_buffer", &load_index_from_buffer<dense_index_py_t>, py::arg("buffer"),
|
|
1282
|
+
py::arg("progress") = nullptr);
|
|
1283
|
+
i.def("view_index_from_buffer", &view_index_from_buffer<dense_index_py_t>, py::arg("buffer"),
|
|
1284
|
+
py::arg("progress") = nullptr);
|
|
1215
1285
|
|
|
1216
1286
|
i.def("reset", &reset_index<dense_index_py_t>);
|
|
1217
1287
|
i.def("clear", &clear_index<dense_index_py_t>);
|
|
1218
1288
|
i.def("copy", ©_index, py::kw_only(), py::arg("copy") = true);
|
|
1219
|
-
i.def("compact", &compact_index);
|
|
1220
|
-
i.def("join", &join_index, py::arg("other"), py::arg("max_proposals") = 0, py::arg("exact") = false
|
|
1289
|
+
i.def("compact", &compact_index, py::arg("threads"), py::arg("progress") = nullptr);
|
|
1290
|
+
i.def("join", &join_index, py::arg("other"), py::arg("max_proposals") = 0, py::arg("exact") = false,
|
|
1291
|
+
py::arg("progress") = nullptr);
|
|
1221
1292
|
|
|
1222
1293
|
using punned_index_stats_t = typename dense_index_py_t::stats_t;
|
|
1223
1294
|
auto i_stats = py::class_<punned_index_stats_t>(m, "IndexStats");
|
|
@@ -1242,6 +1313,7 @@ PYBIND11_MODULE(compiled, m) {
|
|
|
1242
1313
|
py::arg("query"), //
|
|
1243
1314
|
py::arg("count") = 10, //
|
|
1244
1315
|
py::arg("exact") = false, //
|
|
1245
|
-
py::arg("threads") = 0
|
|
1316
|
+
py::arg("threads") = 0, //
|
|
1317
|
+
py::arg("progress") = nullptr //
|
|
1246
1318
|
);
|
|
1247
1319
|
}
|