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/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
  }
@@ -0,0 +1,8 @@
1
+ include(FetchContent)
2
+ FetchContent_Declare(
3
+ emscripten
4
+ GIT_REPOSITORY https://github.com/emscripten-core/emscripten
5
+ GIT_TAG main
6
+ )
7
+ FetchContent_MakeAvailable(emscripten)
8
+ include_directories(${emscripten_SOURCE_DIR}/include)
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.21.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
- #include <execinfo.h>
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
- #include <omp.h> // `omp_set_num_threads()`
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 (omp_get_thread_num() == 0)
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 (omp_get_thread_num() == 0)
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
- #else
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
- #else
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>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
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>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
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>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
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>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
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<std::size_t, neighbor_id_at>>(dataset, args, config, limits);
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>, std::size_t, std::uint32_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.elements = dataset.vectors_count();
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 vec[3] = {10, 20, 15};
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{&vec[0], 3ul});
91
+ index.add(42, view_t{&vec42[0], 3ul});
91
92
 
92
93
  // Reconstruct
93
- scalar_t vec_reconstructed[3] = {0, 0, 0};
94
- index.get(42, span_t{&vec_reconstructed[0], 3ul});
95
- expect(vec_reconstructed[0] == vec[0]);
96
- expect(vec_reconstructed[1] == vec[1]);
97
- expect(vec_reconstructed[2] == vec[2]);
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) {
@@ -0,0 +1,6 @@
1
+ ==============
2
+ GoLang SDK
3
+ ==============
4
+
5
+
6
+ .. mdinclude:: ../golang/README.md
package/docs/index.rst CHANGED
@@ -14,11 +14,14 @@ Overview
14
14
  :hidden:
15
15
  :caption: �
16
16
 
17
- c/index
18
17
  cpp/index
19
- java/index
20
18
  python/index
19
+ java/index
21
20
  javascript/index
21
+ rust/index
22
+ swift/index
23
+ c/index
24
+ golang/index
22
25
 
23
26
  .. toctree::
24
27
  :hidden:
@@ -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:
@@ -0,0 +1,6 @@
1
+ ==============
2
+ Rust SDK
3
+ ==============
4
+
5
+
6
+ .. mdinclude:: ../rust/README.md
@@ -0,0 +1,6 @@
1
+ ==============
2
+ Swift SDK
3
+ ==============
4
+
5
+
6
+ .. mdinclude:: ../swift/README.md
@@ -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
+ ```