usearch 0.19.1 → 0.19.3

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/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "0.19.1"
3
+ version = "0.19.3"
4
4
  authors = ["Ashot Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/c/lib.cpp CHANGED
@@ -15,8 +15,7 @@ using index_t = index_punned_dense_gt<label_t>;
15
15
  using add_result_t = index_t::add_result_t;
16
16
  using search_result_t = index_t::search_result_t;
17
17
  using serialization_result_t = index_t::serialization_result_t;
18
-
19
- using span_t = span_gt<float>;
18
+ using vector_view_t = span_gt<float>;
20
19
 
21
20
  // helper functions that are not part of the C ABI
22
21
  metric_kind_t to_native_metric(usearch_metric_kind_t kind) {
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.19.1'
7
+ version = '0.19.3'
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/bench.cpp CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @brief A benchmark for the construction speed of the USearch index
3
- * and the resulting accuracy (recall) of the Approximate Nearest Neighbors
4
- * Search queries.
2
+ * @brief A benchmark for the construction speed of the USearch index
3
+ * and the resulting accuracy (recall) of the Approximate Nearest Neighbors
4
+ * Search queries.
5
5
  */
6
6
 
7
7
  #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
@@ -48,8 +48,11 @@
48
48
  // OS-specific includes
49
49
  #if defined(USEARCH_DEFINED_WINDOWS)
50
50
  #define _USE_MATH_DEFINES
51
+ #define NOMINMAX
51
52
  #include <Windows.h>
52
53
  #include <sys/stat.h> // `fstat` for file size
54
+ #undef NOMINMAX
55
+ #undef _USE_MATH_DEFINES
53
56
  #else
54
57
  #include <fcntl.h> // `fallocate`
55
58
  #include <stdlib.h> // `posix_memalign`
@@ -1305,6 +1308,7 @@ class index_gt {
1305
1308
  using id_t = id_at;
1306
1309
  using allocator_t = allocator_at;
1307
1310
  using point_allocator_t = point_allocator_at;
1311
+ static_assert(sizeof(label_t) >= sizeof(id_t), "Having tiny labels doesn't make sense.");
1308
1312
 
1309
1313
  using scalar_t = typename metric_t::scalar_t;
1310
1314
  using vector_view_t = span_gt<scalar_t const>;
@@ -652,7 +652,7 @@ class index_punned_dense_gt {
652
652
  }
653
653
  };
654
654
 
655
- using punned_small_t = index_punned_dense_gt<std::int64_t, std::uint32_t>;
655
+ using punned_small_t = index_punned_dense_gt<std::uint64_t, std::uint32_t>;
656
656
  using punned_big_t = index_punned_dense_gt<uuid_t, uint40_t>;
657
657
 
658
658
  } // namespace usearch
@@ -9,14 +9,16 @@
9
9
  using namespace unum::usearch;
10
10
  using namespace unum;
11
11
 
12
- using label_t = jint;
13
- using span_t = span_gt<float>;
12
+ using metric_t = index_punned_dense_metric_t;
14
13
  using distance_t = punned_distance_t;
15
- using punned_t = index_punned_dense_gt<label_t>;
14
+ using index_t = punned_small_t;
16
15
 
17
- using add_result_t = typename punned_t::add_result_t;
18
- using search_result_t = typename punned_t::search_result_t;
19
- using serialization_result_t = typename punned_t::serialization_result_t;
16
+ using add_result_t = typename index_t::add_result_t;
17
+ using search_result_t = typename index_t::search_result_t;
18
+ using serialization_result_t = typename index_t::serialization_result_t;
19
+ using label_t = typename index_t::label_t;
20
+ using id_t = typename index_t::id_t;
21
+ using vector_view_t = unum::usearch::span_gt<float>;
20
22
 
21
23
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
22
24
  JNIEnv* env, jclass, //
@@ -41,14 +43,14 @@ JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
41
43
 
42
44
  scalar_kind_t accuracy = scalar_kind_from_name(accuracy_cstr, accuracy_length);
43
45
  metric_kind_t metric_kind = metric_from_name(metric_cstr, metric_length);
44
- punned_t index = punned_t::make(static_cast<std::size_t>(dimensions), metric_kind, config, accuracy,
45
- expansion_add, expansion_search);
46
+ index_t index = index_t::make( //
47
+ static_cast<std::size_t>(dimensions), metric_kind, config, accuracy, expansion_add, expansion_search);
46
48
  if (!index.reserve(static_cast<std::size_t>(capacity))) {
47
49
  jclass jc = (*env).FindClass("java/lang/Error");
48
50
  if (jc)
49
51
  (*env).ThrowNew(jc, "Failed to reserve desired capacity!");
50
52
  } else {
51
- punned_t* result_ptr = new punned_t(std::move(index));
53
+ index_t* result_ptr = new index_t(std::move(index));
52
54
  std::memcpy(&result, &result_ptr, sizeof(jlong));
53
55
  }
54
56
 
@@ -65,7 +67,7 @@ JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
65
67
 
66
68
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1save(JNIEnv* env, jclass, jlong c_ptr, jstring path) {
67
69
  char const* path_cstr = (*env).GetStringUTFChars(path, 0);
68
- if (!reinterpret_cast<punned_t*>(c_ptr)->save(path_cstr)) {
70
+ if (!reinterpret_cast<index_t*>(c_ptr)->save(path_cstr)) {
69
71
  jclass jc = (*env).FindClass("java/lang/Error");
70
72
  if (jc)
71
73
  (*env).ThrowNew(jc, "Failed to dump vector index to path!");
@@ -75,7 +77,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1save(JNIEnv* env, jclass
75
77
 
76
78
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1load(JNIEnv* env, jclass, jlong c_ptr, jstring path) {
77
79
  char const* path_cstr = (*env).GetStringUTFChars(path, 0);
78
- if (!reinterpret_cast<punned_t*>(c_ptr)->load(path_cstr)) {
80
+ if (!reinterpret_cast<index_t*>(c_ptr)->load(path_cstr)) {
79
81
  jclass jc = (*env).FindClass("java/lang/Error");
80
82
  if (jc)
81
83
  (*env).ThrowNew(jc, "Failed to load vector index from path!");
@@ -85,7 +87,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1load(JNIEnv* env, jclass
85
87
 
86
88
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view(JNIEnv* env, jclass, jlong c_ptr, jstring path) {
87
89
  char const* path_cstr = (*env).GetStringUTFChars(path, 0);
88
- if (!reinterpret_cast<punned_t*>(c_ptr)->view(path_cstr)) {
90
+ if (!reinterpret_cast<index_t*>(c_ptr)->view(path_cstr)) {
89
91
  jclass jc = (*env).FindClass("java/lang/Error");
90
92
  if (jc)
91
93
  (*env).ThrowNew(jc, "Failed to view vector index from path!");
@@ -94,27 +96,27 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view(JNIEnv* env, jclass
94
96
  }
95
97
 
96
98
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1destroy(JNIEnv*, jclass, jlong c_ptr) {
97
- delete reinterpret_cast<punned_t*>(c_ptr);
99
+ delete reinterpret_cast<index_t*>(c_ptr);
98
100
  }
99
101
 
100
102
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1size(JNIEnv*, jclass, jlong c_ptr) {
101
- return reinterpret_cast<punned_t*>(c_ptr)->size();
103
+ return reinterpret_cast<index_t*>(c_ptr)->size();
102
104
  }
103
105
 
104
106
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1connectivity(JNIEnv*, jclass, jlong c_ptr) {
105
- return reinterpret_cast<punned_t*>(c_ptr)->connectivity();
107
+ return reinterpret_cast<index_t*>(c_ptr)->connectivity();
106
108
  }
107
109
 
108
110
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1dimensions(JNIEnv*, jclass, jlong c_ptr) {
109
- return reinterpret_cast<punned_t*>(c_ptr)->dimensions();
111
+ return reinterpret_cast<index_t*>(c_ptr)->dimensions();
110
112
  }
111
113
 
112
114
  JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1capacity(JNIEnv*, jclass, jlong c_ptr) {
113
- return reinterpret_cast<punned_t*>(c_ptr)->capacity();
115
+ return reinterpret_cast<index_t*>(c_ptr)->capacity();
114
116
  }
115
117
 
116
118
  JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1reserve(JNIEnv* env, jclass, jlong c_ptr, jlong capacity) {
117
- if (!reinterpret_cast<punned_t*>(c_ptr)->reserve(static_cast<std::size_t>(capacity))) {
119
+ if (!reinterpret_cast<index_t*>(c_ptr)->reserve(static_cast<std::size_t>(capacity))) {
118
120
  jclass jc = (*env).FindClass("java/lang/Error");
119
121
  if (jc)
120
122
  (*env).ThrowNew(jc, "Failed to grow vector index!");
@@ -126,9 +128,9 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1add( //
126
128
 
127
129
  jfloat* vector_data = (*env).GetFloatArrayElements(vector, 0);
128
130
  jsize vector_dims = (*env).GetArrayLength(vector);
129
- span_t vector_span = span_t{vector_data, static_cast<std::size_t>(vector_dims)};
131
+ vector_view_t vector_span = vector_view_t{vector_data, static_cast<std::size_t>(vector_dims)};
130
132
 
131
- if (!reinterpret_cast<punned_t*>(c_ptr)->add(label, vector_span)) {
133
+ if (!reinterpret_cast<index_t*>(c_ptr)->add(static_cast<label_t>(label), vector_span)) {
132
134
  jclass jc = (*env).FindClass("java/lang/Error");
133
135
  if (jc)
134
136
  (*env).ThrowNew(jc, "Failed to insert a new point in vector index!");
@@ -150,10 +152,10 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
150
152
 
151
153
  jfloat* vector_data = (*env).GetFloatArrayElements(vector, 0);
152
154
  jsize vector_dims = (*env).GetArrayLength(vector);
153
- span_t vector_span = span_t{vector_data, static_cast<std::size_t>(vector_dims)};
154
- search_result_t result = reinterpret_cast<punned_t*>(c_ptr)->search(vector_span, static_cast<std::size_t>(wanted));
155
+ vector_view_t vector_span = vector_view_t{vector_data, static_cast<std::size_t>(vector_dims)};
156
+ search_result_t result = reinterpret_cast<index_t*>(c_ptr)->search(vector_span, static_cast<std::size_t>(wanted));
155
157
  if (result) {
156
- std::size_t found = result.dump_to(matches_data, NULL);
158
+ std::size_t found = result.dump_to(reinterpret_cast<label_t*>(matches_data), NULL);
157
159
  (*env).SetIntArrayRegion(matches, 0, found, matches_data);
158
160
  } else {
159
161
  jclass jc = (*env).FindClass("java/lang/Error");
@@ -9,14 +9,9 @@
9
9
  * @see NodeJS docs: https://nodejs.org/api/addons.html#hello-world
10
10
  *
11
11
  */
12
-
13
- #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
14
- #define _USE_MATH_DEFINES
15
- #define NAPI_CPP_EXCEPTIONS
16
- #endif
17
-
18
12
  #include <new> // `std::bad_alloc`
19
13
 
14
+ #define NAPI_CPP_EXCEPTIONS
20
15
  #include <napi.h>
21
16
  #include <node_api.h>
22
17
 
@@ -25,9 +20,13 @@
25
20
  using namespace unum::usearch;
26
21
  using namespace unum;
27
22
 
28
- using label_t = std::uint32_t;
23
+ using metric_t = index_punned_dense_metric_t;
29
24
  using distance_t = punned_distance_t;
30
- using punned_t = index_punned_dense_gt<label_t>;
25
+ using index_t = punned_small_t;
26
+ using add_result_t = typename index_t::add_result_t;
27
+ using search_result_t = typename index_t::search_result_t;
28
+ using label_t = typename index_t::label_t;
29
+ using id_t = typename index_t::id_t;
31
30
 
32
31
  class Index : public Napi::ObjectWrap<Index> {
33
32
  public:
@@ -47,7 +46,7 @@ class Index : public Napi::ObjectWrap<Index> {
47
46
  void Add(Napi::CallbackInfo const& ctx);
48
47
  Napi::Value Search(Napi::CallbackInfo const& ctx);
49
48
 
50
- std::unique_ptr<punned_t> native_;
49
+ std::unique_ptr<index_t> native_;
51
50
  };
52
51
 
53
52
  Napi::Object Index::Init(Napi::Env env, Napi::Object exports) {
@@ -124,7 +123,7 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
124
123
  }
125
124
 
126
125
  native_.reset(
127
- new punned_t(punned_t::make(dimensions, metric_kind, config, accuracy, expansion_add, expansion_search)));
126
+ new index_t(index_t::make(dimensions, metric_kind, config, accuracy, expansion_add, expansion_search)));
128
127
  native_->reserve(limits);
129
128
  }
130
129
 
@@ -235,7 +234,7 @@ Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
235
234
  return {};
236
235
  }
237
236
 
238
- Napi::Uint32Array matches_js = Napi::Uint32Array::New(env, wanted);
237
+ Napi::TypedArrayOf<label_t> matches_js = Napi::TypedArrayOf<label_t>::New(env, wanted);
239
238
  Napi::Float32Array distances_js = Napi::Float32Array::New(env, wanted);
240
239
  try {
241
240
  std::size_t count = native_->search(vector, wanted).dump_to(matches_js.Data(), distances_js.Data());
@@ -11,7 +11,7 @@ index.add(16, new Float32Array([10, 25]))
11
11
  assert.equal(index.size(), 2)
12
12
 
13
13
  var results = index.search(new Float32Array([13, 14]), 2)
14
- assert.deepEqual(results.labels, new Uint32Array([15, 16]))
14
+ assert.deepEqual(results.labels, new BigUint64Array([15n, 16n]))
15
15
  assert.deepEqual(results.distances, new Float32Array([45, 130]))
16
16
 
17
17
  console.log('JavaScript tests passed!')
@@ -8,12 +8,15 @@
8
8
  using namespace unum::usearch;
9
9
  using namespace unum;
10
10
 
11
+ using metric_t = index_punned_dense_metric_t;
11
12
  using distance_t = punned_distance_t;
12
- using punned_t = index_punned_dense_gt<UInt32>;
13
- using add_result_t = punned_t::add_result_t;
14
- using search_result_t = punned_t::search_result_t;
15
- using serialization_result_t = punned_t::serialization_result_t;
16
- using shared_index_t = std::shared_ptr<punned_t>;
13
+ using index_t = punned_small_t;
14
+ using add_result_t = typename index_t::add_result_t;
15
+ using search_result_t = typename index_t::search_result_t;
16
+ using serialization_result_t = typename index_t::serialization_result_t;
17
+ using label_t = typename index_t::label_t;
18
+ using id_t = typename index_t::id_t;
19
+ using shared_index_t = std::shared_ptr<index_t>;
17
20
 
18
21
  metric_kind_t to_native_metric(USearchMetric m) {
19
22
  switch (m) {
@@ -117,11 +120,11 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
117
120
  index_config_t config;
118
121
 
119
122
  config.connectivity = static_cast<std::size_t>(connectivity);
120
- shared_index_t ptr = std::make_shared<punned_t>(punned_t::make(dims, to_native_metric(metric), config, to_native_scalar(quantization)));
123
+ shared_index_t ptr = std::make_shared<index_t>(index_t::make(dims, to_native_metric(metric), config, to_native_scalar(quantization)));
121
124
  return [[USearchIndex alloc] initWithIndex:ptr];
122
125
  }
123
126
 
124
- - (void)addSingle:(UInt32)label
127
+ - (void)addSingle:(USearchLabel)label
125
128
  vector:(Float32 const *_Nonnull)vector {
126
129
  add_result_t result = _native->add(label, vector);
127
130
 
@@ -134,7 +137,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
134
137
 
135
138
  - (UInt32)searchSingle:(Float32 const *_Nonnull)vector
136
139
  count:(UInt32)wanted
137
- labels:(UInt32 *_Nullable)labels
140
+ labels:(USearchLabel *_Nullable)labels
138
141
  distances:(Float32 *_Nullable)distances {
139
142
  search_result_t result = _native->search(vector, static_cast<std::size_t>(wanted));
140
143
 
@@ -148,7 +151,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
148
151
  return static_cast<UInt32>(found);
149
152
  }
150
153
 
151
- - (void)addDouble:(UInt32)label
154
+ - (void)addDouble:(USearchLabel)label
152
155
  vector:(Float64 const *_Nonnull)vector {
153
156
  add_result_t result = _native->add(label, (f64_t const *)vector);
154
157
 
@@ -161,7 +164,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
161
164
 
162
165
  - (UInt32)searchDouble:(Float64 const *_Nonnull)vector
163
166
  count:(UInt32)wanted
164
- labels:(UInt32 *_Nullable)labels
167
+ labels:(USearchLabel *_Nullable)labels
165
168
  distances:(Float32 *_Nullable)distances {
166
169
  search_result_t result = _native->search((f64_t const *)vector, static_cast<std::size_t>(wanted));
167
170
 
@@ -175,7 +178,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
175
178
  return static_cast<UInt32>(found);
176
179
  }
177
180
 
178
- - (void)addHalf:(UInt32)label
181
+ - (void)addHalf:(USearchLabel)label
179
182
  vector:(void const *_Nonnull)vector {
180
183
  add_result_t result = _native->add(label, (f16_t const *)vector);
181
184
 
@@ -188,7 +191,7 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
188
191
 
189
192
  - (UInt32)searchHalf:(void const *_Nonnull)vector
190
193
  count:(UInt32)wanted
191
- labels:(UInt32 *_Nullable)labels
194
+ labels:(USearchLabel *_Nullable)labels
192
195
  distances:(Float32 *_Nullable)distances {
193
196
  search_result_t result = _native->search((f16_t const *)vector, static_cast<std::size_t>(wanted));
194
197
 
@@ -25,7 +25,7 @@ typedef NS_ENUM(NSUInteger, USearchMetric) {
25
25
  USearchMetricSorensen
26
26
  };
27
27
 
28
- typedef UInt32 USearchLabel;
28
+ typedef UInt64 USearchLabel;
29
29
 
30
30
  API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
31
31
  @interface USearchIndex : NSObject
@@ -80,7 +80,7 @@ API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
80
80
  * @brief Adds a labeled vector to the index.
81
81
  * @param vector Double-precision vector.
82
82
  */
83
- - (void)addDouble:(UInt32)label
83
+ - (void)addDouble:(USearchLabel)label
84
84
  vector:(Float64 const *_Nonnull)vector NS_SWIFT_NAME(addDouble(label:vector:));
85
85
 
86
86
  /**
@@ -100,7 +100,7 @@ API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
100
100
  * @brief Adds a labeled vector to the index.
101
101
  * @param vector Half-precision vector.
102
102
  */
103
- - (void)addHalf:(UInt32)label
103
+ - (void)addHalf:(USearchLabel)label
104
104
  vector:(void const *_Nonnull)vector NS_SWIFT_NAME(addHalf(label:vector:));
105
105
 
106
106
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "0.19.1",
3
+ "version": "0.19.3",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ashot Vardanian",
6
6
  "license": "Apache 2.0",
@@ -24,6 +24,6 @@
24
24
  "@semantic-release/exec": "^6.0.3",
25
25
  "@semantic-release/git": "^10.0.1",
26
26
  "conventional-changelog-eslint": "^3.0.9",
27
- "semantic-release": "^21.0.6"
27
+ "semantic-release": "^21.0.7"
28
28
  }
29
29
  }
package/python/lib.cpp CHANGED
@@ -12,11 +12,10 @@
12
12
  *
13
13
  * @copyright Copyright (c) 2023
14
14
  */
15
- #define PY_SSIZE_T_CLEAN
16
- #define NOMINMAX // Some of our dependencies call `std::max(x, y)`, which crashes Windows builds
17
15
  #include <limits> // `std::numeric_limits`
18
16
  #include <thread> // `std::thread`
19
17
 
18
+ #define PY_SSIZE_T_CLEAN
20
19
  #include <pybind11/numpy.h>
21
20
  #include <pybind11/pybind11.h>
22
21
 
@@ -38,26 +37,23 @@ enum class metric_signature_t {
38
37
 
39
38
  namespace py = pybind11;
40
39
  using py_shape_t = py::array::ShapeContainer;
41
-
42
- using label_t = std::uint32_t;
43
- using distance_t = punned_distance_t;
40
+ using label_t = typename punned_small_t::label_t;
41
+ using id_t = typename punned_small_t::id_t;
44
42
  using metric_t = index_punned_dense_metric_t;
45
- using id_t = std::uint32_t;
46
- using big_id_t = std::uint64_t;
47
-
48
- using punned_index_t = index_punned_dense_gt<label_t, id_t>;
49
- using punned_add_result_t = typename punned_index_t::add_result_t;
50
- using punned_search_result_t = typename punned_index_t::search_result_t;
43
+ using distance_t = punned_distance_t;
44
+ using dense_index_t = punned_small_t;
45
+ using dense_add_result_t = typename dense_index_t::add_result_t;
46
+ using dense_search_result_t = typename dense_index_t::search_result_t;
51
47
 
52
- struct punned_index_py_t : public punned_index_t {
53
- using native_t = punned_index_t;
48
+ struct dense_index_py_t : public dense_index_t {
49
+ using native_t = dense_index_t;
54
50
  using native_t::add;
55
51
  using native_t::capacity;
56
52
  using native_t::reserve;
57
53
  using native_t::search;
58
54
  using native_t::size;
59
55
 
60
- punned_index_py_t(native_t&& base) : native_t(std::move(base)) {}
56
+ dense_index_py_t(native_t&& base) : native_t(std::move(base)) {}
61
57
  };
62
58
 
63
59
  using set_member_t = std::uint32_t;
@@ -121,7 +117,7 @@ metric_t udf(metric_kind_t kind, metric_signature_t signature, std::uintptr_t me
121
117
  }
122
118
  }
123
119
 
124
- static punned_index_py_t make_index( //
120
+ static dense_index_py_t make_index( //
125
121
  std::size_t dimensions, //
126
122
  scalar_kind_t scalar_kind, //
127
123
  metric_kind_t metric_kind, //
@@ -136,14 +132,14 @@ static punned_index_py_t make_index( //
136
132
  config.connectivity = connectivity;
137
133
 
138
134
  if (tune)
139
- config = punned_index_t::optimize(config);
135
+ config = dense_index_t::optimize(config);
140
136
 
141
137
  if (metric_uintptr)
142
- return punned_index_t::make( //
138
+ return dense_index_t::make( //
143
139
  dimensions, udf(metric_kind, metric_signature, metric_uintptr, scalar_kind), config, scalar_kind,
144
140
  expansion_add, expansion_search);
145
141
  else
146
- return punned_index_t::make(dimensions, metric_kind, config, scalar_kind, expansion_add, expansion_search);
142
+ return dense_index_t::make(dimensions, metric_kind, config, scalar_kind, expansion_add, expansion_search);
147
143
  }
148
144
 
149
145
  static std::unique_ptr<sparse_index_py_t> make_sparse_index( //
@@ -173,7 +169,7 @@ scalar_kind_t numpy_string_to_kind(std::string const& name) {
173
169
  return scalar_kind_t::unknown_k;
174
170
  }
175
171
 
176
- static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer vector, bool copy, std::size_t) {
172
+ static void add_one_to_index(dense_index_py_t& index, label_t label, py::buffer vector, bool copy, std::size_t) {
177
173
 
178
174
  py::buffer_info vector_info = vector.request();
179
175
  if (vector_info.ndim != 1)
@@ -203,7 +199,7 @@ static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer
203
199
 
204
200
  template <typename scalar_at>
205
201
  static void add_typed_to_index( //
206
- punned_index_py_t& index, //
202
+ dense_index_py_t& index, //
207
203
  py::buffer_info const& labels_info, py::buffer_info const& vectors_info, //
208
204
  bool copy, std::size_t threads) {
209
205
 
@@ -223,8 +219,8 @@ static void add_typed_to_index( //
223
219
  });
224
220
  }
225
221
 
226
- static void add_many_to_index( //
227
- punned_index_py_t& index, py::buffer labels, py::buffer vectors, //
222
+ static void add_many_to_index( //
223
+ dense_index_py_t& index, py::buffer labels, py::buffer vectors, //
228
224
  bool copy, std::size_t threads) {
229
225
 
230
226
  if (index.limits().threads_add < threads)
@@ -265,7 +261,7 @@ static void add_many_to_index( //
265
261
  }
266
262
  }
267
263
 
268
- static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector, std::size_t wanted, bool exact) {
264
+ static py::tuple search_one_in_index(dense_index_py_t& index, py::buffer vector, std::size_t wanted, bool exact) {
269
265
 
270
266
  py::buffer_info vector_info = vector.request();
271
267
  Py_ssize_t vector_dimensions = vector_info.shape[0];
@@ -282,7 +278,7 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
282
278
  search_config_t config;
283
279
  config.exact = exact;
284
280
 
285
- auto raise_and_dump = [&](punned_search_result_t result) {
281
+ auto raise_and_dump = [&](dense_search_result_t result) {
286
282
  result.error.raise();
287
283
  count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
288
284
  };
@@ -308,9 +304,9 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
308
304
  }
309
305
 
310
306
  template <typename scalar_at>
311
- static void search_typed( //
312
- punned_index_py_t& index, py::buffer_info& vectors_info, //
313
- std::size_t wanted, bool exact, std::size_t threads, //
307
+ static void search_typed( //
308
+ dense_index_py_t& index, py::buffer_info& vectors_info, //
309
+ std::size_t wanted, bool exact, std::size_t threads, //
314
310
  py::array_t<label_t>& labels_py, py::array_t<distance_t>& distances_py, py::array_t<Py_ssize_t>& counts_py) {
315
311
 
316
312
  auto labels_py2d = labels_py.template mutable_unchecked<2>();
@@ -325,7 +321,7 @@ static void search_typed( //
325
321
  config.thread = thread_idx;
326
322
  config.exact = exact;
327
323
  scalar_at const* vector = (scalar_at const*)(vectors_data + task_idx * vectors_info.strides[0]);
328
- punned_search_result_t result = index.search(vector, wanted, config);
324
+ dense_search_result_t result = index.search(vector, wanted, config);
329
325
  result.error.raise();
330
326
  counts_py1d(task_idx) =
331
327
  static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
@@ -344,7 +340,7 @@ static void search_typed( //
344
340
  * 3. array with match counts.
345
341
  */
346
342
  static py::tuple search_many_in_index( //
347
- punned_index_py_t& index, py::buffer vectors, std::size_t wanted, bool exact, std::size_t threads) {
343
+ dense_index_py_t& index, py::buffer vectors, std::size_t wanted, bool exact, std::size_t threads) {
348
344
 
349
345
  if (wanted == 0)
350
346
  return py::tuple(3);
@@ -591,7 +587,7 @@ PYBIND11_MODULE(compiled, m) {
591
587
  h.def_readonly("bytes_for_vectors", &file_head_result_t::bytes_for_vectors);
592
588
  h.def_readonly("bytes_checksum", &file_head_result_t::bytes_checksum);
593
589
 
594
- auto i = py::class_<punned_index_py_t>(m, "Index");
590
+ auto i = py::class_<dense_index_py_t>(m, "Index");
595
591
 
596
592
  i.def(py::init(&make_index), //
597
593
  py::kw_only(), //
@@ -632,30 +628,29 @@ PYBIND11_MODULE(compiled, m) {
632
628
  py::arg("threads") = 0 //
633
629
  );
634
630
 
635
- i.def("__len__", &punned_index_py_t::size);
636
- i.def_property_readonly("size", &punned_index_py_t::size);
637
- i.def_property_readonly("ndim", &punned_index_py_t::dimensions);
638
- i.def_property_readonly("connectivity", &punned_index_py_t::connectivity);
639
- i.def_property_readonly("capacity", &punned_index_py_t::capacity);
631
+ i.def("__len__", &dense_index_py_t::size);
632
+ i.def_property_readonly("size", &dense_index_py_t::size);
633
+ i.def_property_readonly("ndim", &dense_index_py_t::dimensions);
634
+ i.def_property_readonly("connectivity", &dense_index_py_t::connectivity);
635
+ i.def_property_readonly("capacity", &dense_index_py_t::capacity);
640
636
  i.def_property_readonly( //
641
- "dtype", [](punned_index_py_t const& index) -> scalar_kind_t { return index.metric().scalar_kind_; });
637
+ "dtype", [](dense_index_py_t const& index) -> scalar_kind_t { return index.metric().scalar_kind_; });
642
638
  i.def_property_readonly( //
643
- "memory_usage", [](punned_index_py_t const& index) -> std::size_t { return index.memory_usage(); },
639
+ "memory_usage", [](dense_index_py_t const& index) -> std::size_t { return index.memory_usage(); },
644
640
  py::call_guard<py::gil_scoped_release>());
645
641
 
646
- i.def_property("expansion_add", &punned_index_py_t::expansion_add, &punned_index_py_t::change_expansion_add);
647
- i.def_property("expansion_search", &punned_index_py_t::expansion_search,
648
- &punned_index_py_t::change_expansion_search);
642
+ i.def_property("expansion_add", &dense_index_py_t::expansion_add, &dense_index_py_t::change_expansion_add);
643
+ i.def_property("expansion_search", &dense_index_py_t::expansion_search, &dense_index_py_t::change_expansion_search);
649
644
 
650
- i.def_property_readonly("labels", &get_all_labels<punned_index_py_t>);
651
- i.def("get_labels", &get_labels<punned_index_py_t>, py::arg("limit") = std::numeric_limits<std::size_t>::max());
652
- i.def("__contains__", &punned_index_py_t::contains);
653
- i.def("__getitem__", &get_member<punned_index_py_t>, py::arg("label"), py::arg("dtype") = scalar_kind_t::f32_k);
645
+ i.def_property_readonly("labels", &get_all_labels<dense_index_py_t>);
646
+ i.def("get_labels", &get_labels<dense_index_py_t>, py::arg("limit") = std::numeric_limits<std::size_t>::max());
647
+ i.def("__contains__", &dense_index_py_t::contains);
648
+ i.def("__getitem__", &get_member<dense_index_py_t>, py::arg("label"), py::arg("dtype") = scalar_kind_t::f32_k);
654
649
 
655
- i.def("save", &save_index<punned_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
656
- i.def("load", &load_index<punned_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
657
- i.def("view", &view_index<punned_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
658
- i.def("clear", &punned_index_py_t::clear, py::call_guard<py::gil_scoped_release>());
650
+ i.def("save", &save_index<dense_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
651
+ i.def("load", &load_index<dense_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
652
+ i.def("view", &view_index<dense_index_py_t>, py::arg("path"), py::call_guard<py::gil_scoped_release>());
653
+ i.def("clear", &dense_index_py_t::clear, py::call_guard<py::gil_scoped_release>());
659
654
 
660
655
  auto si = py::class_<sparse_index_py_t>(m, "SparseIndex");
661
656
 
@@ -1,14 +1,13 @@
1
- import os
2
- import logging
3
1
  import itertools
4
- from typing import List, Optional, Iterable
2
+ from typing import List
5
3
  from dataclasses import asdict
6
4
 
7
5
  import numpy as np
8
6
  import pandas as pd
9
7
 
10
- from usearch.index import Index, Label
11
- from usearch.eval import Evaluation, Dataset, AddTask
8
+ from usearch.index import Index, Label, MetricKind, ScalarKind
9
+ from usearch.numba import jit as njit
10
+ from usearch.eval import Evaluation, AddTask
12
11
  from usearch.index import (
13
12
  DEFAULT_CONNECTIVITY,
14
13
  DEFAULT_EXPANSION_ADD,
@@ -26,14 +25,15 @@ def bench_speed(
26
25
  ) -> pd.DataFrame:
27
26
  # Build various indexes:
28
27
  indexes = []
29
- jit_options = [False]
30
- if jit:
31
- jit_options.append(jit)
32
- dtype_options = ["f32", "f16", "f8"]
28
+ jit_options = [False, True] if jit else [False]
29
+ dtype_options = [ScalarKind.F32, ScalarKind.F16, ScalarKind.F8]
33
30
  for jit, dtype in itertools.product(jit_options, dtype_options):
31
+ metric = MetricKind.IP
32
+ if jit:
33
+ metric = njit(eval.ndim, metric, dtype)
34
34
  index = Index(
35
35
  ndim=eval.ndim,
36
- jit=jit,
36
+ metric=metric,
37
37
  dtype=dtype,
38
38
  expansion_add=expansion_add,
39
39
  expansion_search=expansion_search,
@@ -1,9 +1,12 @@
1
1
  import os
2
+ import importlib.util
3
+
2
4
  import pytest
3
5
  import numpy as np
4
6
 
5
7
  from usearch.io import load_matrix, save_matrix
6
- from usearch.eval import recall_members, random_vectors
8
+ from usearch.eval import random_vectors
9
+ from usearch.numba import jit as njit
7
10
 
8
11
  from usearch.index import (
9
12
  Index,
@@ -13,9 +16,7 @@ from usearch.index import (
13
16
  MetricSignature,
14
17
  CompiledMetric,
15
18
  Matches,
16
- MetricKindBitwise,
17
19
  )
18
- from usearch.index import _normalize_dtype
19
20
  from usearch.index import (
20
21
  DEFAULT_CONNECTIVITY,
21
22
  DEFAULT_EXPANSION_ADD,
@@ -34,7 +35,7 @@ index_types = [
34
35
  numpy_types = [np.float32, np.float64, np.float16]
35
36
 
36
37
  connectivity_options = [3, 13, 50, DEFAULT_CONNECTIVITY]
37
- jit_options = [False]
38
+ jit_options = [False, True] if importlib.util.find_spec("spam") is not None else [False]
38
39
  continuous_metrics = [
39
40
  MetricKind.Cos,
40
41
  MetricKind.L2sq,
@@ -80,6 +81,8 @@ def test_index(
80
81
  connectivity: int,
81
82
  jit: bool,
82
83
  ):
84
+ if jit:
85
+ metric = njit(ndim, metric, index_type)
83
86
  index = Index(
84
87
  metric=metric,
85
88
  ndim=ndim,
@@ -87,7 +90,6 @@ def test_index(
87
90
  connectivity=connectivity,
88
91
  expansion_add=DEFAULT_EXPANSION_ADD,
89
92
  expansion_search=DEFAULT_EXPANSION_SEARCH,
90
- jit=jit,
91
93
  )
92
94
  assert index.ndim == ndim
93
95
  assert index.connectivity == connectivity
@@ -176,7 +178,10 @@ def test_index_batch(
176
178
  @pytest.mark.parametrize("index_type", index_types)
177
179
  @pytest.mark.parametrize("numpy_type", numpy_types)
178
180
  def test_exact_recall(
179
- metric: MetricKind, batch_size: int, index_type: ScalarKind, numpy_type: str
181
+ metric: MetricKind,
182
+ batch_size: int,
183
+ index_type: ScalarKind,
184
+ numpy_type: str,
180
185
  ):
181
186
  ndim: int = batch_size
182
187
  index = Index(ndim=ndim, metric=metric, dtype=index_type)
@@ -436,7 +441,10 @@ def test_index_peachpy(ndim: int, batch_size: int):
436
441
  @pytest.mark.parametrize("connectivity", connectivity_options)
437
442
  @pytest.mark.parametrize("batch_size", batch_sizes)
438
443
  def test_bitwise_index(
439
- bits: int, metric: MetricKind, connectivity: int, batch_size: int
444
+ bits: int,
445
+ metric: MetricKind,
446
+ connectivity: int,
447
+ batch_size: int,
440
448
  ):
441
449
  index = Index(ndim=bits, metric=metric, connectivity=connectivity)
442
450
 
@@ -31,7 +31,7 @@ MetricKindBitwise = (
31
31
 
32
32
  SparseIndex = _CompiledSetsIndex
33
33
 
34
- Label = np.uint32
34
+ Label = np.uint64
35
35
 
36
36
 
37
37
  def _normalize_dtype(dtype, metric: MetricKind = MetricKind.IP) -> ScalarKind:
@@ -167,10 +167,9 @@ class Index:
167
167
 
168
168
  def __init__(
169
169
  self,
170
- ndim: int,
170
+ ndim: int = 0,
171
171
  metric: Union[str, MetricKind, CompiledMetric] = MetricKind.IP,
172
- dtype: Optional[str] = None,
173
- jit: bool = False,
172
+ dtype: Optional[Union[str, ScalarKind]] = None,
174
173
  connectivity: int = DEFAULT_CONNECTIVITY,
175
174
  expansion_add: int = DEFAULT_EXPANSION_ADD,
176
175
  expansion_search: int = DEFAULT_EXPANSION_SEARCH,
@@ -182,6 +181,10 @@ class Index:
182
181
 
183
182
  :param ndim: Number of vector dimensions
184
183
  :type ndim: int
184
+ Required for some metrics, optional for others.
185
+ Haversine, for example, only applies to 2-dimensional latitude/longitude
186
+ coordinates. Angular (Cos) and Euclidean (L2sq), obviously, apply to
187
+ vectors with arbitrary number of dimensions.
185
188
 
186
189
  :param metric: Distance function, defaults to MetricKind.IP
187
190
  :type metric: Union[MetricKind, Callable, str], optional
@@ -191,18 +194,12 @@ class Index:
191
194
  Not every kind is JIT-able. For Jaccard distance, use `SparseIndex`.
192
195
 
193
196
  :param dtype: Scalar type for internal vector storage, defaults to None
194
- :type dtype: str, optional
197
+ :type dtype: Optional[Union[str, ScalarKind]], optional
195
198
  For continuous metrics can be: f16, f32, f64, or f8.
196
199
  For bitwise metrics it's implementation-defined, and can't change.
197
200
  Example: you can use the `f16` index with `f32` vectors in Euclidean space,
198
201
  which will be automatically downcasted.
199
202
 
200
- :param jit: Enable Numba to JIT compile the metric, defaults to False
201
- :type jit: bool, optional
202
- This can result in up-to 3x performance difference on very large vectors
203
- and very recent hardware, as the Python module is compiled with high
204
- compatibility in mind and avoids very fancy assembly instructions.
205
-
206
203
  :param connectivity: Connections per node in HNSW, defaults to None
207
204
  :type connectivity: Optional[int], optional
208
205
  Hyper-parameter for the number of Graph connections
@@ -231,21 +228,6 @@ class Index:
231
228
  """
232
229
 
233
230
  metric = _normalize_metric(metric)
234
- if isinstance(metric, MetricKind) and jit:
235
- try:
236
- from usearch.numba import jit
237
- except ImportError:
238
- raise ModuleNotFoundError(
239
- "To use JIT install Numba with `pip install numba`."
240
- "Alternatively, reinstall with `pip install usearch[jit]`"
241
- )
242
-
243
- metric = jit(
244
- ndim=ndim,
245
- metric=metric,
246
- dtype=_normalize_dtype(dtype, metric),
247
- )
248
-
249
231
  if isinstance(metric, MetricKind):
250
232
  self._metric_kind = metric
251
233
  self._metric_jit = None
@@ -4,31 +4,19 @@
4
4
  # into the primary `Index` class, connecting USearch with Numba.
5
5
  from math import sqrt
6
6
 
7
- from numba import cfunc, types, carray
8
-
9
7
  from usearch.index import MetricKind, ScalarKind, MetricSignature, CompiledMetric
10
8
 
11
9
 
12
- signature_i8args = types.float32(types.CPointer(types.int8), types.CPointer(types.int8))
13
-
14
- signature_f16args = types.float32(
15
- types.CPointer(types.float16), types.CPointer(types.float16)
16
- )
17
-
18
- signature_f32args = types.float32(
19
- types.CPointer(types.float32), types.CPointer(types.float32)
20
- )
21
-
22
- signature_f64args = types.float32(
23
- types.CPointer(types.float64), types.CPointer(types.float64)
24
- )
25
-
26
-
27
10
  def jit(
28
- ndim: int, metric: MetricKind = MetricKind.Cos, dtype: ScalarKind = ScalarKind.F32
11
+ ndim: int,
12
+ metric: MetricKind = MetricKind.Cos,
13
+ dtype: ScalarKind = ScalarKind.F32,
29
14
  ) -> CompiledMetric:
30
- """JIT-compiles a distance metric specifically tuned for the target hardware
31
- and number of dimensions.
15
+ """JIT-compiles the metric for target hardware and number of dimensions.
16
+
17
+ This can result in up-to 3x performance difference on very large vectors
18
+ and very recent hardware, as the Python module is compiled with high
19
+ compatibility in mind and avoids very fancy assembly instructions.
32
20
 
33
21
  Uses Numba `cfunc` functionality, annotating it with Numba `types` instead
34
22
  of `ctypes` to support half-precision.
@@ -37,6 +25,24 @@ def jit(
37
25
  assert isinstance(metric, MetricKind)
38
26
  assert isinstance(dtype, ScalarKind)
39
27
 
28
+ from numba import cfunc, types, carray
29
+
30
+ signature_i8args = types.float32(
31
+ types.CPointer(types.int8), types.CPointer(types.int8)
32
+ )
33
+
34
+ signature_f16args = types.float32(
35
+ types.CPointer(types.float16), types.CPointer(types.float16)
36
+ )
37
+
38
+ signature_f32args = types.float32(
39
+ types.CPointer(types.float32), types.CPointer(types.float32)
40
+ )
41
+
42
+ signature_f64args = types.float32(
43
+ types.CPointer(types.float64), types.CPointer(types.float64)
44
+ )
45
+
40
46
  numba_supported_types = (
41
47
  ScalarKind.F8,
42
48
  # Half-precision is still unsupported
@@ -46,7 +52,7 @@ def jit(
46
52
  ScalarKind.F64,
47
53
  )
48
54
  if dtype not in numba_supported_types:
49
- return None
55
+ return metric
50
56
 
51
57
  scalar_kind_to_accumulator_type = {
52
58
  ScalarKind.F8: types.int32,
@@ -109,7 +115,7 @@ def jit(
109
115
 
110
116
  pointer = cfunc(scalar_kind_to_signature[dtype])(metric_kind_to_function[metric])
111
117
  return CompiledMetric(
112
- pointer=pointer,
118
+ pointer=pointer.address,
113
119
  kind=metric,
114
120
  signature=MetricSignature.ArrayArray,
115
121
  )
package/rust/lib.cpp CHANGED
@@ -4,14 +4,14 @@
4
4
  using namespace unum::usearch;
5
5
  using namespace unum;
6
6
 
7
- using punned_t = typename Index::punned_t;
8
- using add_result_t = typename punned_t::add_result_t;
9
- using search_result_t = typename punned_t::search_result_t;
10
- using serialization_result_t = typename punned_t::serialization_result_t;
7
+ using index_t = typename Index::index_t;
8
+ using add_result_t = typename index_t::add_result_t;
9
+ using search_result_t = typename index_t::search_result_t;
10
+ using serialization_result_t = typename index_t::serialization_result_t;
11
11
 
12
- Index::Index(std::unique_ptr<punned_t> index) : index_(std::move(index)) {}
12
+ Index::Index(std::unique_ptr<index_t> index) : index_(std::move(index)) {}
13
13
 
14
- void Index::add_in_thread(uint32_t label, rust::Slice<float const> vector, size_t thread) const {
14
+ void Index::add_in_thread(label_t label, rust::Slice<float const> vector, size_t thread) const {
15
15
  add_config_t config;
16
16
  config.thread = thread;
17
17
  index_->add(label, vector.data(), config).error.raise();
@@ -33,7 +33,7 @@ Matches Index::search_in_thread(rust::Slice<float const> vector, size_t count, s
33
33
  return matches;
34
34
  }
35
35
 
36
- void Index::add(uint32_t label, rust::Slice<float const> vector) const {
36
+ void Index::add(label_t label, rust::Slice<float const> vector) const {
37
37
  index_->add(label, vector.data()).error.raise();
38
38
  }
39
39
 
@@ -64,9 +64,9 @@ void Index::view(rust::Str path) const { index_->view(std::string(path).c_str())
64
64
 
65
65
  scalar_kind_t accuracy(rust::Str quant) { return scalar_kind_from_name(quant.data(), quant.size()); }
66
66
 
67
- std::unique_ptr<Index> wrap(punned_t&& index) {
68
- std::unique_ptr<punned_t> punned_ptr;
69
- punned_ptr.reset(new punned_t(std::move(index)));
67
+ std::unique_ptr<Index> wrap(index_t&& index) {
68
+ std::unique_ptr<index_t> punned_ptr;
69
+ punned_ptr.reset(new index_t(std::move(index)));
70
70
  std::unique_ptr<Index> result;
71
71
  result.reset(new Index(std::move(punned_ptr)));
72
72
  return result;
@@ -104,7 +104,7 @@ scalar_kind_t rust_to_cpp_scalar(ScalarKind value) {
104
104
  }
105
105
 
106
106
  std::unique_ptr<Index> new_index(IndexOptions const& options) {
107
- return wrap(punned_t::make(options.dimensions, rust_to_cpp_metric(options.metric), config(options.connectivity),
108
- rust_to_cpp_scalar(options.quantization), options.expansion_add,
109
- options.expansion_search));
107
+ return wrap(index_t::make(options.dimensions, rust_to_cpp_metric(options.metric), config(options.connectivity),
108
+ rust_to_cpp_scalar(options.quantization), options.expansion_add,
109
+ options.expansion_search));
110
110
  }
package/rust/lib.hpp CHANGED
@@ -10,16 +10,20 @@ struct IndexOptions;
10
10
 
11
11
  class Index {
12
12
  public:
13
- using label_t = std::uint32_t;
13
+ using metric_t = unum::usearch::index_punned_dense_metric_t;
14
14
  using distance_t = unum::usearch::punned_distance_t;
15
- using punned_t = unum::usearch::index_punned_dense_gt<label_t>;
15
+ using index_t = unum::usearch::punned_small_t;
16
+ using add_result_t = typename index_t::add_result_t;
17
+ using search_result_t = typename index_t::search_result_t;
18
+ using label_t = typename index_t::label_t;
19
+ using id_t = typename index_t::id_t;
16
20
 
17
- Index(std::unique_ptr<punned_t> index);
21
+ Index(std::unique_ptr<index_t> index);
18
22
 
19
23
  void reserve(size_t) const;
20
24
 
21
- void add(uint32_t label, rust::Slice<float const> vector) const;
22
- void add_in_thread(uint32_t label, rust::Slice<float const> vector, size_t thread) const;
25
+ void add(label_t label, rust::Slice<float const> vector) const;
26
+ void add_in_thread(label_t label, rust::Slice<float const> vector, size_t thread) const;
23
27
 
24
28
  Matches search(rust::Slice<float const> vector, size_t count) const;
25
29
  Matches search_in_thread(rust::Slice<float const> vector, size_t count, size_t thread) const;
@@ -34,7 +38,7 @@ class Index {
34
38
  void view(rust::Str path) const;
35
39
 
36
40
  private:
37
- std::unique_ptr<punned_t> index_;
41
+ std::unique_ptr<index_t> index_;
38
42
  };
39
43
 
40
44
  std::unique_ptr<Index> new_index(IndexOptions const& options);
package/rust/lib.rs CHANGED
@@ -4,7 +4,7 @@ pub mod ffi {
4
4
  // Shared structs with fields visible to both languages.
5
5
  struct Matches {
6
6
  count: usize,
7
- labels: Vec<u32>,
7
+ labels: Vec<u64>,
8
8
  distances: Vec<f32>,
9
9
  }
10
10
 
@@ -51,7 +51,7 @@ pub mod ffi {
51
51
  pub fn size(self: &Index) -> usize;
52
52
  pub fn capacity(self: &Index) -> usize;
53
53
 
54
- pub fn add(self: &Index, label: u32, vector: &[f32]) -> Result<()>;
54
+ pub fn add(self: &Index, label: u64, vector: &[f32]) -> Result<()>;
55
55
  pub fn search(self: &Index, query: &[f32], count: usize) -> Result<Matches>;
56
56
 
57
57
  pub fn save(self: &Index, path: &str) -> Result<()>;
@@ -7,11 +7,15 @@
7
7
 
8
8
  @available(iOS 13, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
9
9
  extension USearchIndex {
10
+ public typealias Label = USearchLabel
11
+ public typealias Metric = USearchMetric
12
+ public typealias Scalar = USearchScalar
13
+
10
14
  /// Adds a labeled vector to the index.
11
15
  /// - Parameter label: Unique identifer for that object.
12
16
  /// - Parameter vector: Single-precision vector.
13
17
  /// - Throws: If runs out of memory.
14
- public func add(label: UInt32, vector: ArraySlice<Float32>) {
18
+ public func add(label: USearchLabel, vector: ArraySlice<Float32>) {
15
19
  vector.withContiguousStorageIfAvailable {
16
20
  addSingle(label: label, vector: $0.baseAddress!)
17
21
  }
@@ -23,8 +27,8 @@ extension USearchIndex {
23
27
  /// - Parameter count: Upper limit on the number of matches to retrieve.
24
28
  /// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
25
29
  /// - Throws: If runs out of memory.
26
- public func search(vector: ArraySlice<Float32>, count: Int) -> ([UInt32], [Float]) {
27
- var matches: [UInt32] = Array(repeating: 0, count: count)
30
+ public func search(vector: ArraySlice<Float32>, count: Int) -> ([Label], [Float]) {
31
+ var matches: [Label] = Array(repeating: 0, count: count)
28
32
  var distances: [Float] = Array(repeating: 0, count: count)
29
33
  let results = vector.withContiguousStorageIfAvailable {
30
34
  searchSingle(vector: $0.baseAddress!, count: CUnsignedInt(count), labels: &matches, distances: &distances)
@@ -38,7 +42,7 @@ extension USearchIndex {
38
42
  /// - Parameter label: Unique identifer for that object.
39
43
  /// - Parameter vector: Double-precision vector.
40
44
  /// - Throws: If runs out of memory.
41
- public func add(label: UInt32, vector: ArraySlice<Float64>) {
45
+ public func add(label: Label, vector: ArraySlice<Float64>) {
42
46
  vector.withContiguousStorageIfAvailable {
43
47
  addDouble(label: label, vector: $0.baseAddress!)
44
48
  }
@@ -49,8 +53,8 @@ extension USearchIndex {
49
53
  /// - Parameter count: Upper limit on the number of matches to retrieve.
50
54
  /// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
51
55
  /// - Throws: If runs out of memory.
52
- public func search(vector: ArraySlice<Float64>, count: Int) -> ([UInt32], [Float]) {
53
- var matches: [UInt32] = Array(repeating: 0, count: count)
56
+ public func search(vector: ArraySlice<Float64>, count: Int) -> ([Label], [Float]) {
57
+ var matches: [Label] = Array(repeating: 0, count: count)
54
58
  var distances: [Float] = Array(repeating: 0, count: count)
55
59
  let results = vector.withContiguousStorageIfAvailable {
56
60
  searchDouble(vector: $0.baseAddress!, count: CUnsignedInt(count), labels: &matches, distances: &distances)
@@ -67,7 +71,7 @@ extension USearchIndex {
67
71
  /// - Parameter vector: Half-precision vector.
68
72
  /// - Throws: If runs out of memory.
69
73
  @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
70
- public func add(label: UInt32, vector: ArraySlice<Float16>) {
74
+ public func add(label: Label, vector: ArraySlice<Float16>) {
71
75
  vector.withContiguousStorageIfAvailable { buffer in
72
76
  addHalf(label: label, vector: buffer.baseAddress!)
73
77
  }
@@ -79,8 +83,8 @@ extension USearchIndex {
79
83
  /// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
80
84
  /// - Throws: If runs out of memory.
81
85
  @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
82
- public func search(vector: ArraySlice<Float16>, count: Int) -> ([UInt32], [Float]) {
83
- var matches: [UInt32] = Array(repeating: 0, count: count)
86
+ public func search(vector: ArraySlice<Float16>, count: Int) -> ([Label], [Float]) {
87
+ var matches: [Label] = Array(repeating: 0, count: count)
84
88
  var distances: [Float] = Array(repeating: 0, count: count)
85
89
  let results = vector.withContiguousStorageIfAvailable {
86
90
  searchHalf(vector: $0.baseAddress!, count: CUnsignedInt(count), labels: &matches, distances: &distances)
package/wolfram/lib.cpp CHANGED
@@ -2,9 +2,16 @@
2
2
  #include <usearch/index_punned_dense.hpp>
3
3
 
4
4
  using namespace unum::usearch;
5
+
6
+ using metric_t = index_punned_dense_metric_t;
5
7
  using distance_t = punned_distance_t;
6
- using punned_t = index_punned_dense_gt<int>;
7
- using span_t = span_gt<float>;
8
+ using index_t = punned_small_t;
9
+ using vector_view_t = span_gt<float>;
10
+
11
+ using add_result_t = typename index_t::add_result_t;
12
+ using search_result_t = typename index_t::search_result_t;
13
+ using label_t = typename index_t::label_t;
14
+ using id_t = typename index_t::id_t;
8
15
 
9
16
  EXTERN_C DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData) { return LIBRARY_NO_ERROR; }
10
17
  EXTERN_C DLLEXPORT void WolframLibrary_uninitialize(WolframLibraryData libData) { return; }
@@ -24,10 +31,10 @@ EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgum
24
31
 
25
32
  scalar_kind_t accuracy = scalar_kind_from_name(accuracy_cstr, std::strlen(accuracy_cstr));
26
33
  metric_kind_t metric_kind = metric_from_name(metric_cstr, std::strlen(metric_cstr));
27
- punned_t index = make_punned<punned_t>(metric_kind, dimensions, accuracy, config);
34
+ index_t index = make_punned<index_t>(metric_kind, dimensions, accuracy, config);
28
35
  index.reserve(capacity);
29
36
 
30
- punned_t* result_ptr = new punned_t(std::move(index));
37
+ index_t* result_ptr = new index_t(std::move(index));
31
38
  MArgument_setInteger(Res, (long)result_ptr);
32
39
 
33
40
  } catch (...) {
@@ -41,7 +48,7 @@ EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgum
41
48
 
42
49
  EXTERN_C DLLEXPORT int IndexSave(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
43
50
  char* path_cstr = nullptr;
44
- punned_t* c_ptr = (punned_t*)MArgument_getUTF8String(Args[0]);
51
+ index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
45
52
  try {
46
53
  path_cstr = MArgument_getUTF8String(Args[1]);
47
54
  c_ptr->save(path_cstr);
@@ -54,7 +61,7 @@ EXTERN_C DLLEXPORT int IndexSave(WolframLibraryData libData, mint Argc, MArgumen
54
61
 
55
62
  EXTERN_C DLLEXPORT int IndexLoad(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
56
63
  char* path_cstr = nullptr;
57
- punned_t* c_ptr = (punned_t*)MArgument_getUTF8String(Args[0]);
64
+ index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
58
65
  try {
59
66
  path_cstr = MArgument_getUTF8String(Args[1]);
60
67
  c_ptr->load(path_cstr);
@@ -67,7 +74,7 @@ EXTERN_C DLLEXPORT int IndexLoad(WolframLibraryData libData, mint Argc, MArgumen
67
74
 
68
75
  EXTERN_C DLLEXPORT int IndexView(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
69
76
  char* path_cstr = nullptr;
70
- punned_t* c_ptr = (punned_t*)MArgument_getUTF8String(Args[0]);
77
+ index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
71
78
  try {
72
79
  path_cstr = MArgument_getUTF8String(Args[1]);
73
80
  c_ptr->view(path_cstr);
@@ -79,44 +86,44 @@ EXTERN_C DLLEXPORT int IndexView(WolframLibraryData libData, mint Argc, MArgumen
79
86
  }
80
87
 
81
88
  EXTERN_C DLLEXPORT int IndexDestroy(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
82
- delete (punned_t*)MArgument_getUTF8String(Args[0]);
89
+ delete (index_t*)MArgument_getUTF8String(Args[0]);
83
90
  return LIBRARY_NO_ERROR;
84
91
  }
85
92
 
86
93
  EXTERN_C DLLEXPORT int IndexSize(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
87
- size_t res = ((punned_t*)MArgument_getUTF8String(Args[0]))->size();
94
+ std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->size();
88
95
  MArgument_setInteger(Res, res);
89
96
  return LIBRARY_NO_ERROR;
90
97
  }
91
98
 
92
99
  EXTERN_C DLLEXPORT int IndexConnectivity(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
93
- size_t res = ((punned_t*)MArgument_getUTF8String(Args[0]))->connectivity();
100
+ std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->connectivity();
94
101
  MArgument_setInteger(Res, res);
95
102
  return LIBRARY_NO_ERROR;
96
103
  }
97
104
 
98
105
  EXTERN_C DLLEXPORT int IndexDimensions(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
99
- size_t res = ((punned_t*)MArgument_getUTF8String(Args[0]))->dimensions();
106
+ std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->dimensions();
100
107
  MArgument_setInteger(Res, res);
101
108
  return LIBRARY_NO_ERROR;
102
109
  }
103
110
 
104
111
  EXTERN_C DLLEXPORT int IndexCapacity(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
105
- size_t res = ((punned_t*)MArgument_getUTF8String(Args[0]))->capacity();
112
+ std::size_t res = ((index_t*)MArgument_getUTF8String(Args[0]))->capacity();
106
113
  MArgument_setInteger(Res, res);
107
114
  return LIBRARY_NO_ERROR;
108
115
  }
109
116
 
110
117
  EXTERN_C DLLEXPORT int IndexAdd(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
111
118
  char* path_cstr = nullptr;
112
- punned_t* c_ptr = (punned_t*)MArgument_getUTF8String(Args[0]);
119
+ index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
113
120
  float* vector_data = nullptr;
114
121
  try {
115
122
  int label = MArgument_getInteger(Args[1]);
116
123
  MTensor tens = MArgument_getMTensor(Args[2]);
117
- size_t len = libData->MTensor_getFlattenedLength(tens);
124
+ std::size_t len = libData->MTensor_getFlattenedLength(tens);
118
125
  vector_data = (float*)libData->MTensor_getRealData(tens);
119
- span_t vector_span = span_t{vector_data, len};
126
+ vector_view_t vector_span = vector_view_t{vector_data, len};
120
127
  c_ptr->add(label, vector_span);
121
128
  } catch (...) {
122
129
  return LIBRARY_FUNCTION_ERROR;
@@ -125,7 +132,7 @@ EXTERN_C DLLEXPORT int IndexAdd(WolframLibraryData libData, mint Argc, MArgument
125
132
  }
126
133
 
127
134
  EXTERN_C DLLEXPORT int IndexSearch(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
128
- punned_t* c_ptr = (punned_t*)MArgument_getUTF8String(Args[0]);
135
+ index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
129
136
  MTensor matches;
130
137
  mint dims[] = {1};
131
138
  int wanted = MArgument_getInteger(Args[2]);
@@ -137,12 +144,12 @@ EXTERN_C DLLEXPORT int IndexSearch(WolframLibraryData libData, mint Argc, MArgum
137
144
  libData->MTensor_new(MType_Integer, 1, dims, &matches);
138
145
 
139
146
  MTensor tens = MArgument_getMTensor(Args[1]);
140
- size_t len = libData->MTensor_getFlattenedLength(tens);
147
+ std::size_t len = libData->MTensor_getFlattenedLength(tens);
141
148
  vector_data = (float*)libData->MTensor_getRealData(tens);
142
- span_t vector_span = span_t{vector_data, len};
149
+ vector_view_t vector_span = vector_view_t{vector_data, len};
143
150
 
144
151
  matches_data = (int*)std::malloc(sizeof(int) * wanted);
145
- found = c_ptr->search(vector_span, (size_t)wanted, matches_data, nullptr);
152
+ found = c_ptr->search(vector_span, static_cast<std::size_t>(wanted), matches_data, nullptr);
146
153
  for (mint i = 0; i < found; i++)
147
154
  libData->MTensor_setInteger(matches, &i, matches_data[i]);
148
155