usearch 0.18.0 → 0.18.1

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.18.0"
3
+ version = "0.18.1"
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/README.md CHANGED
@@ -276,7 +276,7 @@ The difference would be in the shape of the tensors.
276
276
 
277
277
  ```py
278
278
  n = 100
279
- labels = np.array(range(n), dtype=np.longlong)
279
+ labels = np.arange(n)
280
280
  vectors = np.random.uniform(0, 0.3, (n, index.ndim)).astype(np.float32)
281
281
 
282
282
  index.add(labels, vectors, threads=..., copy=...)
@@ -584,7 +584,7 @@ fingerprints = np.vstack([encoder.GetFingerprint(x) for x in molecules])
584
584
  fingerprints = np.packbits(fingerprints, axis=1)
585
585
 
586
586
  index = Index(ndim=2048, metric=MetricKind.BitwiseTanimoto)
587
- labels = np.array(len(molecules), dtype=np.longlong)
587
+ labels = np.arange(len(molecules))
588
588
 
589
589
  index.add(labels, fingerprints)
590
590
  matches = index.search(fingerprints, 10)
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.18.0'
7
+ version = '0.18.1'
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ashot Vardanian",
6
6
  "license": "Apache 2.0",
package/python/lib.cpp CHANGED
@@ -26,7 +26,7 @@ using namespace unum;
26
26
  namespace py = pybind11;
27
27
  using py_shape_t = py::array::ShapeContainer;
28
28
 
29
- using label_t = Py_ssize_t;
29
+ using label_t = std::uint32_t;
30
30
  using distance_t = punned_distance_t;
31
31
  using metric_t = punned_metric_t;
32
32
  using id_t = std::uint32_t;
@@ -132,11 +132,11 @@ static void add_one_to_index(punned_index_py_t& index, label_t label, py::buffer
132
132
  config.store_vector = copy;
133
133
 
134
134
  // https://docs.python.org/3/library/struct.html#format-characters
135
- if (vector_info.format == "B")
135
+ if (vector_info.format == "B" || vector_info.format == "u1" || vector_info.format == "|u1")
136
136
  index.add(label, reinterpret_cast<b1x8_t const*>(vector_data), config).error.raise();
137
- else if (vector_info.format == "c" || vector_info.format == "b")
137
+ else if (vector_info.format == "b" || vector_info.format == "i1" || vector_info.format == "|i1")
138
138
  index.add(label, reinterpret_cast<f8_bits_t const*>(vector_data), config).error.raise();
139
- else if (vector_info.format == "e")
139
+ else if (vector_info.format == "e" || vector_info.format == "f2" || vector_info.format == "<f2")
140
140
  index.add(label, reinterpret_cast<f16_t const*>(vector_data), config).error.raise();
141
141
  else if (vector_info.format == "f" || vector_info.format == "f4" || vector_info.format == "<f4")
142
142
  index.add(label, reinterpret_cast<float const*>(vector_data), config).error.raise();
@@ -156,7 +156,7 @@ static void add_many_to_index( //
156
156
  py::buffer_info labels_info = labels.request();
157
157
  py::buffer_info vectors_info = vectors.request();
158
158
 
159
- if (labels_info.format != py::format_descriptor<label_t>::format())
159
+ if (labels_info.itemsize != sizeof(label_t))
160
160
  throw std::invalid_argument("Incompatible label type!");
161
161
 
162
162
  if (labels_info.ndim != 1)
@@ -181,7 +181,7 @@ static void add_many_to_index( //
181
181
  char const* labels_data = reinterpret_cast<char const*>(labels_info.ptr);
182
182
 
183
183
  // https://docs.python.org/3/library/struct.html#format-characters
184
- if (vectors_info.format == "B")
184
+ if (vectors_info.format == "B" || vectors_info.format == "u1" || vectors_info.format == "|u1")
185
185
  executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
186
186
  add_config_t config;
187
187
  config.store_vector = copy;
@@ -190,7 +190,7 @@ static void add_many_to_index( //
190
190
  b1x8_t const* vector = reinterpret_cast<b1x8_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
191
191
  index.add(label, vector, config).error.raise();
192
192
  });
193
- else if (vectors_info.format == "c" || vectors_info.format == "b")
193
+ else if (vectors_info.format == "b" || vectors_info.format == "i1" || vectors_info.format == "|i1")
194
194
  executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
195
195
  add_config_t config;
196
196
  config.store_vector = copy;
@@ -200,7 +200,7 @@ static void add_many_to_index( //
200
200
  reinterpret_cast<f8_bits_t const*>(vectors_data + task_idx * vectors_info.strides[0]);
201
201
  index.add(label, vector, config).error.raise();
202
202
  });
203
- else if (vectors_info.format == "e")
203
+ else if (vectors_info.format == "e" || vectors_info.format == "f2" || vectors_info.format == "<f2")
204
204
  executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
205
205
  add_config_t config;
206
206
  config.store_vector = copy;
@@ -249,15 +249,15 @@ static py::tuple search_one_in_index(punned_index_py_t& index, py::buffer vector
249
249
  config.exact = exact;
250
250
 
251
251
  // https://docs.python.org/3/library/struct.html#format-characters
252
- if (vector_info.format == "B") {
252
+ if (vector_info.format == "B" || vector_info.format == "u1" || vector_info.format == "|u1") {
253
253
  punned_search_result_t result = index.search(reinterpret_cast<b1x8_t const*>(vector_data), wanted, config);
254
254
  result.error.raise();
255
255
  count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
256
- } else if (vector_info.format == "c" || vector_info.format == "b") {
256
+ } else if (vector_info.format == "b" || vector_info.format == "i1" || vector_info.format == "|i1") {
257
257
  punned_search_result_t result = index.search(reinterpret_cast<f8_bits_t const*>(vector_data), wanted, config);
258
258
  result.error.raise();
259
259
  count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
260
- } else if (vector_info.format == "e") {
260
+ } else if (vector_info.format == "e" || vector_info.format == "f2" || vector_info.format == "<f2") {
261
261
  punned_search_result_t result = index.search(reinterpret_cast<f16_t const*>(vector_data), wanted, config);
262
262
  result.error.raise();
263
263
  count = result.dump_to(&labels_py1d(0), &distances_py1d(0));
@@ -320,7 +320,7 @@ static py::tuple search_many_in_index( //
320
320
  auto counts_py1d = counts_py.template mutable_unchecked<1>();
321
321
 
322
322
  // https://docs.python.org/3/library/struct.html#format-characters
323
- if (vectors_info.format == "B")
323
+ if (vectors_info.format == "B" || vectors_info.format == "u1" || vectors_info.format == "|u1")
324
324
  executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
325
325
  search_config_t config;
326
326
  config.thread = thread_idx;
@@ -331,7 +331,7 @@ static py::tuple search_many_in_index( //
331
331
  counts_py1d(task_idx) =
332
332
  static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
333
333
  });
334
- else if (vectors_info.format == "c" || vectors_info.format == "b")
334
+ else if (vectors_info.format == "b" || vectors_info.format == "i1" || vectors_info.format == "|i1")
335
335
  executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
336
336
  search_config_t config;
337
337
  config.thread = thread_idx;
@@ -342,7 +342,7 @@ static py::tuple search_many_in_index( //
342
342
  counts_py1d(task_idx) =
343
343
  static_cast<Py_ssize_t>(result.dump_to(&labels_py2d(task_idx, 0), &distances_py2d(task_idx, 0)));
344
344
  });
345
- else if (vectors_info.format == "e")
345
+ else if (vectors_info.format == "e" || vectors_info.format == "f2" || vectors_info.format == "<f2")
346
346
  executor_default_t{threads}.execute_bulk(vectors_count, [&](std::size_t thread_idx, std::size_t task_idx) {
347
347
  search_config_t config;
348
348
  config.thread = thread_idx;
@@ -7,7 +7,7 @@ from collections import defaultdict
7
7
  import numpy as np
8
8
 
9
9
  from usearch.io import load_matrix
10
- from usearch.index import Index, Matches, MetricKind, MetricKindBitwise
10
+ from usearch.index import Index, Matches, MetricKind, MetricKindBitwise, Label
11
11
 
12
12
 
13
13
  def random_vectors(
@@ -127,7 +127,7 @@ class Dataset:
127
127
  count = min(
128
128
  d.vectors.shape[0], count) if count is not None else d.vectors.shape[0]
129
129
  d.vectors = d.vectors[:count, :]
130
- d.labels = np.arange(count, dtype=np.longlong)
130
+ d.labels = np.arange(count, dtype=Label)
131
131
 
132
132
  if queries is not None:
133
133
  d.queries = load_matrix(queries)
@@ -150,7 +150,7 @@ class Dataset:
150
150
 
151
151
  d.vectors = random_vectors(count=count, ndim=ndim)
152
152
  d.queries = d.vectors
153
- d.labels = np.arange(count, dtype=np.longlong)
153
+ d.labels = np.arange(count, dtype=Label)
154
154
  d.neighbors = np.reshape(d.labels, (count, 1))
155
155
 
156
156
  return d
@@ -29,7 +29,7 @@ MetricKindBitwise = (
29
29
 
30
30
  SparseIndex = _CompiledSetsIndex
31
31
 
32
- Label = np.longlong
32
+ Label = np.uint32
33
33
 
34
34
 
35
35
  def _normalize_dtype(dtype) -> ScalarKind:
@@ -295,9 +295,9 @@ class Index:
295
295
  else:
296
296
  labels = start_id
297
297
  if isinstance(labels, np.ndarray):
298
- labels = labels.astype(np.longlong)
298
+ labels = labels.astype(Label)
299
299
  elif isinstance(labels, Iterable):
300
- labels = np.array(labels, dtype=np.longlong)
300
+ labels = np.array(labels, dtype=Label)
301
301
 
302
302
  self._compiled.add(labels, vectors, copy=copy, threads=threads)
303
303
 
@@ -7,7 +7,7 @@ import numpy as np
7
7
  from typing import List
8
8
 
9
9
  from ucall.rich_posix import Server
10
- from usearch.index import Index, list_matches
10
+ from usearch.index import Index, Matches, Label
11
11
 
12
12
 
13
13
  def _ascii_to_vector(string: str) -> np.ndarray:
@@ -58,26 +58,25 @@ def serve(
58
58
  @server
59
59
  def add_one(label: int, vector: np.ndarray):
60
60
  print('adding', label, vector)
61
- labels = np.array([label], dtype=np.longlong)
61
+ labels = np.array([label], dtype=Label)
62
62
  vectors = vector.flatten().reshape(vector.shape[0], 1)
63
63
  index.add(labels, vectors)
64
64
 
65
65
  @server
66
66
  def add_many(labels: np.ndarray, vectors: np.ndarray):
67
- labels = labels.astype(np.longlong)
68
67
  index.add(labels, vectors, threads=threads)
69
68
 
70
69
  @server
71
70
  def search_one(vector: np.ndarray, count: int) -> List[dict]:
72
71
  print('search', vector, count)
73
72
  vectors = vector.reshape(vector.shape[0], 1)
74
- results = index.search(vectors, count)
75
- return list_matches(results, 0)
73
+ results: Matches = index.search(vectors, count)
74
+ return results.to_list()
76
75
 
77
76
  @server
78
77
  def search_many(vectors: np.ndarray, count: int) -> List[List[dict]]:
79
- results = index.search(vectors, count)
80
- return [list_matches(results, i) for i in range(vectors.shape[0])]
78
+ results: Matches = index.search(vectors, count)
79
+ return results.to_list()
81
80
 
82
81
  @server
83
82
  def add_ascii(label: int, string: str):