usearch 0.21.0 → 0.22.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/workflows/prerelease.yml +19 -0
- package/.github/workflows/release.yml +28 -0
- package/.github/workflows/update_version.sh +2 -1
- package/CMakeLists.txt +4 -0
- package/Cargo.toml +1 -1
- package/README.md +94 -605
- package/c/lib.cpp +19 -15
- package/c/usearch.h +26 -23
- package/cmake/emscripten.cmake +8 -0
- package/conanfile.py +1 -1
- package/cpp/README.md +88 -0
- package/cpp/bench.cpp +32 -41
- package/cpp/test.cpp +13 -7
- package/docs/golang/index.rst +6 -0
- package/docs/index.rst +5 -2
- package/docs/python/reference.rst +9 -2
- package/docs/rust/index.rst +6 -0
- package/docs/swift/index.rst +6 -0
- package/golang/README.md +28 -0
- package/include/usearch/index.hpp +204 -78
- package/include/usearch/index_punned_dense.hpp +239 -85
- package/java/README.md +22 -0
- package/javascript/README.md +27 -0
- package/javascript/lib.cpp +1 -1
- package/package.json +1 -1
- package/python/README.md +283 -0
- package/python/lib.cpp +61 -11
- package/python/scripts/join.py +7 -3
- package/python/scripts/test.py +28 -243
- package/python/scripts/test_jit.py +244 -0
- package/python/usearch/index.py +97 -25
- package/rust/README.md +67 -0
- package/swift/README.md +20 -0
- package/wasm/CMakeLists.txt +15 -0
- package/wasm/lib.cpp +5 -0
- package/wasmer.toml +8 -0
package/c/lib.cpp
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
#include <usearch/index_punned_dense.hpp>
|
|
4
4
|
|
|
5
|
+
#ifndef USEARCH_EXPORT
|
|
6
|
+
#define USEARCH_EXPORT
|
|
7
|
+
#endif
|
|
8
|
+
|
|
5
9
|
extern "C" {
|
|
6
10
|
#include "usearch.h"
|
|
7
11
|
}
|
|
@@ -88,7 +92,7 @@ index_punned_dense_metric_t udf(metric_kind_t kind, usearch_metric_t raw_ptr) {
|
|
|
88
92
|
|
|
89
93
|
extern "C" {
|
|
90
94
|
|
|
91
|
-
usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) {
|
|
95
|
+
USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) {
|
|
92
96
|
|
|
93
97
|
assert(options && error);
|
|
94
98
|
|
|
@@ -108,48 +112,48 @@ usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* e
|
|
|
108
112
|
return result_ptr;
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
void usearch_free(usearch_index_t index, usearch_error_t*) { delete reinterpret_cast<index_t*>(index); }
|
|
115
|
+
USEARCH_EXPORT void usearch_free(usearch_index_t index, usearch_error_t*) { delete reinterpret_cast<index_t*>(index); }
|
|
112
116
|
|
|
113
|
-
void usearch_save(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
117
|
+
USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
114
118
|
serialization_result_t result = reinterpret_cast<index_t*>(index)->save(path);
|
|
115
119
|
if (!result)
|
|
116
120
|
*error = result.error.what();
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
void usearch_load(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
123
|
+
USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
120
124
|
serialization_result_t result = reinterpret_cast<index_t*>(index)->load(path);
|
|
121
125
|
if (!result)
|
|
122
126
|
*error = result.error.what();
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
void usearch_view(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
129
|
+
USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
126
130
|
serialization_result_t result = reinterpret_cast<index_t*>(index)->view(path);
|
|
127
131
|
if (!result)
|
|
128
132
|
*error = result.error.what();
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
size_t usearch_size(usearch_index_t index, usearch_error_t*) { //
|
|
135
|
+
USEARCH_EXPORT size_t usearch_size(usearch_index_t index, usearch_error_t*) { //
|
|
132
136
|
return reinterpret_cast<index_t*>(index)->size();
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
size_t usearch_capacity(usearch_index_t index, usearch_error_t*) {
|
|
139
|
+
USEARCH_EXPORT size_t usearch_capacity(usearch_index_t index, usearch_error_t*) {
|
|
136
140
|
return reinterpret_cast<index_t*>(index)->capacity();
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
size_t usearch_dimensions(usearch_index_t index, usearch_error_t*) {
|
|
143
|
+
USEARCH_EXPORT size_t usearch_dimensions(usearch_index_t index, usearch_error_t*) {
|
|
140
144
|
return reinterpret_cast<index_t*>(index)->dimensions();
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
size_t usearch_connectivity(usearch_index_t index, usearch_error_t*) {
|
|
147
|
+
USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_t*) {
|
|
144
148
|
return reinterpret_cast<index_t*>(index)->connectivity();
|
|
145
149
|
}
|
|
146
150
|
|
|
147
|
-
void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t*) {
|
|
151
|
+
USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t*) {
|
|
148
152
|
// TODO: Consider returning the new capacity.
|
|
149
153
|
reinterpret_cast<index_t*>(index)->reserve(capacity);
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
void usearch_add(
|
|
156
|
+
USEARCH_EXPORT void usearch_add( //
|
|
153
157
|
usearch_index_t index, usearch_label_t label, void const* vector, usearch_scalar_kind_t kind, //
|
|
154
158
|
usearch_error_t* error) {
|
|
155
159
|
add_result_t result = add_(reinterpret_cast<index_t*>(index), label, vector, to_native_scalar(kind));
|
|
@@ -157,11 +161,11 @@ void usearch_add(
|
|
|
157
161
|
*error = result.error.what();
|
|
158
162
|
}
|
|
159
163
|
|
|
160
|
-
bool usearch_contains(usearch_index_t index, usearch_label_t label, usearch_error_t*) {
|
|
164
|
+
USEARCH_EXPORT bool usearch_contains(usearch_index_t index, usearch_label_t label, usearch_error_t*) {
|
|
161
165
|
return reinterpret_cast<index_t*>(index)->contains(label);
|
|
162
166
|
}
|
|
163
167
|
|
|
164
|
-
size_t usearch_search(
|
|
168
|
+
USEARCH_EXPORT size_t usearch_search( //
|
|
165
169
|
usearch_index_t index, void const* vector, usearch_scalar_kind_t kind, size_t results_limit, //
|
|
166
170
|
usearch_label_t* found_labels, usearch_distance_t* found_distances, usearch_error_t* error) {
|
|
167
171
|
search_result_t result = search_(reinterpret_cast<index_t*>(index), vector, to_native_scalar(kind), results_limit);
|
|
@@ -173,13 +177,13 @@ size_t usearch_search(
|
|
|
173
177
|
return result.dump_to(found_labels, found_distances);
|
|
174
178
|
}
|
|
175
179
|
|
|
176
|
-
bool usearch_get(
|
|
180
|
+
USEARCH_EXPORT bool usearch_get( //
|
|
177
181
|
usearch_index_t index, usearch_label_t label, //
|
|
178
182
|
void* vector, usearch_scalar_kind_t kind, usearch_error_t*) {
|
|
179
183
|
return get_(reinterpret_cast<index_t*>(index), label, vector, to_native_scalar(kind));
|
|
180
184
|
}
|
|
181
185
|
|
|
182
|
-
void usearch_remove(usearch_index_t, usearch_label_t, usearch_error_t* error) {
|
|
186
|
+
USEARCH_EXPORT void usearch_remove(usearch_index_t, usearch_label_t, usearch_error_t* error) {
|
|
183
187
|
if (error != nullptr)
|
|
184
188
|
*error = "USearch does not support removal of elements yet.";
|
|
185
189
|
}
|
package/c/usearch.h
CHANGED
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
#ifdef __cplusplus
|
|
5
5
|
extern "C" {
|
|
6
6
|
#endif
|
|
7
|
+
#ifndef USEARCH_EXPORT
|
|
8
|
+
#define USEARCH_EXPORT
|
|
9
|
+
#endif
|
|
7
10
|
#include <stdbool.h> // `bool`
|
|
8
11
|
#include <stdint.h> // `size_t`
|
|
9
12
|
|
|
10
|
-
typedef void* usearch_index_t;
|
|
11
|
-
typedef uint32_t usearch_label_t;
|
|
12
|
-
typedef float usearch_distance_t;
|
|
13
|
-
typedef char const* usearch_error_t;
|
|
13
|
+
USEARCH_EXPORT typedef void* usearch_index_t;
|
|
14
|
+
USEARCH_EXPORT typedef uint32_t usearch_label_t;
|
|
15
|
+
USEARCH_EXPORT typedef float usearch_distance_t;
|
|
16
|
+
USEARCH_EXPORT typedef char const* usearch_error_t;
|
|
14
17
|
|
|
15
|
-
typedef usearch_distance_t (*usearch_metric_t)(void const*, void const*);
|
|
18
|
+
USEARCH_EXPORT typedef usearch_distance_t (*usearch_metric_t)(void const*, void const*);
|
|
16
19
|
|
|
17
|
-
typedef enum usearch_metric_kind_t {
|
|
20
|
+
USEARCH_EXPORT typedef enum usearch_metric_kind_t {
|
|
18
21
|
usearch_metric_ip_k = 0,
|
|
19
22
|
usearch_metric_l2sq_k,
|
|
20
23
|
usearch_metric_cos_k,
|
|
@@ -27,7 +30,7 @@ typedef enum usearch_metric_kind_t {
|
|
|
27
30
|
usearch_metric_unknown_k,
|
|
28
31
|
} usearch_metric_kind_t;
|
|
29
32
|
|
|
30
|
-
typedef enum usearch_scalar_kind_t {
|
|
33
|
+
USEARCH_EXPORT typedef enum usearch_scalar_kind_t {
|
|
31
34
|
usearch_scalar_f32_k = 0,
|
|
32
35
|
usearch_scalar_f64_k,
|
|
33
36
|
usearch_scalar_f16_k,
|
|
@@ -36,7 +39,7 @@ typedef enum usearch_scalar_kind_t {
|
|
|
36
39
|
usearch_scalar_unknown_k,
|
|
37
40
|
} usearch_scalar_kind_t;
|
|
38
41
|
|
|
39
|
-
typedef struct usearch_init_options_t {
|
|
42
|
+
USEARCH_EXPORT typedef struct usearch_init_options_t {
|
|
40
43
|
|
|
41
44
|
usearch_metric_kind_t metric_kind;
|
|
42
45
|
usearch_metric_t metric;
|
|
@@ -48,39 +51,39 @@ typedef struct usearch_init_options_t {
|
|
|
48
51
|
size_t expansion_search;
|
|
49
52
|
} usearch_init_options_t;
|
|
50
53
|
|
|
51
|
-
usearch_index_t usearch_init(usearch_init_options_t*, usearch_error_t*);
|
|
52
|
-
void usearch_free(usearch_index_t, usearch_error_t*);
|
|
54
|
+
USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t*, usearch_error_t*);
|
|
55
|
+
USEARCH_EXPORT void usearch_free(usearch_index_t, usearch_error_t*);
|
|
53
56
|
|
|
54
|
-
void usearch_save(usearch_index_t, char const* path, usearch_error_t*);
|
|
55
|
-
void usearch_load(usearch_index_t, char const* path, usearch_error_t*);
|
|
56
|
-
void usearch_view(usearch_index_t, char const* path, usearch_error_t*);
|
|
57
|
+
USEARCH_EXPORT void usearch_save(usearch_index_t, char const* path, usearch_error_t*);
|
|
58
|
+
USEARCH_EXPORT void usearch_load(usearch_index_t, char const* path, usearch_error_t*);
|
|
59
|
+
USEARCH_EXPORT void usearch_view(usearch_index_t, char const* path, usearch_error_t*);
|
|
57
60
|
|
|
58
|
-
size_t usearch_size(usearch_index_t, usearch_error_t*);
|
|
59
|
-
size_t usearch_capacity(usearch_index_t, usearch_error_t*);
|
|
60
|
-
size_t usearch_dimensions(usearch_index_t, usearch_error_t*);
|
|
61
|
-
size_t usearch_connectivity(usearch_index_t, usearch_error_t*);
|
|
61
|
+
USEARCH_EXPORT size_t usearch_size(usearch_index_t, usearch_error_t*);
|
|
62
|
+
USEARCH_EXPORT size_t usearch_capacity(usearch_index_t, usearch_error_t*);
|
|
63
|
+
USEARCH_EXPORT size_t usearch_dimensions(usearch_index_t, usearch_error_t*);
|
|
64
|
+
USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t, usearch_error_t*);
|
|
62
65
|
|
|
63
|
-
void usearch_reserve(usearch_index_t, size_t capacity, usearch_error_t*);
|
|
66
|
+
USEARCH_EXPORT void usearch_reserve(usearch_index_t, size_t capacity, usearch_error_t*);
|
|
64
67
|
|
|
65
|
-
void usearch_add(
|
|
68
|
+
USEARCH_EXPORT void usearch_add( //
|
|
66
69
|
usearch_index_t, usearch_label_t, void const* vector, usearch_scalar_kind_t vector_kind, //
|
|
67
70
|
usearch_error_t*);
|
|
68
71
|
|
|
69
|
-
bool usearch_contains(usearch_index_t, usearch_label_t, usearch_error_t*);
|
|
72
|
+
USEARCH_EXPORT bool usearch_contains(usearch_index_t, usearch_label_t, usearch_error_t*);
|
|
70
73
|
|
|
71
74
|
/**
|
|
72
75
|
* @brief Performs k-Approximate Nearest Neighbors Search.
|
|
73
76
|
* @return Number of found matches.
|
|
74
77
|
*/
|
|
75
|
-
size_t usearch_search(
|
|
78
|
+
USEARCH_EXPORT size_t usearch_search( //
|
|
76
79
|
usearch_index_t, void const* query_vector, usearch_scalar_kind_t query_kind, size_t results_limit, //
|
|
77
80
|
usearch_label_t* found_labels, usearch_distance_t* found_distances, usearch_error_t*);
|
|
78
81
|
|
|
79
|
-
bool usearch_get(
|
|
82
|
+
USEARCH_EXPORT bool usearch_get( //
|
|
80
83
|
usearch_index_t, usearch_label_t, //
|
|
81
84
|
void* vector, usearch_scalar_kind_t vector_kind, usearch_error_t*);
|
|
82
85
|
|
|
83
|
-
void usearch_remove(usearch_index_t, usearch_label_t, usearch_error_t*);
|
|
86
|
+
USEARCH_EXPORT void usearch_remove(usearch_index_t, usearch_label_t, usearch_error_t*);
|
|
84
87
|
|
|
85
88
|
#ifdef __cplusplus
|
|
86
89
|
}
|
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.
|
|
7
|
+
version = '0.22.0'
|
|
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/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# USearch C++ Inteface
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
To use in a C++ project simply copy the `include/usearch/index.hpp` header into your project.
|
|
6
|
+
Alternatively fetch it with CMake:
|
|
7
|
+
|
|
8
|
+
```cmake
|
|
9
|
+
FetchContent_Declare(usearch GIT_REPOSITORY https://github.com/unum-cloud/usearch.git)
|
|
10
|
+
FetchContent_MakeAvailable(usearch)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
Once included, the low-level C++11 interface is as simple as it gets: `reserve()`, `add()`, `search()`, `size()`, `capacity()`, `save()`, `load()`, `view()`.
|
|
16
|
+
This covers 90% of use-cases.
|
|
17
|
+
|
|
18
|
+
```c++
|
|
19
|
+
using namespace unum::usearch;
|
|
20
|
+
|
|
21
|
+
index_gt<cos_gt<float>> index;
|
|
22
|
+
float vec[3] = {0.1, 0.3, 0.2};
|
|
23
|
+
|
|
24
|
+
index.reserve(10);
|
|
25
|
+
index.add(/* label: */ 42, /* vector: */ {&vec[0], 3});
|
|
26
|
+
auto results = index.search(/* query: */ {&vec[0], 3}, 5 /* neighbors */);
|
|
27
|
+
|
|
28
|
+
for (std::size_t i = 0; i != results.size(); ++i)
|
|
29
|
+
results[i].element.label, results[i].element.vector, results[i].distance;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The `add` is thread-safe for concurrent index construction.
|
|
33
|
+
|
|
34
|
+
## Serialization
|
|
35
|
+
|
|
36
|
+
```c++
|
|
37
|
+
index.save("index.usearch");
|
|
38
|
+
index.load("index.usearch"); // Copying from disk
|
|
39
|
+
index.view("index.usearch"); // Memory-mapping from disk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## User-Defined Metrics in C++
|
|
43
|
+
|
|
44
|
+
For advanced users, more compile-time abstractions are available.
|
|
45
|
+
|
|
46
|
+
```cpp
|
|
47
|
+
template <typename metric_at = ip_gt<float>, //
|
|
48
|
+
typename label_at = std::size_t, // `uint32_t`, `uuid_t`...
|
|
49
|
+
typename id_at = std::uint32_t, // `uint40_t`, `uint64_t`...
|
|
50
|
+
typename scalar_at = float, // `double`, `half`, `char`...
|
|
51
|
+
typename allocator_at = std::allocator<char>> //
|
|
52
|
+
class index_gt;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
You may want to use a custom memory allocator or a rare scalar type, but most often, you would start by defining a custom similarity measure.
|
|
56
|
+
The function object should have the following signature to support different-length vectors.
|
|
57
|
+
|
|
58
|
+
```cpp
|
|
59
|
+
struct custom_metric_t {
|
|
60
|
+
T operator()(T const* a, T const* b, std::size_t a_length, std::size_t b_length) const;
|
|
61
|
+
};
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The following distances are pre-packaged:
|
|
65
|
+
|
|
66
|
+
- `cos_gt<scalar_t>` for "Cosine" or "Angular" distance.
|
|
67
|
+
- `ip_gt<scalar_t>` for "Inner Product" or "Dot Product" distance.
|
|
68
|
+
- `l2sq_gt<scalar_t>` for the squared "L2" or "Euclidean" distance.
|
|
69
|
+
- `jaccard_gt<scalar_t>` for "Jaccard" distance between two ordered sets of unique elements.
|
|
70
|
+
- `hamming_gt<scalar_t>` for "Hamming" distance, as the number of shared bits in hashes.
|
|
71
|
+
- `tanimoto_gt<scalar_t>` for "Tanimoto" coefficient for bit-strings.
|
|
72
|
+
- `sorensen_gt<scalar_t>` for "Dice-Sorensen" coefficient for bit-strings.
|
|
73
|
+
- `pearson_correlation_gt<scalar_t>` for "Pearson" correlation between probability distributions.
|
|
74
|
+
- `haversine_gt<scalar_t>` for "Haversine" or "Great Circle" distance between coordinates used in GIS applications.
|
|
75
|
+
|
|
76
|
+
## Multi-Threading
|
|
77
|
+
|
|
78
|
+
Most AI, HPC, or Big Data packages use some form of a thread pool.
|
|
79
|
+
Instead of spawning additional threads within USearch, we focus on the thread safety of `add()` function, simplifying resource management.
|
|
80
|
+
|
|
81
|
+
```cpp
|
|
82
|
+
#pragma omp parallel for
|
|
83
|
+
for (std::size_t i = 0; i < n; ++i)
|
|
84
|
+
native.add(label, span_t{vector, dims}, add_config_t { .thread = omp_get_thread_num() });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
During initialization, we allocate enough temporary memory for all the cores on the machine.
|
|
88
|
+
On the call, the user can supply the identifier of the current thread, making this library easy to integrate with OpenMP and similar tools.
|
package/cpp/bench.cpp
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
#define STDERR_FILENO HANDLE(2)
|
|
17
17
|
#else
|
|
18
|
-
#
|
|
18
|
+
#if defined(__linux__)
|
|
19
|
+
#include <execinfo.h> // `backtrace`
|
|
20
|
+
#endif
|
|
19
21
|
#include <fcntl.h> // `open`
|
|
20
22
|
#include <stdlib.h> // `getenv`
|
|
21
23
|
#include <sys/mman.h> // `mmap`
|
|
@@ -36,7 +38,9 @@
|
|
|
36
38
|
#include <vector>
|
|
37
39
|
|
|
38
40
|
#include <clipp.h> // Command Line Interface
|
|
39
|
-
#
|
|
41
|
+
#if USEARCH_USE_OPENMP
|
|
42
|
+
#include <omp.h> // `omp_set_num_threads()`
|
|
43
|
+
#endif
|
|
40
44
|
|
|
41
45
|
#include <simsimd/simsimd.h>
|
|
42
46
|
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
using namespace unum::usearch;
|
|
46
50
|
using namespace unum;
|
|
47
51
|
|
|
52
|
+
using label_t = std::int64_t;
|
|
48
53
|
using vector_id_t = std::uint32_t;
|
|
49
54
|
using vector_view_t = span_gt<float const>;
|
|
50
55
|
|
|
@@ -278,15 +283,19 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
|
|
|
278
283
|
|
|
279
284
|
running_stats_printer_t printer{n, "Indexing"};
|
|
280
285
|
|
|
286
|
+
#if USEARCH_USE_OPENMP
|
|
281
287
|
#pragma omp parallel for schedule(static, 32)
|
|
288
|
+
#endif
|
|
282
289
|
for (std::size_t i = 0; i < n; ++i) {
|
|
283
290
|
add_config_t config;
|
|
291
|
+
#if USEARCH_USE_OPENMP
|
|
284
292
|
config.thread = omp_get_thread_num();
|
|
293
|
+
#endif
|
|
285
294
|
config.store_vector = true;
|
|
286
295
|
vector_view_t vector{vectors + dims * i, dims};
|
|
287
296
|
native.add(ids[i], vector, config);
|
|
288
297
|
printer.progress++;
|
|
289
|
-
if (
|
|
298
|
+
if (config.thread == 0)
|
|
290
299
|
printer.refresh();
|
|
291
300
|
}
|
|
292
301
|
}
|
|
@@ -299,34 +308,18 @@ void search_many( //
|
|
|
299
308
|
std::string name = "Search " + std::to_string(wanted);
|
|
300
309
|
running_stats_printer_t printer{n, name.c_str()};
|
|
301
310
|
|
|
311
|
+
#if USEARCH_USE_OPENMP
|
|
302
312
|
#pragma omp parallel for schedule(static, 32)
|
|
313
|
+
#endif
|
|
303
314
|
for (std::size_t i = 0; i < n; ++i) {
|
|
304
315
|
search_config_t config;
|
|
316
|
+
#if USEARCH_USE_OPENMP
|
|
305
317
|
config.thread = omp_get_thread_num();
|
|
318
|
+
#endif
|
|
306
319
|
vector_view_t vector{vectors + dims * i, dims};
|
|
307
320
|
native.search(vector, wanted, config).dump_to(ids + wanted * i, distances + wanted * i);
|
|
308
321
|
printer.progress++;
|
|
309
|
-
if (
|
|
310
|
-
printer.refresh();
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
template <typename index_at, typename vector_id_at, typename real_at>
|
|
315
|
-
void paginate_many( //
|
|
316
|
-
index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted,
|
|
317
|
-
vector_id_at const* hints) {
|
|
318
|
-
|
|
319
|
-
std::string name = "Paginate " + std::to_string(wanted);
|
|
320
|
-
running_stats_printer_t printer{n, name.c_str()};
|
|
321
|
-
|
|
322
|
-
#pragma omp parallel for schedule(static, 32)
|
|
323
|
-
for (std::size_t i = 0; i < n; ++i) {
|
|
324
|
-
search_config_t config;
|
|
325
|
-
config.thread = omp_get_thread_num();
|
|
326
|
-
vector_view_t vector{vectors + dims * i, dims};
|
|
327
|
-
native.search_around(hints[i], vector, wanted, config);
|
|
328
|
-
printer.progress++;
|
|
329
|
-
if (omp_get_thread_num() == 0)
|
|
322
|
+
if (config.thread == 0)
|
|
330
323
|
printer.refresh();
|
|
331
324
|
}
|
|
332
325
|
}
|
|
@@ -397,14 +390,6 @@ static void single_shot(dataset_at& dataset, index_at& index, bool construct = t
|
|
|
397
390
|
std::printf("Unmatched %.2f %% (%zu items)\n", unmatched_count * 100.f / index.size(), unmatched_count);
|
|
398
391
|
std::printf("Proposals %.2f / man (%zu total)\n", join_attempts * 1.f / index.size(), join_attempts);
|
|
399
392
|
|
|
400
|
-
// Paginate
|
|
401
|
-
std::vector<vector_id_t> hints(dataset.queries_count());
|
|
402
|
-
for (std::size_t i = 0; i != hints.size(); ++i)
|
|
403
|
-
hints[i] = dataset.neighborhood(i)[0];
|
|
404
|
-
paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 10, hints.data());
|
|
405
|
-
paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 100, hints.data());
|
|
406
|
-
paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 1000, hints.data());
|
|
407
|
-
|
|
408
393
|
std::printf("------------\n");
|
|
409
394
|
std::printf("\n");
|
|
410
395
|
}
|
|
@@ -416,7 +401,7 @@ void handler(int sig) {
|
|
|
416
401
|
// get void*'s for all entries on the stack
|
|
417
402
|
#if defined(USEARCH_DEFINED_WINDOWS)
|
|
418
403
|
size = CaptureStackBackTrace(0, 10, array, NULL);
|
|
419
|
-
#
|
|
404
|
+
#elif defined(USEARCH_DEFINED_LINUX)
|
|
420
405
|
size = backtrace(array, 10);
|
|
421
406
|
#endif // WINDOWS
|
|
422
407
|
|
|
@@ -438,7 +423,7 @@ void handler(int sig) {
|
|
|
438
423
|
WriteFile(STDERR_FILENO, "\n", 1, &bytes_written, NULL);
|
|
439
424
|
}
|
|
440
425
|
free(symbol);
|
|
441
|
-
#
|
|
426
|
+
#elif defined(USEARCH_DEFINED_LINUX)
|
|
442
427
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
|
443
428
|
#endif // WINDOWS
|
|
444
429
|
|
|
@@ -555,24 +540,24 @@ void run_big_or_small(dataset_at& dataset, args_t const& args, index_config_t co
|
|
|
555
540
|
if (args.native) {
|
|
556
541
|
if (args.metric_cos) {
|
|
557
542
|
std::printf("-- Metric: Angular\n");
|
|
558
|
-
run_typed<index_gt<cos_gt<float>,
|
|
543
|
+
run_typed<index_gt<cos_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
559
544
|
} else if (args.metric_l2) {
|
|
560
545
|
std::printf("-- Metric: Euclidean\n");
|
|
561
|
-
run_typed<index_gt<l2sq_gt<float>,
|
|
546
|
+
run_typed<index_gt<l2sq_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
562
547
|
} else if (args.metric_haversine) {
|
|
563
548
|
std::printf("-- Metric: Haversine\n");
|
|
564
|
-
run_typed<index_gt<haversine_gt<float>,
|
|
549
|
+
run_typed<index_gt<haversine_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
565
550
|
} else {
|
|
566
551
|
std::printf("-- Metric: Inner Product\n");
|
|
567
|
-
run_typed<index_gt<ip_gt<float>,
|
|
552
|
+
run_typed<index_gt<ip_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
568
553
|
}
|
|
569
554
|
} else
|
|
570
|
-
run_punned<index_punned_dense_gt<
|
|
555
|
+
run_punned<index_punned_dense_gt<label_t, neighbor_id_at>>(dataset, args, config, limits);
|
|
571
556
|
}
|
|
572
557
|
|
|
573
558
|
void report_alternative_setups() {
|
|
574
559
|
using set_member_t = std::uint32_t;
|
|
575
|
-
using sets_index_t = index_gt<jaccard_gt<set_member_t>,
|
|
560
|
+
using sets_index_t = index_gt<jaccard_gt<set_member_t>, label_t, std::uint32_t>;
|
|
576
561
|
set_member_t set_a[] = {10, 12, 15};
|
|
577
562
|
set_member_t set_b[] = {11, 12, 15, 16};
|
|
578
563
|
sets_index_t index;
|
|
@@ -662,12 +647,14 @@ int main(int argc, char** argv) {
|
|
|
662
647
|
exit(0);
|
|
663
648
|
}
|
|
664
649
|
|
|
650
|
+
#if USEARCH_USE_OPENMP
|
|
665
651
|
// Instead of relying on `multithreaded` from "index_punned_dense.hpp" we will use OpenMP
|
|
666
652
|
// to better estimate statistics between tasks batches, without having to recreate
|
|
667
653
|
// the threads.
|
|
668
654
|
omp_set_dynamic(true);
|
|
669
655
|
omp_set_num_threads(args.threads);
|
|
670
656
|
std::printf("- OpenMP threads: %d\n", omp_get_max_threads());
|
|
657
|
+
#endif
|
|
671
658
|
|
|
672
659
|
std::printf("- Dataset: \n");
|
|
673
660
|
std::printf("-- Base vectors path: %s\n", args.path_vectors.c_str());
|
|
@@ -695,7 +682,7 @@ int main(int argc, char** argv) {
|
|
|
695
682
|
// config.expansion_search = args.expansion_search;
|
|
696
683
|
index_limits_t limits;
|
|
697
684
|
limits.threads_add = limits.threads_search = args.threads;
|
|
698
|
-
limits.
|
|
685
|
+
limits.members = dataset.vectors_count();
|
|
699
686
|
|
|
700
687
|
std::printf("- Index: \n");
|
|
701
688
|
std::printf("-- Connectivity: %zu\n", config.connectivity);
|
|
@@ -703,7 +690,11 @@ int main(int argc, char** argv) {
|
|
|
703
690
|
// std::printf("-- Expansion @ Search: %zu\n", config.expansion_search);
|
|
704
691
|
|
|
705
692
|
if (args.big)
|
|
693
|
+
#ifdef USEARCH_64BIT_ENV
|
|
706
694
|
run_big_or_small<uint40_t>(dataset, args, config, limits);
|
|
695
|
+
#else
|
|
696
|
+
std::printf("Error: Don't use 40 bit identifiers in 32bit environment\n");
|
|
697
|
+
#endif
|
|
707
698
|
else
|
|
708
699
|
run_big_or_small<std::uint32_t>(dataset, args, config, limits);
|
|
709
700
|
|
package/cpp/test.cpp
CHANGED
|
@@ -84,17 +84,23 @@ template <typename scalar_at, typename index_at> void test3d_punned(index_at&& i
|
|
|
84
84
|
using view_t = span_gt<scalar_t const>;
|
|
85
85
|
using span_t = span_gt<scalar_at>;
|
|
86
86
|
|
|
87
|
-
scalar_t
|
|
87
|
+
scalar_t vec42[3] = {10, 20, 15};
|
|
88
|
+
scalar_t vec43[3] = {19, 22, 11};
|
|
88
89
|
|
|
89
90
|
index.reserve(10);
|
|
90
|
-
index.add(42, view_t{&
|
|
91
|
+
index.add(42, view_t{&vec42[0], 3ul});
|
|
91
92
|
|
|
92
93
|
// Reconstruct
|
|
93
|
-
scalar_t
|
|
94
|
-
index.get(42, span_t{&
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
97
|
-
expect(
|
|
94
|
+
scalar_t vec42_reconstructed[3] = {0, 0, 0};
|
|
95
|
+
index.get(42, span_t{&vec42_reconstructed[0], 3ul});
|
|
96
|
+
expect(vec42_reconstructed[0] == vec42[0]);
|
|
97
|
+
expect(vec42_reconstructed[1] == vec42[1]);
|
|
98
|
+
expect(vec42_reconstructed[2] == vec42[2]);
|
|
99
|
+
|
|
100
|
+
index.add(43, view_t{&vec43[0], 3ul});
|
|
101
|
+
expect(index.size() == 2);
|
|
102
|
+
index.remove(43);
|
|
103
|
+
expect(index.size() == 1);
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
template <typename index_at> void test_sets(index_at&& index) {
|
package/docs/index.rst
CHANGED
|
@@ -17,6 +17,14 @@ IO
|
|
|
17
17
|
:members:
|
|
18
18
|
:undoc-members:
|
|
19
19
|
|
|
20
|
+
==========
|
|
21
|
+
Evaluation
|
|
22
|
+
==========
|
|
23
|
+
|
|
24
|
+
.. automodule:: usearch.eval
|
|
25
|
+
:members:
|
|
26
|
+
:undoc-members:
|
|
27
|
+
|
|
20
28
|
======
|
|
21
29
|
Client
|
|
22
30
|
======
|
|
@@ -25,11 +33,10 @@ Client
|
|
|
25
33
|
:members:
|
|
26
34
|
:undoc-members:
|
|
27
35
|
|
|
28
|
-
|
|
29
36
|
======
|
|
30
37
|
Server
|
|
31
38
|
======
|
|
32
39
|
|
|
33
40
|
.. automodule:: usearch.server
|
|
34
41
|
:members:
|
|
35
|
-
:undoc-members:
|
|
42
|
+
:undoc-members:
|
package/golang/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# USearch for GoLang
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```golang
|
|
6
|
+
import (
|
|
7
|
+
"github.com/unum-cloud/usearch/golang"
|
|
8
|
+
)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```golang
|
|
14
|
+
package main
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"fmt"
|
|
18
|
+
"github.com/unum-cloud/usearch/golang"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
func main() {
|
|
22
|
+
conf := usearch.DefaultConfig(128)
|
|
23
|
+
index := usearch.NewIndex(conf)
|
|
24
|
+
v := make([]float32, 128)
|
|
25
|
+
index.Add(42, v)
|
|
26
|
+
results := index.Search(v, 1)
|
|
27
|
+
}
|
|
28
|
+
```
|