usearch 2.8.7 → 2.8.9
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 +26 -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 +28 -24
- package/javascript/README.md +64 -12
- package/javascript/lib.cpp +11 -3
- package/javascript/usearch.js +4 -2
- package/javascript/usearch.test.js +15 -0
- package/package.json +1 -2
- package/setup.py +76 -15
- package/simsimd/README.md +14 -13
- package/simsimd/include/simsimd/probability.h +2 -2
- package/simsimd/package.json +1 -1
- 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/simsimd/CMakeLists.txt +0 -67
- package/simsimd/cpp/bench.cxx +0 -206
- package/simsimd/package-ci.json +0 -10
- package/simsimd/setup.py +0 -86
- 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,193 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cells": [
|
|
3
|
-
{
|
|
4
|
-
"cell_type": "code",
|
|
5
|
-
"execution_count": null,
|
|
6
|
-
"metadata": {},
|
|
7
|
-
"outputs": [],
|
|
8
|
-
"source": [
|
|
9
|
-
"!pip install faiss-cpu numba pandas plotly nbformat jinja2 kaleido tqdm scikit-learn scipy matplotlib"
|
|
10
|
-
]
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"cell_type": "code",
|
|
14
|
-
"execution_count": 1,
|
|
15
|
-
"metadata": {},
|
|
16
|
-
"outputs": [],
|
|
17
|
-
"source": [
|
|
18
|
-
"from bench import *\n",
|
|
19
|
-
"import plotly.graph_objects as go"
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"cell_type": "code",
|
|
24
|
-
"execution_count": 6,
|
|
25
|
-
"metadata": {},
|
|
26
|
-
"outputs": [
|
|
27
|
-
{
|
|
28
|
-
"data": {
|
|
29
|
-
"text/plain": [
|
|
30
|
-
"('auto', 'auto', 'auto')"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
"execution_count": 6,
|
|
34
|
-
"metadata": {},
|
|
35
|
-
"output_type": "execute_result"
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
"source": [
|
|
39
|
-
"from usearch.index import Index\n",
|
|
40
|
-
"\n",
|
|
41
|
-
"Index(ndim=256, metric=\"cos\", dtype=\"f32\").hardware_acceleration, Index(ndim=256, metric=\"cos\", dtype=\"f16\").hardware_acceleration, Index(ndim=256, metric=\"cos\", dtype=\"i8\").hardware_acceleration"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"cell_type": "code",
|
|
46
|
-
"execution_count": null,
|
|
47
|
-
"metadata": {},
|
|
48
|
-
"outputs": [],
|
|
49
|
-
"source": [
|
|
50
|
-
"def speed_chart(df, title):\n",
|
|
51
|
-
" subtitles = [\n",
|
|
52
|
-
" f'{n}, recall@1 ~{r*100:.2f}%'\n",
|
|
53
|
-
" for n, r in zip(df['names'], df['recall_at_one'])\n",
|
|
54
|
-
" ]\n",
|
|
55
|
-
" fig = go.Figure(\n",
|
|
56
|
-
" data=[\n",
|
|
57
|
-
" go.Bar(name='Add', x=subtitles, y=df['add_per_second']),\n",
|
|
58
|
-
" go.Bar(name='Search', x=subtitles, y=df['search_per_second']),\n",
|
|
59
|
-
" ])\n",
|
|
60
|
-
" fig.update_layout(\n",
|
|
61
|
-
" width=1000,\n",
|
|
62
|
-
" height=1000,\n",
|
|
63
|
-
" barmode='group',\n",
|
|
64
|
-
" title=dict(\n",
|
|
65
|
-
" text=title,\n",
|
|
66
|
-
" ),\n",
|
|
67
|
-
" legend=dict(\n",
|
|
68
|
-
" orientation='h',\n",
|
|
69
|
-
" entrywidth=70,\n",
|
|
70
|
-
" yanchor='bottom',\n",
|
|
71
|
-
" y=1.02,\n",
|
|
72
|
-
" xanchor='right',\n",
|
|
73
|
-
" x=1,\n",
|
|
74
|
-
" ),\n",
|
|
75
|
-
" )\n",
|
|
76
|
-
" fig.write_image(title + '.png')\n",
|
|
77
|
-
" return fig"
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"cell_type": "code",
|
|
82
|
-
"execution_count": null,
|
|
83
|
-
"metadata": {},
|
|
84
|
-
"outputs": [],
|
|
85
|
-
"source": [
|
|
86
|
-
"from usearch.eval import Dataset\n",
|
|
87
|
-
"data = Dataset.build(\n",
|
|
88
|
-
" vectors='../../datasets/wiki_1M/base.1M.fbin',\n",
|
|
89
|
-
" queries='../../datasets/wiki_1M/query.public.100K.fbin',\n",
|
|
90
|
-
" neighbors='../../datasets/wiki_1M/groundtruth.public.100K.ibin',\n",
|
|
91
|
-
")"
|
|
92
|
-
]
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"cell_type": "code",
|
|
96
|
-
"execution_count": null,
|
|
97
|
-
"metadata": {},
|
|
98
|
-
"outputs": [],
|
|
99
|
-
"source": [
|
|
100
|
-
"eval_default = Evaluation.for_dataset(data)\n",
|
|
101
|
-
"results_default = bench_speed(eval_default, jit=True)\n",
|
|
102
|
-
"results_default"
|
|
103
|
-
]
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
"cell_type": "code",
|
|
107
|
-
"execution_count": null,
|
|
108
|
-
"metadata": {},
|
|
109
|
-
"outputs": [],
|
|
110
|
-
"source": [
|
|
111
|
-
"speed_chart(results_default, 'USearch vs FAISS Speed')"
|
|
112
|
-
]
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
"cell_type": "code",
|
|
116
|
-
"execution_count": null,
|
|
117
|
-
"metadata": {},
|
|
118
|
-
"outputs": [],
|
|
119
|
-
"source": [
|
|
120
|
-
"eval_sliced = Evaluation.for_dataset(data, batch_size=256)\n",
|
|
121
|
-
"results_sliced = bench_speed(eval_sliced, jit=True)\n",
|
|
122
|
-
"results_sliced"
|
|
123
|
-
]
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
"cell_type": "code",
|
|
127
|
-
"execution_count": null,
|
|
128
|
-
"metadata": {},
|
|
129
|
-
"outputs": [],
|
|
130
|
-
"source": [
|
|
131
|
-
"speed_chart(results_sliced, 'USearch vs FAISS Speed with 256 Batch Size')"
|
|
132
|
-
]
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
"cell_type": "code",
|
|
136
|
-
"execution_count": null,
|
|
137
|
-
"metadata": {},
|
|
138
|
-
"outputs": [],
|
|
139
|
-
"source": [
|
|
140
|
-
"eval_clustered = Evaluation.for_dataset(data, clusters=10_000)\n",
|
|
141
|
-
"len(eval_clustered.tasks)"
|
|
142
|
-
]
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
"cell_type": "code",
|
|
146
|
-
"execution_count": null,
|
|
147
|
-
"metadata": {},
|
|
148
|
-
"outputs": [],
|
|
149
|
-
"source": [
|
|
150
|
-
"results_clustered = bench_speed(eval_clustered)\n",
|
|
151
|
-
"results_clustered"
|
|
152
|
-
]
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
"cell_type": "code",
|
|
156
|
-
"execution_count": null,
|
|
157
|
-
"metadata": {},
|
|
158
|
-
"outputs": [],
|
|
159
|
-
"source": [
|
|
160
|
-
"speed_chart(results_clustered, 'USearch vs FAISS Speed on Clustered Data')"
|
|
161
|
-
]
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"cell_type": "code",
|
|
165
|
-
"execution_count": null,
|
|
166
|
-
"metadata": {},
|
|
167
|
-
"outputs": [],
|
|
168
|
-
"source": []
|
|
169
|
-
}
|
|
170
|
-
],
|
|
171
|
-
"metadata": {
|
|
172
|
-
"kernelspec": {
|
|
173
|
-
"display_name": "usearch-test",
|
|
174
|
-
"language": "python",
|
|
175
|
-
"name": "python3"
|
|
176
|
-
},
|
|
177
|
-
"language_info": {
|
|
178
|
-
"codemirror_mode": {
|
|
179
|
-
"name": "ipython",
|
|
180
|
-
"version": 3
|
|
181
|
-
},
|
|
182
|
-
"file_extension": ".py",
|
|
183
|
-
"mimetype": "text/x-python",
|
|
184
|
-
"name": "python",
|
|
185
|
-
"nbconvert_exporter": "python",
|
|
186
|
-
"pygments_lexer": "ipython3",
|
|
187
|
-
"version": "3.11.5"
|
|
188
|
-
},
|
|
189
|
-
"orig_nbformat": 4
|
|
190
|
-
},
|
|
191
|
-
"nbformat": 4,
|
|
192
|
-
"nbformat_minor": 2
|
|
193
|
-
}
|
package/python/scripts/bench.py
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import itertools
|
|
2
|
-
from typing import List
|
|
3
|
-
from dataclasses import asdict
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
import pandas as pd
|
|
7
|
-
|
|
8
|
-
from usearch.index import Index, Key, MetricKind, ScalarKind
|
|
9
|
-
from usearch.numba import jit as njit
|
|
10
|
-
from usearch.eval import Evaluation, AddTask
|
|
11
|
-
from usearch.index import (
|
|
12
|
-
DEFAULT_CONNECTIVITY,
|
|
13
|
-
DEFAULT_EXPANSION_ADD,
|
|
14
|
-
DEFAULT_EXPANSION_SEARCH,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def bench_speed(
|
|
19
|
-
eval: Evaluation,
|
|
20
|
-
connectivity: int = DEFAULT_CONNECTIVITY,
|
|
21
|
-
expansion_add: int = DEFAULT_EXPANSION_ADD,
|
|
22
|
-
expansion_search: int = DEFAULT_EXPANSION_SEARCH,
|
|
23
|
-
jit: bool = False,
|
|
24
|
-
train: bool = False,
|
|
25
|
-
) -> pd.DataFrame:
|
|
26
|
-
# Build various indexes:
|
|
27
|
-
indexes = []
|
|
28
|
-
jit_options = [False, True] if jit else [False]
|
|
29
|
-
dtype_options = [ScalarKind.F32, ScalarKind.F16, ScalarKind.I8]
|
|
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
|
-
index = Index(
|
|
35
|
-
ndim=eval.ndim,
|
|
36
|
-
metric=metric,
|
|
37
|
-
dtype=dtype,
|
|
38
|
-
expansion_add=expansion_add,
|
|
39
|
-
expansion_search=expansion_search,
|
|
40
|
-
connectivity=connectivity,
|
|
41
|
-
path="USearch" + ["", "+JIT"][jit] + ":" + str(dtype),
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# Skip the cases, where JIT-ing is impossible
|
|
45
|
-
if jit and not index.jit:
|
|
46
|
-
continue
|
|
47
|
-
indexes.append(index)
|
|
48
|
-
|
|
49
|
-
# Add FAISS indexes to the mix:
|
|
50
|
-
try:
|
|
51
|
-
from index_faiss import IndexFAISS, IndexQuantizedFAISS
|
|
52
|
-
|
|
53
|
-
indexes.append(
|
|
54
|
-
IndexFAISS(
|
|
55
|
-
ndim=eval.ndim,
|
|
56
|
-
expansion_add=expansion_add,
|
|
57
|
-
expansion_search=expansion_search,
|
|
58
|
-
connectivity=connectivity,
|
|
59
|
-
path="FAISS:f32",
|
|
60
|
-
)
|
|
61
|
-
)
|
|
62
|
-
if train:
|
|
63
|
-
indexes.append(
|
|
64
|
-
IndexQuantizedFAISS(
|
|
65
|
-
train=eval.tasks[0].vectors,
|
|
66
|
-
expansion_add=expansion_add,
|
|
67
|
-
expansion_search=expansion_search,
|
|
68
|
-
connectivity=connectivity,
|
|
69
|
-
path="FAISS+IVFPQ:f32",
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
|
-
except (ImportError, ModuleNotFoundError):
|
|
73
|
-
pass
|
|
74
|
-
|
|
75
|
-
# Time to evaluate:
|
|
76
|
-
results = [eval(index) for index in indexes]
|
|
77
|
-
return pd.DataFrame(
|
|
78
|
-
{
|
|
79
|
-
"names": [i.path for i in indexes],
|
|
80
|
-
"add_per_second": [x["add_per_second"] for x in results],
|
|
81
|
-
"search_per_second": [x["search_per_second"] for x in results],
|
|
82
|
-
"recall_at_one": [x["recall_at_one"] for x in results],
|
|
83
|
-
}
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def bench_params(
|
|
88
|
-
count: int = 1_000_000,
|
|
89
|
-
connectivities: int = range(10, 20),
|
|
90
|
-
dimensions: List[int] = [
|
|
91
|
-
2,
|
|
92
|
-
3,
|
|
93
|
-
4,
|
|
94
|
-
8,
|
|
95
|
-
16,
|
|
96
|
-
32,
|
|
97
|
-
96,
|
|
98
|
-
100,
|
|
99
|
-
256,
|
|
100
|
-
384,
|
|
101
|
-
512,
|
|
102
|
-
768,
|
|
103
|
-
1024,
|
|
104
|
-
1536,
|
|
105
|
-
],
|
|
106
|
-
expansion_add: int = DEFAULT_EXPANSION_ADD,
|
|
107
|
-
expansion_search: int = DEFAULT_EXPANSION_SEARCH,
|
|
108
|
-
) -> pd.DataFrame:
|
|
109
|
-
"""Measures indexing speed for different dimensionality vectors.
|
|
110
|
-
|
|
111
|
-
:param count: Number of vectors, defaults to 1_000_000
|
|
112
|
-
:type count: int, optional
|
|
113
|
-
"""
|
|
114
|
-
|
|
115
|
-
results = []
|
|
116
|
-
for connectivity, ndim in itertools.product(connectivities, dimensions):
|
|
117
|
-
task = AddTask(
|
|
118
|
-
keys=np.arange(count, dtype=Key),
|
|
119
|
-
vectors=np.random.rand(count, ndim).astype(np.float32),
|
|
120
|
-
)
|
|
121
|
-
index = Index(
|
|
122
|
-
ndim=ndim,
|
|
123
|
-
connectivity=connectivity,
|
|
124
|
-
expansion_add=expansion_add,
|
|
125
|
-
expansion_search=expansion_search,
|
|
126
|
-
)
|
|
127
|
-
result = asdict(task(index))
|
|
128
|
-
result["ndim"] = dimensions
|
|
129
|
-
result["connectivity"] = connectivity
|
|
130
|
-
results.append(result)
|
|
131
|
-
|
|
132
|
-
# return self._execute_tasks(
|
|
133
|
-
# tasks,
|
|
134
|
-
# title='HNSW Indexing Speed vs Vector Dimensions',
|
|
135
|
-
# x='ndim', y='add_per_second', log_x=True,
|
|
136
|
-
# )
|
|
137
|
-
return pd.DataFrame(results)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
from time import time
|
|
2
|
-
|
|
3
|
-
from usearch.index import search, MetricKind, ScalarKind
|
|
4
|
-
from usearch.compiled import hardware_acceleration
|
|
5
|
-
from faiss import knn, METRIC_L2
|
|
6
|
-
import numpy as np
|
|
7
|
-
import fire
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def run(
|
|
11
|
-
n: int = 10**5,
|
|
12
|
-
q: int = 10,
|
|
13
|
-
k: int = 100,
|
|
14
|
-
ndim: int = 256,
|
|
15
|
-
half: bool = False,
|
|
16
|
-
):
|
|
17
|
-
usearch_dtype = ScalarKind.F16 if half else ScalarKind.F32
|
|
18
|
-
acceleration = hardware_acceleration(
|
|
19
|
-
dtype=usearch_dtype,
|
|
20
|
-
ndim=ndim,
|
|
21
|
-
metric_kind=MetricKind.L2sq,
|
|
22
|
-
)
|
|
23
|
-
print("Hardware acceleration in USearch: ", acceleration)
|
|
24
|
-
|
|
25
|
-
x = np.random.random((n, ndim))
|
|
26
|
-
x = x.astype(np.float16) if half else x.astype(np.float32)
|
|
27
|
-
|
|
28
|
-
start = time()
|
|
29
|
-
_ = search(
|
|
30
|
-
x,
|
|
31
|
-
x[:q],
|
|
32
|
-
k,
|
|
33
|
-
MetricKind.L2sq,
|
|
34
|
-
exact=True,
|
|
35
|
-
).keys
|
|
36
|
-
print("USearch: ", time() - start)
|
|
37
|
-
|
|
38
|
-
start = time()
|
|
39
|
-
_ = knn(x[:q], x, k, metric=METRIC_L2)[1]
|
|
40
|
-
print("FAISS: ", time() - start)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if __name__ == "__main__":
|
|
44
|
-
fire.Fire(run)
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cells": [
|
|
3
|
-
{
|
|
4
|
-
"cell_type": "code",
|
|
5
|
-
"execution_count": null,
|
|
6
|
-
"metadata": {},
|
|
7
|
-
"outputs": [],
|
|
8
|
-
"source": [
|
|
9
|
-
"from usearch.io import load_matrix\n",
|
|
10
|
-
"import numpy as np"
|
|
11
|
-
]
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"cell_type": "code",
|
|
15
|
-
"execution_count": null,
|
|
16
|
-
"metadata": {},
|
|
17
|
-
"outputs": [],
|
|
18
|
-
"source": [
|
|
19
|
-
"vectors = load_matrix(\"datasets/arxiv_2M/abstract.e5-base-v2.fbin\", view=True)"
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"cell_type": "code",
|
|
24
|
-
"execution_count": null,
|
|
25
|
-
"metadata": {},
|
|
26
|
-
"outputs": [],
|
|
27
|
-
"source": [
|
|
28
|
-
"vectors.shape"
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"cell_type": "code",
|
|
33
|
-
"execution_count": null,
|
|
34
|
-
"metadata": {},
|
|
35
|
-
"outputs": [],
|
|
36
|
-
"source": [
|
|
37
|
-
"from usearch.index import Index, USES_SIMSIMD, USES_NATIVE_F16\n",
|
|
38
|
-
"\n",
|
|
39
|
-
"index = Index(ndim=vectors.shape[1], metric=\"cos\", dtype=\"i8\")\n",
|
|
40
|
-
"index.hardware_acceleration, USES_SIMSIMD, USES_NATIVE_F16"
|
|
41
|
-
]
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"cell_type": "code",
|
|
45
|
-
"execution_count": null,
|
|
46
|
-
"metadata": {},
|
|
47
|
-
"outputs": [],
|
|
48
|
-
"source": [
|
|
49
|
-
"\n",
|
|
50
|
-
"import os\n",
|
|
51
|
-
"\n",
|
|
52
|
-
"if os.path.exists(\"cluster.i8.usearch\"):\n",
|
|
53
|
-
" index.load(\"cluster.i8.usearch\")\n",
|
|
54
|
-
"\n",
|
|
55
|
-
"if len(index) == 0:\n",
|
|
56
|
-
" index.add(None, vectors, log=True)\n",
|
|
57
|
-
" index.save(\"cluster.i8.usearch\")"
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"cell_type": "code",
|
|
62
|
-
"execution_count": null,
|
|
63
|
-
"metadata": {},
|
|
64
|
-
"outputs": [],
|
|
65
|
-
"source": [
|
|
66
|
-
"index"
|
|
67
|
-
]
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"cell_type": "code",
|
|
71
|
-
"execution_count": null,
|
|
72
|
-
"metadata": {},
|
|
73
|
-
"outputs": [],
|
|
74
|
-
"source": [
|
|
75
|
-
"clustering = index.cluster(min_count=10, max_count=15, threads=60)\n",
|
|
76
|
-
"clustering"
|
|
77
|
-
]
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"cell_type": "code",
|
|
81
|
-
"execution_count": null,
|
|
82
|
-
"metadata": {},
|
|
83
|
-
"outputs": [],
|
|
84
|
-
"source": [
|
|
85
|
-
"print(f\"{len(clustering.centroids_popularity[0])} unique clusters for {len(index)} members\")"
|
|
86
|
-
]
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"cell_type": "code",
|
|
90
|
-
"execution_count": null,
|
|
91
|
-
"metadata": {},
|
|
92
|
-
"outputs": [],
|
|
93
|
-
"source": [
|
|
94
|
-
"clustering.plot_centroids_popularity()"
|
|
95
|
-
]
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"cell_type": "code",
|
|
99
|
-
"execution_count": null,
|
|
100
|
-
"metadata": {},
|
|
101
|
-
"outputs": [],
|
|
102
|
-
"source": [
|
|
103
|
-
"nx.draw_networkx(g)"
|
|
104
|
-
]
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"cell_type": "code",
|
|
108
|
-
"execution_count": null,
|
|
109
|
-
"metadata": {},
|
|
110
|
-
"outputs": [],
|
|
111
|
-
"source": [
|
|
112
|
-
"import matplotlib.pyplot as plt\n",
|
|
113
|
-
"import networkx as nx\n",
|
|
114
|
-
"\n",
|
|
115
|
-
"G = g\n",
|
|
116
|
-
"\n",
|
|
117
|
-
"node_sizes = nx.get_node_attributes(G, \"size\").values()\n",
|
|
118
|
-
"max_node_size = max(node_sizes)\n",
|
|
119
|
-
"node_sizes = [size * 10 / max_node_size for size in node_sizes]\n",
|
|
120
|
-
"\n",
|
|
121
|
-
"edge_labels = nx.get_edge_attributes(G, \"distance\")\n",
|
|
122
|
-
"edge_labels = {edge: f\"{label:.2}\" for edge, label in edge_labels.items()}\n",
|
|
123
|
-
"\n",
|
|
124
|
-
"pos = nx.spring_layout(G, seed=7)\n",
|
|
125
|
-
"nx.draw_networkx_nodes(G, pos, node_size=node_sizes)\n",
|
|
126
|
-
"nx.draw_networkx_edges(G, pos, edgelist=G.edges(data=False))\n",
|
|
127
|
-
"nx.draw_networkx_labels(G, pos, font_size=10, font_family=\"sans-serif\")\n",
|
|
128
|
-
"nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=5)\n",
|
|
129
|
-
"\n",
|
|
130
|
-
"ax = plt.gca()\n",
|
|
131
|
-
"ax.margins(0.08)\n",
|
|
132
|
-
"plt.axis(\"off\")\n",
|
|
133
|
-
"plt.tight_layout()\n",
|
|
134
|
-
"plt.show()"
|
|
135
|
-
]
|
|
136
|
-
}
|
|
137
|
-
],
|
|
138
|
-
"metadata": {
|
|
139
|
-
"kernelspec": {
|
|
140
|
-
"display_name": "base",
|
|
141
|
-
"language": "python",
|
|
142
|
-
"name": "python3"
|
|
143
|
-
},
|
|
144
|
-
"language_info": {
|
|
145
|
-
"codemirror_mode": {
|
|
146
|
-
"name": "ipython",
|
|
147
|
-
"version": 3
|
|
148
|
-
},
|
|
149
|
-
"file_extension": ".py",
|
|
150
|
-
"mimetype": "text/x-python",
|
|
151
|
-
"name": "python",
|
|
152
|
-
"nbconvert_exporter": "python",
|
|
153
|
-
"pygments_lexer": "ipython3",
|
|
154
|
-
"version": "3.10.11"
|
|
155
|
-
},
|
|
156
|
-
"orig_nbformat": 4
|
|
157
|
-
},
|
|
158
|
-
"nbformat": 4,
|
|
159
|
-
"nbformat_minor": 2
|
|
160
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import Optional
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
from faiss import IndexHNSWFlat, IndexIVFPQ, read_index
|
|
6
|
-
|
|
7
|
-
from usearch.index import Matches, BatchMatches
|
|
8
|
-
from usearch.index import (
|
|
9
|
-
DEFAULT_CONNECTIVITY,
|
|
10
|
-
DEFAULT_EXPANSION_ADD,
|
|
11
|
-
DEFAULT_EXPANSION_SEARCH,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class IndexFAISS:
|
|
16
|
-
def __init__(
|
|
17
|
-
self,
|
|
18
|
-
index: IndexHNSWFlat = None,
|
|
19
|
-
ndim: int = 0,
|
|
20
|
-
connectivity: int = DEFAULT_CONNECTIVITY,
|
|
21
|
-
expansion_add: int = DEFAULT_EXPANSION_ADD,
|
|
22
|
-
expansion_search: int = DEFAULT_EXPANSION_SEARCH,
|
|
23
|
-
path: Optional[os.PathLike] = None,
|
|
24
|
-
*args,
|
|
25
|
-
**kwargs,
|
|
26
|
-
):
|
|
27
|
-
if index is None:
|
|
28
|
-
index = IndexHNSWFlat(ndim, connectivity)
|
|
29
|
-
index.hnsw.efConstruction = expansion_add
|
|
30
|
-
index.hnsw.efSearch = expansion_search
|
|
31
|
-
|
|
32
|
-
self._faiss = index
|
|
33
|
-
self._specs = {
|
|
34
|
-
"Class": "usearch.IndexFAISS",
|
|
35
|
-
"Dimensions": ndim,
|
|
36
|
-
"Connectivity": connectivity,
|
|
37
|
-
"Expansion@Add": expansion_add,
|
|
38
|
-
"Expansion@Search": expansion_search,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
self.path = path
|
|
42
|
-
|
|
43
|
-
def add(self, keys, vectors):
|
|
44
|
-
# Adding keys isn't supported for most index types
|
|
45
|
-
# self._faiss.add_with_ids(vectors, keys)
|
|
46
|
-
self._faiss.add(vectors)
|
|
47
|
-
|
|
48
|
-
def search(self, queries, k: int) -> BatchMatches:
|
|
49
|
-
distances, keys = self._faiss.search(queries, k)
|
|
50
|
-
return BatchMatches(keys, distances, np.array([k] * queries.shape[0]))
|
|
51
|
-
|
|
52
|
-
def __len__(self) -> int:
|
|
53
|
-
return self._faiss.ntotal
|
|
54
|
-
|
|
55
|
-
def clear(self):
|
|
56
|
-
self._faiss.reset()
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def specs(self) -> dict:
|
|
60
|
-
self._specs.update(
|
|
61
|
-
{
|
|
62
|
-
"Size": len(self),
|
|
63
|
-
}
|
|
64
|
-
)
|
|
65
|
-
return self._specs
|
|
66
|
-
|
|
67
|
-
def load(self, path: os.PathLike):
|
|
68
|
-
self._faiss = read_index(path)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class IndexQuantizedFAISS(IndexFAISS):
|
|
72
|
-
def __init__(
|
|
73
|
-
self,
|
|
74
|
-
train: np.ndarray,
|
|
75
|
-
connectivity: int = DEFAULT_CONNECTIVITY,
|
|
76
|
-
expansion_add: int = DEFAULT_EXPANSION_ADD,
|
|
77
|
-
expansion_search: int = DEFAULT_EXPANSION_SEARCH,
|
|
78
|
-
*args,
|
|
79
|
-
**kwargs,
|
|
80
|
-
):
|
|
81
|
-
ndim = train.shape[1]
|
|
82
|
-
super().__init__(
|
|
83
|
-
ndim=ndim,
|
|
84
|
-
connectivity=connectivity,
|
|
85
|
-
expansion_add=expansion_add,
|
|
86
|
-
expansion_search=expansion_search,
|
|
87
|
-
*args,
|
|
88
|
-
**kwargs,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
nlist = 10000 # Number of inverted lists (number of partitions).
|
|
92
|
-
nsegment = 16 # Number of segments for PQ (number of subquantizers).
|
|
93
|
-
nbit = 8 # Number of bits to encode each segment.
|
|
94
|
-
|
|
95
|
-
self._original_faiss = self._faiss
|
|
96
|
-
self._faiss = IndexIVFPQ(self._faiss, ndim, nlist, nsegment, nbit)
|
|
97
|
-
|
|
98
|
-
self._faiss.train(train)
|
|
99
|
-
self._faiss.nprobe = 10
|
|
100
|
-
self._specs["Class"] = "usearch.IndexQuantizedFAISS"
|