usearch 0.2.4 → 0.8.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/.clang-tidy +60 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.yml +64 -0
  5. package/.github/workflows/release.yml +47 -20
  6. package/.github/workflows/update_version.sh +1 -0
  7. package/.vscode/launch.json +22 -0
  8. package/.vscode/settings.json +9 -1
  9. package/.vscode/tasks.json +4 -4
  10. package/CITATION.cff +11 -0
  11. package/CMakeLists.txt +108 -95
  12. package/CODE_OF_CONDUCT.md +128 -0
  13. package/CONTRIBUTING.md +12 -0
  14. package/Cargo.toml +1 -1
  15. package/Dockerfile +14 -0
  16. package/README.md +21 -12
  17. package/VERSION +1 -1
  18. package/binding.gyp +13 -9
  19. package/build.rs +2 -1
  20. package/cmake/config.cmake.in +15 -0
  21. package/cmake/pkg-config.pc.in +4 -0
  22. package/cmake/usearchConfigVersion.cmake.in +20 -0
  23. package/conanfile.py +1 -1
  24. package/docs/{Benchmarking.md → benchmarks.md} +11 -2
  25. package/docs/benchmarks.rst +5 -0
  26. package/docs/{Compilation.md → compilation.md} +54 -27
  27. package/docs/compilation.rst +5 -0
  28. package/docs/conf.dox +2668 -0
  29. package/docs/index.rst +7 -0
  30. package/include/usearch/usearch.hpp +833 -439
  31. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +17 -16
  32. package/javascript/lib.cpp +94 -20
  33. package/javascript/test.js +1 -1
  34. package/objc/USearchObjective.mm +47 -30
  35. package/objc/include/USearchObjective.h +18 -26
  36. package/package.json +1 -1
  37. package/pyproject.toml +3 -2
  38. package/python/lib.cpp +216 -130
  39. package/python/{bench.py → scripts/bench.py} +6 -1
  40. package/python/scripts/bench_params.py +112 -0
  41. package/python/{test.py → scripts/test.py} +32 -13
  42. package/python/usearch/client.py +100 -14
  43. package/python/usearch/server.py +104 -23
  44. package/rust/lib.cpp +20 -15
  45. package/rust/lib.hpp +5 -5
  46. package/rust/lib.rs +3 -3
  47. package/setup.py +7 -0
  48. package/src/CMakeLists.txt +107 -0
  49. package/src/bench.cpp +54 -19
  50. package/src/{advanced.hpp → punned.hpp} +235 -203
  51. package/src/test.cpp +62 -0
  52. package/swift/Index+Sugar.swift +1 -0
  53. package/swift/Test.swift +2 -1
  54. package/wolfram/lib.cpp +14 -14
@@ -4,14 +4,14 @@
4
4
 
5
5
  #include <thread>
6
6
 
7
- #include "advanced.hpp"
7
+ #include "punned.hpp"
8
8
 
9
9
  using namespace unum::usearch;
10
10
  using namespace unum;
11
11
 
12
12
  using label_t = jint;
13
13
  using distance_t = punned_distance_t;
14
- using native_index_t = auto_index_gt<label_t>;
14
+ using punned_t = punned_gt<label_t>;
15
15
  using span_t = span_gt<float>;
16
16
 
17
17
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
@@ -39,10 +39,10 @@ JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
39
39
  std::size_t accuracy_length = (*env).GetStringUTFLength(accuracy);
40
40
 
41
41
  accuracy_t accuracy = accuracy_from_name(accuracy_cstr, accuracy_length);
