usearch 2.6.1 → 2.7.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.
Files changed (54) hide show
  1. package/. npmignore +14 -0
  2. package/.cmake-format.py +19 -0
  3. package/.github/workflows/Dockerfile.libs +2 -2
  4. package/.github/workflows/prerelease.yml +85 -64
  5. package/.github/workflows/release.yml +17 -15
  6. package/.gitignore +1 -1
  7. package/.vscode/launch.json +47 -14
  8. package/.vscode/settings.json +7 -0
  9. package/.vscode/tasks.json +44 -8
  10. package/CITATION.cff +1 -1
  11. package/CMakeLists.txt +218 -67
  12. package/Cargo.toml +1 -1
  13. package/README.md +1 -1
  14. package/binding.gyp +3 -2
  15. package/build.gradle +19 -10
  16. package/c/CMakeLists.txt +21 -48
  17. package/c/README.md +5 -5
  18. package/c/lib.cpp +52 -7
  19. package/c/test.c +137 -94
  20. package/c/usearch.h +42 -0
  21. package/cmake/clipp.cmake +0 -1
  22. package/cmake/jemalloc.cmake +21 -37
  23. package/conanfile.py +1 -1
  24. package/cpp/CMakeLists.txt +14 -123
  25. package/cpp/bench.cpp +0 -12
  26. package/cpp/test.cpp +29 -1
  27. package/csharp/nuget/nuget-package.props +1 -1
  28. package/docs/compilation.md +23 -16
  29. package/docs/conf.py +1 -1
  30. package/golang/README.md +52 -51
  31. package/golang/lib.go +1 -1
  32. package/golang/lib_test.go +4 -1
  33. package/include/usearch/index.hpp +3 -3
  34. package/include/usearch/index_plugins.hpp +345 -160
  35. package/javascript/lib.cpp +204 -250
  36. package/javascript/usearch.js +440 -2
  37. package/javascript/usearch.test.js +37 -0
  38. package/package.json +10 -5
  39. package/python/lib.cpp +95 -224
  40. package/python/scripts/bench_exact.py +2 -2
  41. package/python/scripts/test_jit.py +2 -13
  42. package/python/scripts/test_sparse.py +1 -1
  43. package/python/scripts/test_tooling.py +15 -5
  44. package/rust/lib.cpp +7 -4
  45. package/rust/lib.hpp +2 -0
  46. package/rust/lib.rs +128 -8
  47. package/setup.py +14 -4
  48. package/wasm/CMakeLists.txt +4 -12
  49. package/wasm/lib.cpp +1 -1
  50. package/wasmer.toml +1 -1
  51. package/javascript/docs.js +0 -111
  52. package/javascript/test.js +0 -32
  53. package/javascript/usearch.d.ts +0 -110
  54. package/tsconfig.json +0 -8
package/c/test.c CHANGED
@@ -1,4 +1,3 @@
1
- #include <stdio.h>
2
1
  #include <errno.h>
3
2
  #include <stdio.h>
4
3
  #include <stdlib.h>
@@ -6,24 +5,30 @@
6
5
 
7
6
  #include "usearch.h"
8
7
 
