usearch 2.8.7 → 2.8.8
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/CITATION.cff +1 -1
- package/CMakeLists.txt +3 -3
- package/CONTRIBUTING.md +3 -3
- package/Cargo.toml +1 -1
- package/Dockerfile +16 -0
- package/README.md +41 -38
- package/VERSION +1 -0
- package/binding.gyp +57 -8
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +27 -23
- package/package.json +1 -2
- package/setup.py +76 -15
- package/wasmer.toml +1 -1
- package/. npmignore +0 -14
- package/.gitignore +0 -95
- package/.vscode/launch.json +0 -181
- package/.vscode/settings.json +0 -180
- package/.vscode/tasks.json +0 -71
- package/Package.swift +0 -41
- package/csharp/.editorconfig +0 -361
- package/csharp/Cloud.Unum.USearch.sln +0 -34
- package/csharp/DEV_SETUP.md +0 -51
- package/csharp/Directory.Build.props +0 -29
- package/csharp/README.md +0 -74
- package/csharp/build_and_test.cmd +0 -31
- package/csharp/build_and_test.sh +0 -30
- package/csharp/nuget/nuget-package.props +0 -33
- package/csharp/pack.sh +0 -56
- package/csharp/src/Cloud.Unum.USearch.Tests/Cloud.Unum.USearch.Tests.csproj +0 -39
- package/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs +0 -1013
- package/csharp/src/Cloud.Unum.USearch.Tests/Usings.cs +0 -1
- package/docs/_static/custom.css +0 -26
- package/docs/_static/custom.js +0 -7
- package/docs/benchmarks.md +0 -245
- package/docs/benchmarks.rst +0 -5
- package/docs/c/index.rst +0 -11
- package/docs/c/reference.rst +0 -7
- package/docs/conf.dox +0 -2668
- package/docs/conf.py +0 -53
- package/docs/contributing.rst +0 -5
- package/docs/cpp/index.rst +0 -11
- package/docs/cpp/reference.rst +0 -17
- package/docs/csharp/index.rst +0 -6
- package/docs/format.md +0 -86
- package/docs/format.rst +0 -5
- package/docs/golang/index.rst +0 -6
- package/docs/index.rst +0 -33
- package/docs/java/index.rst +0 -11
- package/docs/java/reference.rst +0 -7
- package/docs/javascript/index.rst +0 -11
- package/docs/javascript/reference.rst +0 -12
- package/docs/python/index.rst +0 -11
- package/docs/python/reference.rst +0 -42
- package/docs/rust/index.rst +0 -6
- package/docs/swift/index.rst +0 -6
- package/docs/wolfram/index.rst +0 -6
- package/golang/README.md +0 -95
- package/golang/go.mod +0 -3
- package/golang/lib.go +0 -379
- package/golang/lib_test.go +0 -108
- package/java/README.md +0 -22
- package/java/cloud/unum/usearch/Index.java +0 -337
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +0 -197
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.h +0 -125
- package/java/test/IndexTest.java +0 -30
- package/objc/USearchObjective.mm +0 -299
- package/objc/include/USearchObjective.h +0 -154
- package/python/README.md +0 -292
- package/python/lib.cpp +0 -1216
- package/python/scripts/bench.ipynb +0 -193
- package/python/scripts/bench.py +0 -137
- package/python/scripts/bench_exact.py +0 -44
- package/python/scripts/cluster.ipynb +0 -160
- package/python/scripts/index_faiss.py +0 -100
- package/python/scripts/join.py +0 -155
- package/python/scripts/test_index.py +0 -247
- package/python/scripts/test_jit.py +0 -231
- package/python/scripts/test_sparse.py +0 -32
- package/python/scripts/test_tooling.py +0 -133
- package/python/usearch/__init__.py +0 -0
- package/python/usearch/client.py +0 -124
- package/python/usearch/eval.py +0 -470
- package/python/usearch/index.py +0 -1401
- package/python/usearch/io.py +0 -113
- package/python/usearch/numba.py +0 -121
- package/python/usearch/server.py +0 -137
- package/rust/README.md +0 -66
- package/rust/lib.cpp +0 -136
- package/rust/lib.hpp +0 -69
- package/rust/lib.rs +0 -610
- package/swift/Index+Sugar.swift +0 -153
- package/swift/README.md +0 -48
- package/swift/Test.swift +0 -37
- package/swift/USearch.swift +0 -8
- package/wasm/CMakeLists.txt +0 -7
- package/wasm/README.md +0 -13
- package/wasm/lib.cpp +0 -5
- package/wolfram/CMakeLists.txt +0 -18
- package/wolfram/README.md +0 -13
- package/wolfram/lib.cpp +0 -160
- package/wolfram/test.wls +0 -27
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
import numpy as np
|
|
5
|
-
|
|
6
|
-
from usearch.io import load_matrix, save_matrix
|
|
7
|
-
from usearch.index import search
|
|
8
|
-
from usearch.eval import random_vectors
|
|
9
|
-
|
|
10
|
-
from usearch.index import Match, Matches, BatchMatches, Index, Indexes
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
dimensions = [3, 97, 256]
|
|
14
|
-
batch_sizes = [1, 77, 100]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@pytest.mark.parametrize("rows", batch_sizes)
|
|
18
|
-
@pytest.mark.parametrize("cols", dimensions)
|
|
19
|
-
def test_serializing_fbin_matrix(rows: int, cols: int):
|
|
20
|
-
"""
|
|
21
|
-
Test the serialization of floating point binary matrix.
|
|
22
|
-
|
|
23
|
-
:param int rows: The number of rows in the matrix.
|
|
24
|
-
:param int cols: The number of columns in the matrix.
|
|
25
|
-
"""
|
|
26
|
-
original = np.random.rand(rows, cols).astype(np.float32)
|
|
27
|
-
save_matrix(original, "tmp.fbin")
|
|
28
|
-
reconstructed = load_matrix("tmp.fbin")
|
|
29
|
-
assert np.allclose(original, reconstructed)
|
|
30
|
-
os.remove("tmp.fbin")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@pytest.mark.parametrize("rows", batch_sizes)
|
|
34
|
-
@pytest.mark.parametrize("cols", dimensions)
|
|
35
|
-
def test_serializing_ibin_matrix(rows: int, cols: int):
|
|
36
|
-
"""
|
|
37
|
-
Test the serialization of integer binary matrix.
|
|
38
|
-
|
|
39
|
-
:param int rows: The number of rows in the matrix.
|
|
40
|
-
:param int cols: The number of columns in the matrix.
|
|
41
|
-
"""
|
|
42
|
-
original = np.random.randint(0, rows + 1, size=(rows, cols)).astype(np.int32)
|
|
43
|
-
save_matrix(original, "tmp.ibin")
|
|
44
|
-
reconstructed = load_matrix("tmp.ibin")
|
|
45
|
-
assert np.allclose(original, reconstructed)
|
|
46
|
-
os.remove("tmp.ibin")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@pytest.mark.parametrize("rows", batch_sizes)
|
|
50
|
-
@pytest.mark.parametrize("cols", dimensions)
|
|
51
|
-
@pytest.mark.parametrize("k", [1, 5])
|
|
52
|
-
@pytest.mark.parametrize("reordered", [False, True])
|
|
53
|
-
def test_exact_search(rows: int, cols: int, k: int, reordered: bool):
|
|
54
|
-
"""
|
|
55
|
-
Test exact search.
|
|
56
|
-
|
|
57
|
-
:param int rows: The number of rows in the matrix.
|
|
58
|
-
:param int cols: The number of columns in the matrix.
|
|
59
|
-
"""
|
|
60
|
-
original = np.random.rand(rows, cols)
|
|
61
|
-
keys = np.arange(rows)
|
|
62
|
-
k = min(k, rows)
|
|
63
|
-
|
|
64
|
-
if reordered:
|
|
65
|
-
reordered_keys = np.arange(rows)
|
|
66
|
-
np.random.shuffle(reordered_keys)
|
|
67
|
-
else:
|
|
68
|
-
reordered_keys = keys
|
|
69
|
-
|
|
70
|
-
matches: BatchMatches = search(original, original[reordered_keys], k, exact=True)
|
|
71
|
-
top_matches = (
|
|
72
|
-
[int(m.keys[0]) for m in matches] if rows > 1 else [int(matches.keys[0])]
|
|
73
|
-
)
|
|
74
|
-
assert top_matches == list(reordered_keys)
|
|
75
|
-
|
|
76
|
-
matches: Matches = search(original, original[-1], k, exact=True)
|
|
77
|
-
top_match = int(matches.keys[0])
|
|
78
|
-
assert top_match == keys[-1]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def test_matches_creation_and_methods():
|
|
82
|
-
matches = Matches(
|
|
83
|
-
keys=np.array([1, 2]),
|
|
84
|
-
distances=np.array([0.5, 0.6]),
|
|
85
|
-
visited_members=2,
|
|
86
|
-
computed_distances=2,
|
|
87
|
-
)
|
|
88
|
-
assert len(matches) == 2
|
|
89
|
-
assert matches[0] == Match(key=1, distance=0.5)
|
|
90
|
-
assert matches.to_list() == [(1, 0.5), (2, 0.6)]
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def test_batch_matches_creation_and_methods():
|
|
94
|
-
keys = np.array([[1, 2], [3, 4]])
|
|
95
|
-
distances = np.array([[0.5, 0.6], [0.7, 0.8]])
|
|
96
|
-
counts = np.array([2, 2])
|
|
97
|
-
batch_matches = BatchMatches(
|
|
98
|
-
keys=keys,
|
|
99
|
-
distances=distances,
|
|
100
|
-
counts=counts,
|
|
101
|
-
visited_members=2,
|
|
102
|
-
computed_distances=2,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
assert len(batch_matches) == 2
|
|
106
|
-
assert batch_matches[0].keys.tolist() == [1, 2]
|
|
107
|
-
assert batch_matches[0].distances.tolist() == [0.5, 0.6]
|
|
108
|
-
assert batch_matches.to_list() == [(1, 0.5), (2, 0.6), (3, 0.7), (4, 0.8)]
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def test_multi_index():
|
|
112
|
-
ndim = 10
|
|
113
|
-
index_a = Index(ndim=ndim)
|
|
114
|
-
index_b = Index(ndim=ndim)
|
|
115
|
-
|
|
116
|
-
vectors = random_vectors(count=3, ndim=ndim)
|
|
117
|
-
index_a.add(42, vectors[0])
|
|
118
|
-
index_b.add(43, vectors[1])
|
|
119
|
-
|
|
120
|
-
indexes = Indexes([index_a, index_b])
|
|
121
|
-
|
|
122
|
-
# Search top 10 for 1
|
|
123
|
-
matches = indexes.search(vectors[2], 10)
|
|
124
|
-
assert len(matches) == 2
|
|
125
|
-
|
|
126
|
-
# Search top 1 for 1
|
|
127
|
-
matches = indexes.search(vectors[2], 1)
|
|
128
|
-
assert len(matches) == 1
|
|
129
|
-
|
|
130
|
-
# Search top 10 for 3
|
|
131
|
-
matches = indexes.search(vectors, 10)
|
|
132
|
-
assert len(matches) == 3
|
|
133
|
-
assert len(matches[0].keys) == 2
|
|
File without changes
|
package/python/usearch/client.py
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
from typing import Union, Optional, List
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
from ucall.client import Client
|
|
5
|
-
|
|
6
|
-
from usearch.index import Matches
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def _vector_to_ascii(vector: np.ndarray) -> Optional[str]:
|
|
10
|
-
if vector.dtype != np.int8 and vector.dtype != np.uint8 and vector.dtype != np.byte:
|
|
11
|
-
return None
|
|
12
|
-
if not np.all((vector >= 0) | (vector <= 100)):
|
|
13
|
-
return None
|
|
14
|
-
|
|
15
|
-
# Let's map [0, 100] to the range from [23, 123],
|
|
16
|
-
# poking 60 and replacing with the 124.
|
|
17
|
-
vector += 23
|
|
18
|
-
vector[vector == 60] = 124
|
|
19
|
-
ascii = str(vector)
|
|
20
|
-
return ascii
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class IndexClient:
|
|
24
|
-
def __init__(
|
|
25
|
-
self, uri: str = "127.0.0.1", port: int = 8545, use_http: bool = True
|
|
26
|
-
) -> None:
|
|
27
|
-
self.client = Client(uri=uri, port=port, use_http=use_http)
|
|
28
|
-
|
|
29
|
-
def add_one(self, key: int, vector: np.ndarray):
|
|
30
|
-
assert isinstance(key, int)
|
|
31
|
-
assert isinstance(vector, np.ndarray)
|
|
32
|
-
vector = vector.flatten()
|
|
33
|
-
ascii = _vector_to_ascii(vector)
|
|
34
|
-
if ascii:
|
|
35
|
-
self.client.add_ascii(key=key, string=ascii)
|
|
36
|
-
else:
|
|
37
|
-
self.client.add_one(key=key, vectors=vector)
|
|
38
|
-
|
|
39
|
-
def add_many(self, keys: np.ndarray, vectors: np.ndarray):
|
|
40
|
-
assert isinstance(keys, int)
|
|
41
|
-
assert isinstance(vectors, np.ndarray)
|
|
42
|
-
assert keys.ndim == 1 and vectors.ndim == 2
|
|
43
|
-
assert keys.shape[0] == vectors.shape[0]
|
|
44
|
-
self.client.add_many(keys=keys, vectors=vectors)
|
|
45
|
-
|
|
46
|
-
def add(self, keys: Union[np.ndarray, int], vectors: np.ndarray):
|
|
47
|
-
if isinstance(keys, int) or len(keys) == 1:
|
|
48
|
-
return self.add_one(keys, vectors)
|
|
49
|
-
else:
|
|
50
|
-
return self.add_many(keys, vectors)
|
|
51
|
-
|
|
52
|
-
def search_one(self, vector: np.ndarray, count: int) -> Matches:
|
|
53
|
-
matches: List[dict] = []
|
|
54
|
-
vector = vector.flatten()
|
|
55
|
-
ascii = _vector_to_ascii(vector)
|
|
56
|
-
if ascii:
|
|
57
|
-
matches = self.client.search_ascii(string=ascii, count=count)
|
|
58
|
-
else:
|
|
59
|
-
matches = self.client.search_one(vector=vector, count=count)
|
|
60
|
-
|
|
61
|
-
print(matches.data)
|
|
62
|
-
matches = matches.json
|
|
63
|
-
|
|
64
|
-
keys = np.array((1, count), dtype=np.uint32)
|
|
65
|
-
distances = np.array((1, count), dtype=np.float32)
|
|
66
|
-
counts = np.array((1), dtype=np.uint32)
|
|
67
|
-
for col, result in enumerate(matches):
|
|
68
|
-
keys[0, col] = result["key"]
|
|
69
|
-
distances[0, col] = result["distance"]
|
|
70
|
-
counts[0] = len(matches)
|
|
71
|
-
|
|
72
|
-
return keys, distances, counts
|
|
73
|
-
|
|
74
|
-
def search_many(self, vectors: np.ndarray, count: int) -> Matches:
|
|
75
|
-
batch_size: int = vectors.shape[0]
|
|
76
|
-
list_of_matches: List[List[dict]] = self.client.search_many(
|
|
77
|
-
vectors=vectors, count=count
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
keys = np.array((batch_size, count), dtype=np.uint32)
|
|
81
|
-
distances = np.array((batch_size, count), dtype=np.float32)
|
|
82
|
-
counts = np.array((batch_size), dtype=np.uint32)
|
|
83
|
-
for row, matches in enumerate(list_of_matches):
|
|
84
|
-
for col, result in enumerate(matches):
|
|
85
|
-
keys[row, col] = result["key"]
|
|
86
|
-
distances[row, col] = result["distance"]
|
|
87
|
-
counts[row] = len(results)
|
|
88
|
-
|
|
89
|
-
return keys, distances, counts
|
|
90
|
-
|
|
91
|
-
def search(self, vectors: np.ndarray, count: int) -> Matches:
|
|
92
|
-
if vectors.ndim == 1 or (vectors.ndim == 2 and vectors.shape[0] == 1):
|
|
93
|
-
return self.search_one(vectors, count)
|
|
94
|
-
else:
|
|
95
|
-
return self.search_many(vectors, count)
|
|
96
|
-
|
|
97
|
-
def __len__(self):
|
|
98
|
-
return self.client.size().json()
|
|
99
|
-
|
|
100
|
-
@property
|
|
101
|
-
def ndim(self):
|
|
102
|
-
return self.client.ndim().json()
|
|
103
|
-
|
|
104
|
-
def capacity(self):
|
|
105
|
-
return self.client.capacity().json()
|
|
106
|
-
|
|
107
|
-
def connectivity(self):
|
|
108
|
-
return self.client.connectivity().json()
|
|
109
|
-
|
|
110
|
-
def load(self, path: str):
|
|
111
|
-
raise NotImplementedError()
|
|
112
|
-
|
|
113
|
-
def view(self, path: str):
|
|
114
|
-
raise NotImplementedError()
|
|
115
|
-
|
|
116
|
-
def save(self, path: str):
|
|
117
|
-
raise NotImplementedError()
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if __name__ == "__main__":
|
|
121
|
-
index = IndexClient()
|
|
122
|
-
index.add(42, np.array([0.4] * 256, dtype=np.float32))
|
|
123
|
-
results = index.search(np.array([0.4] * 256, dtype=np.float32), 10)
|
|
124
|
-
print(results)
|