42
- native_index_t index = index_from_name<native_index_t>( //
42
+ punned_t index = index_from_name<punned_t>( //
43
43
  metric_cstr, metric_length, static_cast<std::size_t>(dimensions), accuracy, config);
44
44
 
45
- native_index_t* result_ptr = new native_index_t(std::move(index));
45
+ punned_t* result_ptr = new punned_t(std::move(index));
46
46
  std::memcpy(&result, &result_ptr, sizeof(jlong));
47
47
  } catch (...) {
48
48
  jclass jc = (*env).FindClass("java/lang/Error");
@@ -59,7 +59,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1save(JNIEnv* env, jclass
59
59
  char const* path_cstr{};
60
60
  try {
61
61
  path_cstr = (*env).GetStringUTFChars(path, 0);
62
- reinterpret_cast<native_index_t*>(c_ptr)->save(path_cstr);
62
+ reinterpret_cast<punned_t*>(c_ptr)->save(path_cstr);
63
63
  } catch (...) {
64
64
  jclass jc = (*env).FindClass("java/lang/Error");
65
65
  if (jc)
@@ -72,7 +72,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1load(JNIEnv* env, jclass
72
72
  char const* path_cstr{};
73
73
  try {
74
74
  path_cstr = (*env).GetStringUTFChars(path, 0);
75
- reinterpret_cast<native_index_t*>(c_ptr)->load(path_cstr);
75
+ reinterpret_cast<punned_t*>(c_ptr)->load(path_cstr);
76
76
  } catch (...) {
77
77
  jclass jc = (*env).FindClass("java/lang/Error");
78
78
  if (jc)
@@ -85,7 +85,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view(JNIEnv* env, jclass
85
85
  char const* path_cstr{};
86
86
  try {
87
87
  path_cstr = (*env).GetStringUTFChars(path, 0);
88
- reinterpret_cast<native_index_t*>(c_ptr)->view(path_cstr);
88
+ reinterpret_cast<punned_t*>(c_ptr)->view(path_cstr);
89
89
  } catch (...) {
90
90
  jclass jc = (*env).FindClass("java/lang/Error");
91
91
  if (jc)
@@ -95,28 +95,28 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view(JNIEnv* env, jclass
95
95
  }
96
96
 
97
97
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1destroy(JNIEnv*, jclass, jlong c_ptr) {
98
- delete reinterpret_cast<native_index_t*>(c_ptr);
98
+ delete reinterpret_cast<punned_t*>(c_ptr);
99
99
  }
100
100
 
101
101
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1size(JNIEnv*, jclass, jlong c_ptr) {
102
- return reinterpret_cast<native_index_t*>(c_ptr)->size();
102
+ return reinterpret_cast<punned_t*>(c_ptr)->size();
103
103
  }
104
104
 
105
105
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1connectivity(JNIEnv*, jclass, jlong c_ptr) {
106
- return reinterpret_cast<native_index_t*>(c_ptr)->connectivity();
106
+ return reinterpret_cast<punned_t*>(c_ptr)->connectivity();
107
107
  }
108
108
 
109
109
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1dimensions(JNIEnv*, jclass, jlong c_ptr) {
110
- return reinterpret_cast<native_index_t*>(c_ptr)->dimensions();
110
+ return reinterpret_cast<punned_t*>(c_ptr)->dimensions();
111
111
  }
112
112
 
113
113
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1capacity(JNIEnv*, jclass, jlong c_ptr) {
114
- return reinterpret_cast<native_index_t*>(c_ptr)->capacity();
114
+ return reinterpret_cast<punned_t*>(c_ptr)->capacity();
115
115
  }
116
116
 
117
117
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1reserve(JNIEnv* env, jclass, jlong c_ptr, jlong capacity) {
118
118
  try {
119
- return reinterpret_cast<native_index_t*>(c_ptr)->reserve(static_cast<std::size_t>(capacity));
119
+ return reinterpret_cast<punned_t*>(c_ptr)->reserve(static_cast<std::size_t>(capacity));
120
120
  } catch (...) {
121
121
  jclass jc = (*env).FindClass("java/lang/Error");
122
122
  if (jc)
@@ -132,7 +132,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1add( //
132
132
  vector_data = (*env).GetFloatArrayElements(vector, 0);
133
133
  jsize vector_dims = (*env).GetArrayLength(vector);
134
134
  span_t vector_span = span_t{vector_data, static_cast<std::size_t>(vector_dims)};
135
- reinterpret_cast<native_index_t*>(c_ptr)->add(label, vector_span);
135
+ reinterpret_cast<punned_t*>(c_ptr)->add(label, vector_span);
136
136
  } catch (...) {
137
137
  jclass jc = (*env).FindClass("java/lang/Error");
138
138
  if (jc)
@@ -159,8 +159,9 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
159
159
  vector_data = (*env).GetFloatArrayElements(vector, 0);
160
160
  jsize vector_dims = (*env).GetArrayLength(vector);
161
161
  span_t vector_span = span_t{vector_data, static_cast<std::size_t>(vector_dims)};
162
- found = reinterpret_cast<native_index_t*>(c_ptr)->search( //
163
- vector_span, static_cast<std::size_t>(wanted), matches_data, NULL);
162
+ found = reinterpret_cast<punned_t*>(c_ptr)
163
+ ->search(vector_span, static_cast<std::size_t>(wanted))
164
+ .dump_to(matches_data, NULL);
164
165
  (*env).SetIntArrayRegion(matches, 0, found, matches_data);
165
166
  } catch (...) {
166
167
  jclass jc = (*env).FindClass("java/lang/Error");
@@ -15,17 +15,19 @@
15
15
  #define NAPI_CPP_EXCEPTIONS
16
16
  #endif
17
17
 
18
+ #include <new> // `std::bad_alloc`
19
+
18
20
  #include <napi.h>
19
21
  #include <node_api.h>
20
22
 
21
- #include "advanced.hpp"
23
+ #include "punned.hpp"
22
24
 
23
25
  using namespace unum::usearch;
24
26
  using namespace unum;
25
27
 
26
28
  using label_t = std::uint32_t;
27
29
  using distance_t = punned_distance_t;
28
- using native_index_t = auto_index_gt<label_t>;
30
+ using punned_t = punned_gt<label_t>;
29
31
 
30
32
  class Index : public Napi::ObjectWrap<Index> {
31
33
  public:
@@ -38,10 +40,14 @@ class Index : public Napi::ObjectWrap<Index> {
38
40
  Napi::Value GetCapacity(Napi::CallbackInfo const& ctx);
39
41
  Napi::Value GetConnectivity(Napi::CallbackInfo const& ctx);
40
42
 
43
+ void Save(Napi::CallbackInfo const& ctx);
44
+ void Load(Napi::CallbackInfo const& ctx);
45
+ void View(Napi::CallbackInfo const& ctx);
46
+
41
47
  void Add(Napi::CallbackInfo const& ctx);
42
48
  Napi::Value Search(Napi::CallbackInfo const& ctx);
43
49
 
44
- std::unique_ptr<native_index_t> native_;
50
+ std::unique_ptr<punned_t> native_;
45
51
  };
46
52
 
47
53
  Napi::Object Index::Init(Napi::Env env, Napi::Object exports) {
@@ -54,6 +60,9 @@ Napi::Object Index::Init(Napi::Env env, Napi::Object exports) {
54
60
  InstanceMethod("connectivity", &Index::GetConnectivity),
55
61
  InstanceMethod("add", &Index::Add),
56
62
  InstanceMethod("search", &Index::Search),
63
+ InstanceMethod("save", &Index::Save),
64
+ InstanceMethod("load", &Index::Load),
65
+ InstanceMethod("view", &Index::View),
57
66
  });
58
67
 
59
68
  Napi::FunctionReference* constructor = new Napi::FunctionReference();
@@ -70,7 +79,7 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
70
79
  int length = ctx.Length();
71
80
  if (length == 0 || length >= 2 || !ctx[0].IsObject()) {
72
81
  Napi::TypeError::New(
73
- env, "Pass args as named objects: dimensions: uint, capacity: uint, metric: [ip, cos, l2_sq, haversine]")
82
+ env, "Pass args as named objects: dimensions: uint, capacity: uint, metric: [ip, cos, l2sq, haversine]")
74
83
  .ThrowAsJavaScriptException();
75
84
  return;
76
85
  }
@@ -96,7 +105,7 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
96
105
  else if (accuracy_str == "f8")
97
106
  accuracy = accuracy_t::f8_k;
98
107
  else {
99
- Napi::TypeError::New(env, "Supported metrics are: [ip, cos, l2_sq, haversine]")
108
+ Napi::TypeError::New(env, "Supported metrics are: [ip, cos, l2sq, haversine]")
100
109
  .ThrowAsJavaScriptException();
101
110
  return;
102
111
  }
@@ -105,28 +114,28 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
105
114
  // By default we use the Inner Product similarity
106
115
  if (params.Has("metric")) {
107
116
  std::string name = params.Get("metric").As<Napi::String>().Utf8Value();
108
- if (name == "l2_sq" || name == "euclidean_sq") {
117
+ if (name == "l2sq" || name == "euclidean_sq") {
109
118
  if (!dimensions) {
110
119
  Napi::TypeError::New(env, "Please define the number of dimensions").ThrowAsJavaScriptException();
111
120
  return;
112
121
  }
113
- native_.reset(new native_index_t(native_index_t::l2(dimensions, accuracy, config)));
122
+ native_.reset(new punned_t(punned_t::l2sq(dimensions, accuracy, config)));
114
123
  } else if (name == "ip" || name == "inner" || name == "dot") {
115
124
  if (!dimensions) {
116
125
  Napi::TypeError::New(env, "Please define the number of dimensions").ThrowAsJavaScriptException();
117
126
  return;
118
127
  }
119
- native_.reset(new native_index_t(native_index_t::ip(dimensions, accuracy, config)));
128
+ native_.reset(new punned_t(punned_t::ip(dimensions, accuracy, config)));
120
129
  } else if (name == "cos" || name == "angular") {
121
130
  if (!dimensions) {
122
131
  Napi::TypeError::New(env, "Please define the number of dimensions").ThrowAsJavaScriptException();
123
132
  return;
124
133
  }
125
- native_.reset(new native_index_t(native_index_t::cos(dimensions, accuracy, config)));
134
+ native_.reset(new punned_t(punned_t::cos(dimensions, accuracy, config)));
126
135
  } else if (name == "haversine") {
127
- native_.reset(new native_index_t(native_index_t::haversine(accuracy, config)));
136
+ native_.reset(new punned_t(punned_t::haversine(accuracy, config)));
128
137
  } else {
129
- Napi::TypeError::New(env, "Supported metrics are: [ip, cos, l2_sq, haversine]")
138
+ Napi::TypeError::New(env, "Supported metrics are: [ip, cos, l2sq, haversine]")
130
139
  .ThrowAsJavaScriptException();
131
140
  return;
132
141
  }
@@ -135,7 +144,7 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
135
144
  Napi::TypeError::New(env, "Please define the number of dimensions").ThrowAsJavaScriptException();
136
145
  return;
137
146
  }
138
- native_.reset(new native_index_t(native_index_t::ip(dimensions, accuracy, config)));
147
+ native_.reset(new punned_t(punned_t::ip(dimensions, accuracy, config)));
139
148
  }
140
149
  }
141
150
 
@@ -150,6 +159,57 @@ Napi::Value Index::GetCapacity(Napi::CallbackInfo const& ctx) {
150
159
  return Napi::Number::New(ctx.Env(), native_->capacity());
151
160
  }
152
161
 
162
+ void Index::Save(Napi::CallbackInfo const& ctx) {
163
+ Napi::Env env = ctx.Env();
164
+
165
+ int length = ctx.Length();
166
+ if (length == 0 || !ctx[0].IsString()) {
167
+ Napi::TypeError::New(env, "Function expects a string path argument").ThrowAsJavaScriptException();
168
+ return;
169
+ }
170
+
171
+ try {
172
+ std::string path = ctx[0].As<Napi::String>();
173
+ native_->save(path.c_str());
174
+ } catch (...) {
175
+ Napi::TypeError::New(env, "Serialization failed").ThrowAsJavaScriptException();
176
+ }
177
+ }
178
+
179
+ void Index::Load(Napi::CallbackInfo const& ctx) {
180
+ Napi::Env env = ctx.Env();
181
+
182
+ int length = ctx.Length();
183
+ if (length == 0 || !ctx[0].IsString()) {
184
+ Napi::TypeError::New(env, "Function expects a string path argument").ThrowAsJavaScriptException();
185
+ return;
186
+ }
187
+
188
+ try {
189
+ std::string path = ctx[0].As<Napi::String>();
190
+ native_->load(path.c_str());
191
+ } catch (...) {
192
+ Napi::TypeError::New(env, "Loading failed").ThrowAsJavaScriptException();
193
+ }
194
+ }
195
+
196
+ void Index::View(Napi::CallbackInfo const& ctx) {
197
+ Napi::Env env = ctx.Env();
198
+
199
+ int length = ctx.Length();
200
+ if (length == 0 || !ctx[0].IsString()) {
201
+ Napi::TypeError::New(env, "Function expects a string path argument").ThrowAsJavaScriptException();
202
+ return;
203
+ }
204
+
205
+ try {
206
+ std::string path = ctx[0].As<Napi::String>();
207
+ native_->view(path.c_str());
208
+ } catch (...) {
209
+ Napi::TypeError::New(env, "Memory-mapping failed").ThrowAsJavaScriptException();
210
+ }
211
+ }
212
+
153
213
  void Index::Add(Napi::CallbackInfo const& ctx) {
154
214
  Napi::Env env = ctx.Env();
155
215
  if (ctx.Length() < 2 || !ctx[0].IsNumber() || !ctx[1].IsTypedArray())
@@ -166,7 +226,14 @@ void Index::Add(Napi::CallbackInfo const& ctx) {
166
226
 
167
227
  if (native_->size() + 1 >= native_->capacity())
168
228
  native_->reserve(ceil2(native_->size() + 1));
169
- native_->add(label, vector);
229
+
230
+ try {
231
+ native_->add(label, vector);
232
+ } catch (std::bad_alloc const&) {
233
+ Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
234
+ } catch (...) {
235
+ Napi::TypeError::New(env, "Insertion failed").ThrowAsJavaScriptException();
236
+ }
170
237
  }
171
238
 
172
239
  Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
@@ -190,13 +257,20 @@ Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
190
257
 
191
258
  Napi::Uint32Array matches_js = Napi::Uint32Array::New(env, wanted);
192
259
  Napi::Float32Array distances_js = Napi::Float32Array::New(env, wanted);
193
- std::size_t count = native_->search(vector, wanted, matches_js.Data(), distances_js.Data());
194
-
195
- Napi::Object result_js = Napi::Object::New(env);
196
- result_js.Set("labels", matches_js);
197
- result_js.Set("distances", distances_js);
198
- result_js.Set("count", Napi::Number::New(env, count));
199
- return result_js;
260
+ try {
261
+ std::size_t count = native_->search(vector, wanted).dump_to(matches_js.Data(), distances_js.Data());
262
+ Napi::Object result_js = Napi::Object::New(env);
263
+ result_js.Set("labels", matches_js);
264
+ result_js.Set("distances", distances_js);
265
+ result_js.Set("count", Napi::Number::New(env, count));
266
+ return result_js;
267
+ } catch (std::bad_alloc const&) {
268
+ Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
269
+ return {};
270
+ } catch (...) {
271
+ Napi::TypeError::New(env, "Search failed").ThrowAsJavaScriptException();
272
+ return {};
273
+ }
200
274
  }
201
275
 
202
276
  Napi::Object InitAll(Napi::Env env, Napi::Object exports) { return Index::Init(env, exports); }
@@ -1,7 +1,7 @@
1
1
  var assert = require('assert');
2
2
  var usearch = require('bindings')('usearch');
3
3
 
4
- var index = new usearch.Index({ metric: 'l2_sq', connectivity: 16, dimensions: 2 })
4
+ var index = new usearch.Index({ metric: 'l2sq', connectivity: 16, dimensions: 2 })
5
5
  assert.equal(index.connectivity(), 16)
6
6
  assert.equal(index.dimensions(), 2)
7
7
  assert.equal(index.size(), 0)
@@ -2,15 +2,15 @@
2
2
 
3
3
  #pragma clang diagnostic push
4
4
  #pragma clang diagnostic ignored "-Wdocumentation"
5
- #import "advanced.hpp"
5
+ #import "punned.hpp"
6
6
  #pragma clang diagnostic pop
7
7
 
8
8
  using namespace unum::usearch;
9
9
  using namespace unum;
10
10
 
11
11
  using distance_t = punned_distance_t;
12
- using native_index_t = auto_index_gt<UInt32>;
13
- using shared_index_t = std::shared_ptr<native_index_t>;
12
+ using punned_t = punned_gt<UInt32>;
13
+ using shared_index_t = std::shared_ptr<punned_t>;
14
14
 
15
15
  @interface Index ()
16
16
 
@@ -59,91 +59,108 @@ using shared_index_t = std::shared_ptr<native_index_t>;
59
59
  + (instancetype)indexIP:(UInt32)dimensions connectivity:(UInt32)connectivity {
60
60
  std::size_t dims = static_cast<std::size_t>(dimensions);
61
61
  config_t config;
62
+
62
63
  config.connectivity = static_cast<std::size_t>(connectivity);
63
- shared_index_t ptr = std::make_shared<native_index_t>(native_index_t::ip(dims, accuracy_t::f32_k, config));
64
+ shared_index_t ptr = std::make_shared<punned_t>(punned_t::ip(dims, accuracy_t::f32_k, config));
64
65
  return [[Index alloc] initWithIndex:ptr];
65
66
  }
66
67
 
67
- + (instancetype)indexL2:(UInt32)dimensions connectivity:(UInt32)connectivity {
68
+ + (instancetype)indexL2sq:(UInt32)dimensions connectivity:(UInt32)connectivity {
68
69
  std::size_t dims = static_cast<std::size_t>(dimensions);
69
70
  config_t config;
71
+
70
72
  config.connectivity = static_cast<std::size_t>(connectivity);
71
- shared_index_t ptr = std::make_shared<native_index_t>(native_index_t::l2(dims, accuracy_t::f32_k, config));
73
+ shared_index_t ptr = std::make_shared<punned_t>(punned_t::l2sq(dims, accuracy_t::f32_k, config));
72
74
  return [[Index alloc] initWithIndex:ptr];
73
75
  }
74
76
 
75
77
  + (instancetype)indexHaversine:(UInt32)connectivity {
76
78
  config_t config;
79
+
77
80
  config.connectivity = static_cast<std::size_t>(connectivity);
78
- shared_index_t ptr = std::make_shared<native_index_t>(native_index_t::haversine(accuracy_t::f32_k, config));
81
+ shared_index_t ptr = std::make_shared<punned_t>(punned_t::haversine(accuracy_t::f32_k, config));
79
82
  return [[Index alloc] initWithIndex:ptr];
80
83
  }
81
84
 
82
85
  - (void)addSingle:(UInt32)label
83
- vector:(Float32 const * _Nonnull)vector {
86
+ vector:(Float32 const *_Nonnull)vector {
84
87
  _native->add(label, vector);
85
88
  }
86
89
 
87
- - (UInt32)searchSingle:(Float32 const* _Nonnull)vector
90
+ - (UInt32)searchSingle:(Float32 const *_Nonnull)vector
88
91
  count:(UInt32)wanted
89
- labels:(UInt32* _Nullable)labels
90
- distances:(Float32* _Nullable)distances {
91
- std::size_t found = _native->search(vector, static_cast<std::size_t>(wanted), labels, distances);
92
+ labels:(UInt32 *_Nullable)labels
93
+ distances:(Float32 *_Nullable)distances {
94
+ std::size_t found = _native->search(vector, static_cast<std::size_t>(wanted)).dump_to(labels, distances);
95
+
92
96
  return static_cast<UInt32>(found);
93
97
  }
94
98
 
95
99
  - (void)addPrecise:(UInt32)label
96
- vector:(Float64 const * _Nonnull)vector {
100
+ vector:(Float64 const *_Nonnull)vector {
97
101
  _native->add(label, (f64_t const *)vector);
98
102
  }
99
103
 
100
- - (UInt32)searchPrecise:(Float64 const* _Nonnull)vector
104
+ - (UInt32)searchPrecise:(Float64 const *_Nonnull)vector
101
105
  count:(UInt32)wanted
102
- labels:(UInt32* _Nullable)labels
103
- distances:(Float32* _Nullable)distances {
104
- std::size_t found = _native->search((f64_t const *)vector, static_cast<std::size_t>(wanted), labels, distances);
106
+ labels:(UInt32 *_Nullable)labels
107
+ distances:(Float32 *_Nullable)distances {
108
+ std::size_t found = _native->search((f64_t const *)vector, static_cast<std::size_t>(wanted)).dump_to(labels, distances);
109
+
105
110
  return static_cast<UInt32>(found);
106
111
  }
107
112
 
108
113
  - (void)addImprecise:(UInt32)label
109
- vector:(void const * _Nonnull)vector {
110
- _native->add(label, (f16_converted_t const *)vector);
114
+ vector:(void const *_Nonnull)vector {
115
+ _native->add(label, (f16_bits_t const *)vector);
111
116
  }
112
117
 
113
- - (UInt32)searchImprecise:(void const* _Nonnull)vector
118
+ - (UInt32)searchImprecise:(void const *_Nonnull)vector
114
119
  count:(UInt32)wanted
115
- labels:(UInt32* _Nullable)labels
116
- distances:(Float32* _Nullable)distances {
117
- std::size_t found = _native->search((f16_converted_t const *)vector, static_cast<std::size_t>(wanted), labels, distances);
120
+ labels:(UInt32 *_Nullable)labels
121
+ distances:(Float32 *_Nullable)distances {
122
+ std::size_t found = _native->search((f16_bits_t const *)vector, static_cast<std::size_t>(wanted)).dump_to(labels, distances);
123
+
118
124
  return static_cast<UInt32>(found);
119
125
  }
120
126
 
121
- - (void)clear { _native->clear(); }
127
+ - (void)clear {
128
+ _native->clear();
129
+ }
122
130
 
123
- - (void)save:(NSString*)path {
131
+ - (void)save:(NSString *)path {
124
132
  char const *path_c = [path UTF8String];
125
- if (!path_c)
133
+
134
+ if (!path_c) {
126
135
  @throw [NSException exceptionWithName:@"Can't save to disk"
127
136
  reason:@"The path must be convertible to UTF8"
128
137
  userInfo:nil];
138
+ }
139
+
129
140
  _native->save(path_c);
130
141
  }
131
142
 
132
- - (void)load:(NSString*)path {
143
+ - (void)load:(NSString *)path {
133
144
  char const *path_c = [path UTF8String];
134
- if (!path_c)
145
+
146
+ if (!path_c) {
135
147
  @throw [NSException exceptionWithName:@"Can't load from disk"
136
148
  reason:@"The path must be convertible to UTF8"
137
149
  userInfo:nil];
150
+ }
151
+
138
152
  _native->load(path_c);
139
153
  }
140
154
 
141
- - (void)view:(NSString*)path {
155
+ - (void)view:(NSString *)path {
142
156
  char const *path_c = [path UTF8String];
143
- if (!path_c)
157
+
158
+ if (!path_c) {
144
159
  @throw [NSException exceptionWithName:@"Can't view from disk"
145
160
  reason:@"The path must be convertible to UTF8"
146
161
  userInfo:nil];
162
+ }
163
+
147
164
  _native->view(path_c);
148
165
  }
149
166
 
@@ -11,7 +11,7 @@ typedef NS_ENUM(NSUInteger, Quantization) {
11
11
  };
12
12
 
13
13
  API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
14
- @interface Index: NSObject
14
+ @interface Index : NSObject
15
15
 
16
16
  @property (readonly) UInt32 dimensions;
17
17
  @property (readonly) UInt32 connectivity;
@@ -22,47 +22,39 @@ API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
22
22
  @property (readonly) UInt32 capacity;
23
23
  @property (readonly) Boolean isEmpty;
24
24
 
25
- - (instancetype)init
26
- NS_UNAVAILABLE;
25
+ - (instancetype)init NS_UNAVAILABLE;
27
26
 
28
27
  + (instancetype)indexIP:(UInt32)dimensions connectivity:(UInt32)connectivity NS_SWIFT_NAME(indexIP(dimensions:connectivity:));
29
- + (instancetype)indexL2:(UInt32)dimensions connectivity:(UInt32)connectivity NS_SWIFT_NAME(l2(dimensions:connectivity:));
28
+ + (instancetype)indexL2sq:(UInt32)dimensions connectivity:(UInt32)connectivity NS_SWIFT_NAME(l2sq(dimensions:connectivity:));
30
29
  + (instancetype)indexHaversine:(UInt32)connectivity NS_SWIFT_NAME(indexHaversine(connectivity:));
31
30
 
32
31
  - (void)addSingle:(UInt32)label
33
- vector:(Float32 const* _Nonnull)vector
34
- NS_SWIFT_NAME(addSingle(label:vector:));
32
+ vector:(Float32 const *_Nonnull)vector NS_SWIFT_NAME(addSingle(label:vector:));
35
33
 
36
- - (UInt32)searchSingle:(Float32 const* _Nonnull)vector
34
+ - (UInt32)searchSingle:(Float32 const *_Nonnull)vector
37
35
  count:(UInt32)count
38
- labels:(UInt32* _Nullable)labels
39
- distances:(Float32* _Nullable)distances
40
- NS_SWIFT_NAME(searchSingle(vector:count:labels:distances:));
36
+ labels:(UInt32 *_Nullable)labels
37
+ distances:(Float32 *_Nullable)distances NS_SWIFT_NAME(searchSingle(vector:count:labels:distances:));
41
38
 
42
39
  - (void)addPrecise:(UInt32)label
43
- vector:(Float64 const* _Nonnull)vector
44
- NS_SWIFT_NAME(addPrecise(label:vector:));
40
+ vector:(Float64 const *_Nonnull)vector NS_SWIFT_NAME(addPrecise(label:vector:));
45
41
 
46
- - (UInt32)searchPrecise:(Float64 const* _Nonnull)vector
42
+ - (UInt32)searchPrecise:(Float64 const *_Nonnull)vector
47
43
  count:(UInt32)count
48
- labels:(UInt32* _Nullable)labels
49
- distances:(Float32* _Nullable)distances
50
- NS_SWIFT_NAME(searchPrecise(vector:count:labels:distances:));
44
+ labels:(UInt32 *_Nullable)labels
45
+ distances:(Float32 *_Nullable)distances NS_SWIFT_NAME(searchPrecise(vector:count:labels:distances:));
51
46
 
52
47
  - (void)addImprecise:(UInt32)label
53
- vector:(void const* _Nonnull)vector
54
- NS_SWIFT_NAME(addImprecise(label:vector:));
48
+ vector:(void const *_Nonnull)vector NS_SWIFT_NAME(addImprecise(label:vector:));
55
49
 
56
- - (UInt32)searchImprecise:(void const* _Nonnull)vector
50
+ - (UInt32)searchImprecise:(void const *_Nonnull)vector
57
51
  count:(UInt32)count
58
- labels:(UInt32* _Nullable)labels
59
- distances:(Float32* _Nullable)distances
60
- NS_SWIFT_NAME(searchImprecise(vector:count:labels:distances:));
52
+ labels:(UInt32 *_Nullable)labels
53
+ distances:(Float32 *_Nullable)distances NS_SWIFT_NAME(searchImprecise(vector:count:labels:distances:));
61
54
 
62
-
63
- - (void)save:(NSString*)path NS_SWIFT_NAME(dump(path:));
64
- - (void)load:(NSString*)path NS_SWIFT_NAME(load(path:));
65
- - (void)view:(NSString*)path NS_SWIFT_NAME(view(path:));
55
+ - (void)save:(NSString *)path NS_SWIFT_NAME(dump(path:));
56
+ - (void)load:(NSString *)path NS_SWIFT_NAME(load(path:));
57
+ - (void)view:(NSString *)path NS_SWIFT_NAME(view(path:));
66
58
  - (void)clear NS_SWIFT_NAME(clear());
67
59
 
68
60
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "0.2.4",
3
+ "version": "0.8.0",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ashot Vardanian",
6
6
  "license": "Apache 2.0",
package/pyproject.toml CHANGED
@@ -1,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["setuptools>=42", "wheel", "cmake>=3.22", "pybind11"]
2
+ requires = ["setuptools>=42", "wheel", "cmake>=3.22", "pybind11", "numpy"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [tool.pytest.ini_options]
@@ -9,8 +9,9 @@ xfail_strict = true
9
9
  filterwarnings = ["error"]
10
10
 
11
11
  [tool.cibuildwheel]
12
+ test-requires = ["pytest", "numpy"]
13
+ test-command = "pytest {project}/python/test.py"
12
14
  build-verbosity = 0
13
-
14
15
  skip = ["*musllinux*", "*i686*", "pp*"]
15
16
 
16
17
  [tool.cibuildwheel.linux]