9
- #define ASSERT(must_be_true, message) \
10
- if (!(must_be_true)) { \
11
- printf("Assert: %s\n", message); \
12
- exit(-1); \
8
+ #define ASSERT(must_be_true, message) \
9
+ if (!(must_be_true)) { \
10
+ printf("Assert: %s\n", message); \
11
+ exit(-1); \
13
12
  }
14
13
 
15
-
16
- float* create_vectors(size_t count, size_t dimensions) {
14
+ /**
15
+ * @brief Creates and initializes vectors with random float values.
16
+ *
17
+ * @param count The number of vectors.
18
+ * @param dimensions The number of dimensions per vector.
19
+ * @return A pointer to the first element of the vectors, that must be @b free-ed afterwards.
20
+ */
21
+ float* create_vectors(size_t const count, size_t const dimensions) {
17
22
  float* data = (float*)malloc(count * dimensions * sizeof(float));
18
23
  ASSERT(data, "Failed to allocate memory");
19
- for (size_t idx = 0; idx < count * dimensions; ++idx)
20
- data[idx] = (float)rand() / (float)RAND_MAX;
24
+ for (size_t index = 0; index < count * dimensions; ++index)
25
+ data[index] = (float)rand() / (float)RAND_MAX;
21
26
  return data;
22
27
  }
23
28
 
24
- usearch_init_options_t create_options(size_t dimensions) {
29
+ usearch_init_options_t create_options(size_t const dimensions) {
25
30
  usearch_init_options_t opts;
26
- opts.connectivity = 2; // 32 in faiss
31
+ opts.connectivity = 3; // 32 in faiss
27
32
  opts.dimensions = dimensions;
28
33
  opts.expansion_add = 40; // 40 in faiss
29
34
  opts.expansion_search = 16; // 10 in faiss
@@ -33,265 +38,303 @@ usearch_init_options_t create_options(size_t dimensions) {
33
38
  return opts;
34
39
  }
35
40
 
36
- void test_init(size_t collection_size, size_t dimensions) {
41
+ /**
42
+ * This test is designed to verify the initialization of the index with specific dimensions and ensures that the
43
+ * associated properties are set correctly. It initializes the index twice, checking for errors at each step, and
44
+ * performs a reserve operation to pre-allocate space in the index, verifying the correct settings of size, capacity,
45
+ * dimensions, and connectivity after each operation.
46
+ */
47
+ void test_init(size_t const collection_size, size_t const dimensions) {
37
48
  printf("Test: Index Initialization...\n");
38
49
 
39
50
  // Init index
40
51
  usearch_error_t error = NULL;
41
52
  usearch_init_options_t opts = create_options(dimensions);
42
- usearch_index_t idx = usearch_init(&opts, &error);
53
+ usearch_index_t index = usearch_init(&opts, &error);
43
54
  ASSERT(!error, error);
44
- usearch_free(idx, &error);
55
+ usearch_free(index, &error);
45
56
  ASSERT(!error, error);
46
57
 
47
58
  // Init second time
48
- idx = usearch_init(&opts, &error);
59
+ index = usearch_init(&opts, &error);
49
60
  ASSERT(!error, error);
50
61
 
51
- ASSERT(usearch_size(idx, &error) == 0, error);
52
- ASSERT(usearch_capacity(idx, &error) == 0, error);
53
- ASSERT(usearch_dimensions(idx, &error) == dimensions, error);
54
- ASSERT(usearch_connectivity(idx, &error) == opts.connectivity, error);
62
+ ASSERT(usearch_size(index, &error) == 0, error);
63
+ ASSERT(usearch_capacity(index, &error) == 0, error);
64
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
65
+ ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
55
66
 
56
67
  // Reserve
57
- usearch_reserve(idx, collection_size, &error);
68
+ usearch_reserve(index, collection_size, &error);
58
69
  ASSERT(!error, error);
59
- ASSERT(usearch_size(idx, &error) == 0, error);
60
- ASSERT(usearch_capacity(idx, &error) == collection_size, error);
61
- ASSERT(usearch_dimensions(idx, &error) == dimensions, error);
62
- ASSERT(usearch_connectivity(idx, &error) == opts.connectivity, error);
70
+ ASSERT(usearch_size(index, &error) == 0, error);
71
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
72
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
73
+ ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
63
74
 
64
- usearch_free(idx, &error);
75
+ usearch_free(index, &error);
65
76
  ASSERT(!error, error);
66
77
 
67
78
  printf("Test: Index Initialization - PASSED\n");
68
79
  }
69
80
 
70
- void test_add_vector(size_t collection_size, size_t dimensions) {
81
+ /**
82
+ * This test validates the addition of vectors to the index. It initializes the index and reserves space for vectors.
83
+ * It then iteratively adds vectors to the index and checks if the index contains the added vectors by verifying the
84
+ * size, capacity, and presence of each vector in the index.
85
+ */
86
+ void test_add_vector(size_t const collection_size, size_t const dimensions) {
71
87
  printf("Test: Add Vector...\n");
72
88
 
73
89
  usearch_error_t error = NULL;
74
90
  usearch_init_options_t opts = create_options(dimensions);
75
- usearch_index_t idx = usearch_init(&opts, &error);
76
- usearch_reserve(idx, collection_size, &error);
91
+ usearch_index_t index = usearch_init(&opts, &error);
92
+ usearch_reserve(index, collection_size, &error);
77
93
 
78
94
  // Add vectors
79
95
  float* data = create_vectors(collection_size, dimensions);
80
96
  for (size_t i = 0; i < collection_size; ++i) {
81
97
  usearch_key_t key = i;
82
- usearch_add(idx, key, data + i * dimensions, usearch_scalar_f32_k, &error);
98
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
83
99
  ASSERT(!error, error);
84
100
  }
85
101
 
86
- ASSERT(usearch_size(idx, &error) == collection_size, error);
87
- ASSERT(usearch_capacity(idx, &error) == collection_size, error);
102
+ ASSERT(usearch_size(index, &error) == collection_size, error);
103
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
88
104
 
89
105
  // Check vectors in the index
90
106
  for (size_t i = 0; i < collection_size; ++i) {
91
107
  usearch_key_t key = i;
92
- ASSERT(usearch_contains(idx, key, &error), error);
108
+ ASSERT(usearch_contains(index, key, &error), error);
93
109
  }
94
- ASSERT(!usearch_contains(idx, -1, &error), error); // Non existing key
110
+ ASSERT(!usearch_contains(index, -1, &error), error); // Non existing key
95
111
 
96
112
  free(data);
97
- usearch_free(idx, &error);
113
+ usearch_free(index, &error);
98
114
  printf("Test: Add Vector - PASSED\n");
99
115
  }
100
116
 
101
- void test_find_vector(size_t collection_size, size_t dimensions) {
117
+ /**
118
+ * This test ensures that vectors added to the index can be correctly found. It initializes the index, reserves space,
119
+ * and adds vectors. It then performs a search query for each added vector to ensure that the vectors are correctly
120
+ * found in the index, validating the count of found vectors.
121
+ */
122
+ void test_find_vector(size_t const collection_size, size_t const dimensions) {
102
123
  printf("Test: Find Vector...\n");
103
124
 
104
125
  usearch_error_t error = NULL;
105
126
  usearch_init_options_t opts = create_options(dimensions);
106
- usearch_index_t idx = usearch_init(&opts, &error);
107
- usearch_reserve(idx, collection_size, &error);
127
+ usearch_index_t index = usearch_init(&opts, &error);
128
+ usearch_reserve(index, collection_size, &error);
108
129
 
109
130
  // Create result buffers
110
- int results_count = collection_size;
111
- usearch_key_t* keys = (usearch_key_t*)malloc(results_count * sizeof(usearch_key_t));
112
- float* distances = (float*)malloc(results_count * sizeof(float));
131
+ usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
132
+ float* distances = (float*)malloc(collection_size * sizeof(float));
113
133
  ASSERT(keys && distances, "Failed to allocate memory");
114
134
 
115
135
  // Add vectors
116
136
  float* data = create_vectors(collection_size, dimensions);
117
137
  for (size_t i = 0; i < collection_size; ++i) {
118
138
  usearch_key_t key = i;
119
- usearch_add(idx, key, data + i * dimensions, usearch_scalar_f32_k, &error);
139
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
120
140
  ASSERT(!error, error);
121
141
  }
122
142
 
123
143
  // Find the vectors
124
144
  for (size_t i = 0; i < collection_size; i++) {
125
- const void *query_vector = data + i * dimensions;
126
- size_t found_count = usearch_search(idx, query_vector, usearch_scalar_f32_k, results_count, keys, distances, &error);
145
+ size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size, keys,
146
+ distances, &error);
127
147
  ASSERT(!error, error);
128
- ASSERT(found_count = results_count, "Vector is missing");
148
+ ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
129
149
  }
130
150
 
131
151
  free(data);
132
152
  free(keys);
133
153
  free(distances);
134
- usearch_free(idx, &error);
154
+ usearch_free(index, &error);
135
155
  printf("Test: Find Vector - PASSED\n");
136
156
  }
137
157
 
138
- void test_get_vector(size_t collection_size, size_t dimensions) {
158
+ /**
159
+ * This test checks the ability of the index to handle multiple vectors associated with the same key. It initializes
160
+ * the index with the multi-option enabled, reserves space, and adds multiple vectors with the same key. The test then
161
+ * retrieves vectors associated with the key from the index and checks the count of retrieved vectors.
162
+ */
163
+ void test_get_vector(size_t const collection_size, size_t const dimensions) {
139
164
  printf("Test: Get Vector...\n");
140
165
 
141
166
  usearch_error_t error = NULL;
142
167
  usearch_init_options_t opts = create_options(dimensions);
143
168
  opts.multi = true;
144
- usearch_index_t idx = usearch_init(&opts, &error);
145
- usearch_reserve(idx, collection_size, &error);
169
+ usearch_index_t index = usearch_init(&opts, &error);
170
+ usearch_reserve(index, collection_size, &error);
146
171
 
147
172
  // Create result buffers
148
- int results_count = collection_size;
149
- float* vectors = (float*)malloc(results_count * dimensions * sizeof(float));
173
+ float* vectors = (float*)malloc(collection_size * dimensions * sizeof(float));
150
174
  ASSERT(vectors, "Failed to allocate memory");
151
175
 
152
176
  // Add multiple vectors with SAME key
153
177
  usearch_key_t const key = 1;
154
178
  float* data = create_vectors(collection_size, dimensions);
155
179
  for (size_t i = 0; i < collection_size; i++) {
156
- usearch_add(idx, key, data + i * dimensions, usearch_scalar_f32_k, &error);
180
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
157
181
  ASSERT(!error, error);
158
182
  }
159
183
 
160
184
  // Retrieve vectors from index
161
- size_t count = usearch_get(idx, key, collection_size, vectors, usearch_scalar_f32_k, &error);
162
- ASSERT(count == results_count, "Vector is missing");
185
+ size_t found_count = usearch_get(index, key, collection_size, vectors, usearch_scalar_f32_k, &error);
186
+ ASSERT(found_count == collection_size, "Vector is missing");
163
187
 
164
188
  free(vectors);
165
189
  free(data);
166
- usearch_free(idx, &error);
190
+ usearch_free(index, &error);
167
191
 
168
192
  printf("Test: Get Vector - PASSED\n");
169
193
  }
170
194
 
171
- void test_remove_vector(size_t collection_size, size_t dimensions) {
195
+ /**
196
+ * This test ensures that vectors can be successfully removed from the index. It initializes the index, reserves space,
197
+ * and adds vectors. It then iteratively removes each vector from the index and checks for errors. However, note that
198
+ * the assert in this test expects an error, indicating that the remove functionality is not currently supported.
199
+ */
200
+ void test_remove_vector(size_t const collection_size, size_t const dimensions) {
172
201
  printf("Test: Remove Vector...\n");
173
202
 
174
203
  usearch_error_t error = NULL;
175
204
  usearch_init_options_t opts = create_options(dimensions);
176
- usearch_index_t idx = usearch_init(&opts, &error);
177
- usearch_reserve(idx, collection_size, &error);
205
+ usearch_index_t index = usearch_init(&opts, &error);
206
+ usearch_reserve(index, collection_size, &error);
178
207
 
179
208
  // Add vectors
180
209
  float* data = create_vectors(collection_size, dimensions);
181
210
  for (size_t i = 0; i < collection_size; ++i) {
182
211
  usearch_key_t key = i;
183
- usearch_add(idx, key, data + i * dimensions, usearch_scalar_f32_k, &error);
212
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
184
213
  ASSERT(!error, error);
185
214
  }
186
215
 
187
216
  // Remove the vectors
188
217
  for (size_t i = 0; i < collection_size; i++) {
189
218
  usearch_key_t key = i;
190
- usearch_remove(idx, key, &error);
219
+ usearch_remove(index, key, &error);
191
220
  ASSERT(!error, "Currently, Remove is not supported");
192
221
  }
193
222
 
194
223
  free(data);
195
- usearch_free(idx, &error);
224
+ usearch_free(index, &error);
196
225
  printf("Test: Remove Vector - PASSED\n");
197
226
  }
198
227
 
199
- void test_save_load(size_t collection_size, size_t dimensions) {
228
+ /**
229
+ * This test validates the save and load functionality of the index. It initializes the index, reserves space, and adds
230
+ * vectors. The index is then saved to a file and freed. A new index is initialized, and the previously saved index is
231
+ * loaded into it. The test then validates the loaded index properties and ensures that it contains all the vectors
232
+ * from the saved index.
233
+ */
234
+ void test_save_load(size_t const collection_size, size_t const dimensions) {
200
235
  printf("Test: Save/Load...\n");
201
236
 
202
237
  usearch_error_t error = NULL;
203
238
  usearch_init_options_t opts = create_options(dimensions);
204
- usearch_index_t idx = usearch_init(&opts, &error);
205
- usearch_reserve(idx, collection_size, &error);
239
+ usearch_index_t index = usearch_init(&opts, &error);
240
+ usearch_reserve(index, collection_size, &error);
206
241
 
207
242
  // Add vectors
208
243
  float* data = create_vectors(collection_size, dimensions);
209
244
  for (size_t i = 0; i < collection_size; ++i) {
210
245
  usearch_key_t key = i;
211
- usearch_add(idx, key, data + i * dimensions, usearch_scalar_f32_k, &error);
246
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
212
247
  ASSERT(!error, error);
213
248
  }
214
249
 
215
250
  // Save and free the index
216
- usearch_save(idx, "usearch_index.bin", &error);
251
+ usearch_save(index, "usearch_index.bin", &error);
217
252
  ASSERT(!error, error);
218
- usearch_free(idx, &error);
253
+ usearch_free(index, &error);
219
254
  ASSERT(!error, error);
220
255
 
221
256
  // Reinit
222
- idx = usearch_init(&opts, &error);
257
+ index = usearch_init(&opts, &error);
223
258
  ASSERT(!error, error);
224
- ASSERT(usearch_size(idx, &error) == 0, error);
259
+ ASSERT(usearch_size(index, &error) == 0, error);
225
260
 
226
261
  // Load
227
- usearch_load(idx, "usearch_index.bin", &error);
262
+ usearch_load(index, "usearch_index.bin", &error);
228
263
  ASSERT(!error, error);
229
- ASSERT(usearch_size(idx, &error) == collection_size, error);
230
- ASSERT(usearch_capacity(idx, &error) == collection_size, error);
231
- ASSERT(usearch_dimensions(idx, &error) == dimensions, error);
232
- ASSERT(usearch_connectivity(idx, &error) == opts.connectivity, error);
264
+ ASSERT(usearch_size(index, &error) == collection_size, error);
265
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
266
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
267
+ ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
233
268
 
234
269
  // Check vectors in the index
235
270
  for (size_t i = 0; i < collection_size; ++i) {
236
271
  usearch_key_t key = i;
237
- ASSERT(usearch_contains(idx, key, &error), error);
272
+ ASSERT(usearch_contains(index, key, &error), error);
238
273
  }
239
274
 
240
275
  free(data);
241
- usearch_free(idx, &error);
276
+ usearch_free(index, &error);
242
277
  printf("Test: Save/Load - PASSED\n");
243
278
  }
244
279
 
245
- void test_view(size_t collection_size, size_t dimensions) {
280
+ /**
281
+ * This test is designed to validate the view functionality of the index. It initializes the index, reserves space, and
282
+ * adds vectors. The index is then saved to a file and freed. A new index is initialized and a view is created from the
283
+ * saved index file. The test is mainly focused on ensuring that no errors occur during these operations, but it does
284
+ * not verify the properties or contents of the viewed index.
285
+ */
286
+ void test_view(size_t const collection_size, size_t const dimensions) {
246
287
  printf("Test: View...\n");
247
288
 
248
289
  usearch_error_t error = NULL;
249
290
  usearch_init_options_t opts = create_options(dimensions);
250
- usearch_index_t idx = usearch_init(&opts, &error);
251
- usearch_reserve(idx, collection_size, &error);
291
+ usearch_index_t index = usearch_init(&opts, &error);
292
+ usearch_reserve(index, collection_size, &error);
252
293
 
253
294
  // Add vectors
254
295
  float* data = create_vectors(collection_size, dimensions);
255
296
  for (size_t i = 0; i < collection_size; ++i) {
256
297
  usearch_key_t key = i;
257
- usearch_add(idx, key, data + i * dimensions, usearch_scalar_f32_k, &error);
298
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
258
299
  ASSERT(!error, error);
259
300
  }
260
301
 
261
302
  // Save and free the index
262
- usearch_save(idx, "usearch_index.bin", &error);
303
+ usearch_save(index, "usearch_index.bin", &error);
263
304
  ASSERT(!error, error);
264
- usearch_free(idx, &error);
305
+ usearch_free(index, &error);
265
306
  ASSERT(!error, error);
266
307
 
267
308
  // Reinit
268
- idx = usearch_init(&opts, &error);
309
+ index = usearch_init(&opts, &error);
269
310
  ASSERT(!error, error);
270
311
 
271
312
  // View
272
- usearch_view(idx, "usearch_index.bin", &error);
313
+ usearch_view(index, "usearch_index.bin", &error);
273
314
  ASSERT(!error, error);
274
315
 
275
316
  free(data);
276
- usearch_free(idx, &error);
317
+ usearch_free(index, &error);
277
318
  printf("Test: View - PASSED\n");
278
319
  }
279
320
 
280
- int main() {
321
+ int main(int argc, char const* argv[]) {
281
322
 
282
323
  size_t collection_sizes[] = {11, 512};
283
324
  size_t dimensions[] = {83, 1};
284
- for (size_t idx = 0; idx < sizeof(collection_sizes) / sizeof(collection_sizes[0]); ++idx) {
325
+ for (size_t index = 0; index < sizeof(collection_sizes) / sizeof(collection_sizes[0]); ++index) {
285
326
  for (size_t jdx = 0; jdx < sizeof(dimensions) / sizeof(dimensions[0]); ++jdx) {
286
- test_init(collection_sizes[idx], dimensions[jdx]);
287
- test_add_vector(collection_sizes[idx], dimensions[jdx]);
288
- test_find_vector(collection_sizes[idx], dimensions[jdx]);
289
- test_get_vector(collection_sizes[idx], dimensions[jdx]);
290
- test_remove_vector(collection_sizes[idx], dimensions[jdx]);
291
- test_save_load(collection_sizes[idx], dimensions[jdx]);
292
- test_view(collection_sizes[idx], dimensions[jdx]);
327
+ test_init(collection_sizes[index], dimensions[jdx]);
328
+ test_add_vector(collection_sizes[index], dimensions[jdx]);
329
+ test_find_vector(collection_sizes[index], dimensions[jdx]);
330
+ test_get_vector(collection_sizes[index], dimensions[jdx]);
331
+ test_remove_vector(collection_sizes[index], dimensions[jdx]);
332
+ test_save_load(collection_sizes[index], dimensions[jdx]);
333
+ test_view(collection_sizes[index], dimensions[jdx]);
293
334
  }
294
335
  }
295
336
 
337
+ (void)argc;
338
+ (void)argv;
296
339
  return 0;
297
- }
340
+ }
package/c/usearch.h CHANGED
@@ -241,6 +241,48 @@ USEARCH_EXPORT size_t usearch_remove(usearch_index_t, usearch_key_t key, usearch
241
241
  */
242
242
  USEARCH_EXPORT size_t usearch_rename(usearch_index_t, usearch_key_t from, usearch_key_t to, usearch_error_t* error);
243
243
 
244
+ /**
245
+ * @brief Computes the distance between two equi-dimensional vectors.
246
+ * @param[in] vector_first The first vector for comparison.
247
+ * @param[in] vector_second The second vector for comparison.
248
+ * @param[in] scalar_kind The scalar type used in the vectors.
249
+ * @param[in] dimensions The number of dimensions in each vector.
250
+ * @param[in] metric_kind The metric kind used for distance calculation between vectors.
251
+ * @param[out] error Pointer to a string where the error message will be stored, if an error occurs.
252
+ * @return Distance between given vectors.
253
+ */
254
+ USEARCH_EXPORT usearch_distance_t usearch_distance( //
255
+ void const* vector_first, void const* vector_second, //
256
+ usearch_scalar_kind_t scalar_kind, size_t dimensions, //
257
+ usearch_metric_kind_t metric_kind, usearch_error_t* error);
258
+
259
+ /**
260
+ * @brief Multi-threaded exact nearest neighbors search for equi-dimensional vectors.
261
+ * @param[in] dataset Pointer to the first scalar of the dataset matrix.
262
+ * @param[in] queries Pointer to the first scalar of the queries matrix.
263
+ * @param[in] dataset_size Number of vectors in the `dataset`.
264
+ * @param[in] queries_size Number of vectors in the `queries` set.
265
+ * @param[in] dataset_stride Number of bytes between starts of consecutive vectors in `dataset`.
266
+ * @param[in] queries_stride Number of bytes between starts of consecutive vectors in `queries`.
267
+ * @param[in] scalar_kind The scalar type used in the vectors.
268
+ * @param[in] dimensions The number of dimensions in each vector.
269
+ * @param[in] metric_kind The metric kind used for distance calculation between vectors.
270
+ * @param[in] count Upper bound on the number of neighbors to search, the "k" in "kANN".
271
+ * @param[in] threads Upper bound for the number of CPU threads to use.
272
+ * @param[out] keys Output buffer for up to `count` nearest neighbors keys.
273
+ * @param[out] distances Output buffer for up to `count` distances to nearest neighbors.
274
+ * @param[out] error Pointer to a string where the error message will be stored, if an error occurs.
275
+ * @return Number of found matches.
276
+ */
277
+ USEARCH_EXPORT void usearch_exact_search( //
278
+ void const* dataset, size_t dataset_size, size_t dataset_stride, //
279
+ void const* queries, size_t queries_size, size_t queries_stride, //
280
+ usearch_scalar_kind_t scalar_kind, size_t dimensions, //
281
+ usearch_metric_kind_t metric_kind, size_t count, size_t threads, //
282
+ usearch_key_t* keys, size_t keys_stride, //
283
+ usearch_distance_t* distances, size_t distances_stride, //
284
+ usearch_error_t* error);
285
+
244
286
  #ifdef __cplusplus
245
287
  }
246
288
  #endif
package/cmake/clipp.cmake CHANGED
@@ -5,4 +5,3 @@ FetchContent_Declare(
5
5
  GIT_TAG v1.2.3
6
6
  )
7
7
  FetchContent_MakeAvailable(clipp)
8
- include_directories(${clipp_SOURCE_DIR}/include)
@@ -1,44 +1,35 @@
1
-
2
1
  include(ExternalProject)
3
2
  set(JEMALLOC_DISABLE_TLS)
4
3
 
5
- if(${USTORE_BUILD_SDK_PYTHON})
4
+ if (${USTORE_BUILD_SDK_PYTHON})
6
5
  set(JEMALLOC_DISABLE_TLS --disable-initial-exec-tls)
7
- endif()
6
+ endif ()
8
7
 
9
- if(${USTORE_REBUILD_JEMALLOC})
8
+ if (${USTORE_REBUILD_JEMALLOC})
10
9
  set(JEMALLOC_PREFIX_DIR ${CMAKE_BINARY_DIR}/_deps/jemalloc)
11
10
  set(JEMALLOC_SRC_DIR ${JEMALLOC_PREFIX_DIR}/src/jemalloc)
12
11
  set(JEMALLOC_INSTALL_DIR ${JEMALLOC_PREFIX_DIR}/install)
13
12
  set(JEMALLOC_DEPENDENCY jemalloc)
14
13
 
15
- ExternalProject_Add(jemalloc
14
+ ExternalProject_Add(
15
+ jemalloc
16
16
  GIT_REPOSITORY https://github.com/jemalloc/jemalloc.git
17
17
  GIT_TAG 5.3.0
18
18
  PREFIX ${JEMALLOC_PREFIX_DIR}
19
- CONFIGURE_COMMAND echo Configuring jemalloc
20
- && cd ${JEMALLOC_SRC_DIR}
21
- && ./autogen.sh && ./configure
22
- --prefix=${JEMALLOC_INSTALL_DIR} --enable-prof ${JEMALLOC_DISABLE_TLS} # --with-jemalloc-prefix=je_
23
- BUILD_COMMAND echo Building jemalloc && cd ${JEMALLOC_SRC_DIR}
24
- && make install_lib_static install_include
19
+ CONFIGURE_COMMAND
20
+ echo Configuring jemalloc && cd ${JEMALLOC_SRC_DIR} && ./autogen.sh && ./configure
21
+ --prefix=${JEMALLOC_INSTALL_DIR} --enable-prof ${JEMALLOC_DISABLE_TLS} # --with-jemalloc-prefix=je_
22
+ BUILD_COMMAND echo Building jemalloc && cd ${JEMALLOC_SRC_DIR} && make install_lib_static install_include
25
23
  INSTALL_COMMAND ""
26
24
  UPDATE_COMMAND ""
27
25
  )
28
26
 
29
- # Create libjemalloc and libjemalloc_pic targets to be used as
30
- # dependencies
27
+ # Create libjemalloc and libjemalloc_pic targets to be used as dependencies
31
28
  add_library(libjemalloc STATIC IMPORTED GLOBAL)
32
29
  add_library(libjemalloc_pic STATIC IMPORTED GLOBAL)
33
30
 
34
- set_property(TARGET libjemalloc
35
- PROPERTY IMPORTED_LOCATION
36
- ${JEMALLOC_INSTALL_DIR}/lib/libjemalloc.a
37
- )
38
- set_property(TARGET libjemalloc_pic
39
- PROPERTY IMPORTED_LOCATION
40
- ${JEMALLOC_INSTALL_DIR}/lib/libjemalloc_pic.a
41
- )
31
+ set_property(TARGET libjemalloc PROPERTY IMPORTED_LOCATION ${JEMALLOC_INSTALL_DIR}/lib/libjemalloc.a)
32
+ set_property(TARGET libjemalloc_pic PROPERTY IMPORTED_LOCATION ${JEMALLOC_INSTALL_DIR}/lib/libjemalloc_pic.a)
42
33
 
43
34
  link_libraries(-Wl,--no-as-needed)
44
35
  link_libraries(dl ${libjemalloc_pic})
@@ -49,30 +40,23 @@ if(${USTORE_REBUILD_JEMALLOC})
49
40
 
50
41
  include_directories(${JEMALLOC_SOURCE_DIR}/include)
51
42
 
52
- else()
53
- find_path(JEMALLOC_ROOT_DIR
54
- NAMES include/jemalloc/jemalloc.h
55
- )
43
+ else ()
44
+ find_path(JEMALLOC_ROOT_DIR NAMES include/jemalloc/jemalloc.h)
56
45
 
57
- find_library(JEMALLOC_LIBRARIES
46
+ find_library(
47
+ JEMALLOC_LIBRARIES
58
48
  NAMES jemalloc
59
49
  HINTS ${JEMALLOC_ROOT_DIR}/lib
60
50
  )
61
51
 
62
- find_path(JEMALLOC_INCLUDE_DIR
52
+ find_path(
53
+ JEMALLOC_INCLUDE_DIR
63
54
  NAMES jemalloc/jemalloc.h
64
55
  HINTS ${JEMALLOC_ROOT_DIR}/include
65
56
  )
66
57
 
67
58
  include(FindPackageHandleStandardArgs)
68
- find_package_handle_standard_args(JeMalloc DEFAULT_MSG
69
- JEMALLOC_LIBRARIES
70
- JEMALLOC_INCLUDE_DIR
71
- )
59
+ find_package_handle_standard_args(JeMalloc DEFAULT_MSG JEMALLOC_LIBRARIES JEMALLOC_INCLUDE_DIR)
72
60
 
73
- mark_as_advanced(
74
- JEMALLOC_ROOT_DIR
75
- JEMALLOC_LIBRARIES
76
- JEMALLOC_INCLUDE_DIR
77
- )
78
- endif()
61
+ mark_as_advanced(JEMALLOC_ROOT_DIR JEMALLOC_LIBRARIES JEMALLOC_INCLUDE_DIR)
62
+ endif ()
package/conanfile.py CHANGED
@@ -7,7 +7,7 @@ required_conan_version = ">=1.53.0"
7
7
  class USearchConan(ConanFile):
8
8
 
9
9
  name = "usearch"
10
- version = '2.6.1'
10
+ version = '2.7.0'
11
11
  license = "Apache-2.0"
12
12
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
13
13
  homepage = "https://github.com/unum-cloud/usearch"