usearch 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +41 -1
- package/Cargo.toml +1 -1
- package/Dockerfile +3 -1
- package/README.md +1 -1
- package/VERSION +1 -1
- package/build.gradle +1 -1
- package/conanfile.py +1 -1
- package/docs/compilation.md +15 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/python/scripts/bench.py +86 -49
- package/python/scripts/bench_params.py +1 -0
|
@@ -160,11 +160,51 @@ jobs:
|
|
|
160
160
|
- name: Run tests
|
|
161
161
|
run: swift test
|
|
162
162
|
|
|
163
|
+
# Publishes to both Docker Hub and GitHub Container Registry
|
|
164
|
+
# https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-docker-hub-and-github-packages
|
|
165
|
+
publish_docker:
|
|
166
|
+
name: Publish Docker Image
|
|
167
|
+
needs: versioning
|
|
168
|
+
runs-on: ubuntu-latest
|
|
169
|
+
steps:
|
|
170
|
+
- uses: actions/checkout@v3
|
|
171
|
+
with:
|
|
172
|
+
ref: 'main'
|
|
173
|
+
- run: git submodule update --init --recursive
|
|
174
|
+
- name: Login to Docker Hub
|
|
175
|
+
uses: docker/login-action@v2
|
|
176
|
+
with:
|
|
177
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
178
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
179
|
+
- name: Log in to GitHub Registry
|
|
180
|
+
uses: docker/login-action@2
|
|
181
|
+
with:
|
|
182
|
+
registry: ghcr.io
|
|
183
|
+
username: ${{ github.actor }}
|
|
184
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
185
|
+
- name: Set up Docker Buildx
|
|
186
|
+
uses: docker/setup-buildx-action@v2
|
|
187
|
+
- name: Extract tags & labels for Docker
|
|
188
|
+
id: meta
|
|
189
|
+
uses: docker/metadata-action@4
|
|
190
|
+
with:
|
|
191
|
+
images: |
|
|
192
|
+
unum/usearch
|
|
193
|
+
ghcr.io/${{ github.repository }}
|
|
194
|
+
- name: Build and push
|
|
195
|
+
uses: docker/build-push-action@v4
|
|
196
|
+
with:
|
|
197
|
+
context: .
|
|
198
|
+
push: true
|
|
199
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
200
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
201
|
+
|
|
202
|
+
|
|
163
203
|
build_docs:
|
|
164
204
|
name: Build Docs
|
|
165
205
|
runs-on: ubuntu-22.04
|
|
166
206
|
if: ${{ always() }}
|
|
167
|
-
needs: [publish_python, publish_javascript, publish_rust, publish_java, publish_swift]
|
|
207
|
+
needs: [publish_python, publish_javascript, publish_rust, publish_java, publish_swift, publish_docker]
|
|
168
208
|
permissions:
|
|
169
209
|
contents: write
|
|
170
210
|
steps:
|
package/Cargo.toml
CHANGED
package/Dockerfile
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# syntax=docker/dockerfile:1
|
|
2
2
|
|
|
3
3
|
FROM ubuntu:23.04
|
|
4
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
5
|
+
|
|
4
6
|
RUN apt-get update && \
|
|
5
7
|
apt-get install -y --no-install-recommends python3.11 python3-pip && \
|
|
6
8
|
apt-get clean && \
|
|
@@ -10,5 +12,5 @@ RUN pip3 install --no-cache-dir --break-system-packages ucall usearch
|
|
|
10
12
|
WORKDIR /usearch
|
|
11
13
|
COPY python/usearch/server.py server.py
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
ENTRYPOINT ["python3", "./server.py"]
|
|
14
16
|
EXPOSE 8545
|
package/README.md
CHANGED
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.9.0
|
package/build.gradle
CHANGED
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.
|
|
7
|
+
version = '0.9.0'
|
|
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/docs/compilation.md
CHANGED
|
@@ -136,4 +136,19 @@ brew install --cask wolfram-engine
|
|
|
136
136
|
|
|
137
137
|
```sh
|
|
138
138
|
docker build -t unum/usearch . && docker run unum/usearch
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For multi-architecture builds and publications:
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
version=$(cat VERSION)
|
|
145
|
+
docker buildx create --use &&
|
|
146
|
+
docker login &&
|
|
147
|
+
docker buildx build \
|
|
148
|
+
--platform "linux/amd64,linux/arm64" \
|
|
149
|
+
--build-arg version=$version \
|
|
150
|
+
--file Dockerfile \
|
|
151
|
+
--tag unum/usearch:$version \
|
|
152
|
+
--tag unum/usearch:latest \
|
|
153
|
+
--push .
|
|
139
154
|
```
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -10,7 +10,7 @@ filterwarnings = ["error"]
|
|
|
10
10
|
|
|
11
11
|
[tool.cibuildwheel]
|
|
12
12
|
test-requires = ["pytest", "numpy"]
|
|
13
|
-
test-command = "pytest {project}/python/test.py"
|
|
13
|
+
test-command = "pytest {project}/python/scripts/test.py"
|
|
14
14
|
build-verbosity = 0
|
|
15
15
|
skip = ["*musllinux*", "*i686*", "pp*"]
|
|
16
16
|
|
package/python/scripts/bench.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import numpy as np
|
|
3
|
-
import
|
|
3
|
+
import math
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from numba import cfunc, types, carray
|
|
5
|
+
import fire
|
|
7
6
|
|
|
8
7
|
from usearch.index import Index
|
|
9
8
|
from usearch.io import load_matrix
|
|
@@ -51,25 +50,6 @@ def bench_usearch(index, vectors: np.array, queries: np.array, neighbors: np.arr
|
|
|
51
50
|
recall_at_one /= queries.shape[0]
|
|
52
51
|
print(f'- Recall@1: {recall_at_one * 100:.2f} %')
|
|
53
52
|
|
|
54
|
-
# Showcases how to use Numba to JIT-compile similarity measures for USearch.
|
|
55
|
-
# https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
signature_f32 = types.float32(
|
|
59
|
-
types.CPointer(types.float32),
|
|
60
|
-
types.CPointer(types.float32),
|
|
61
|
-
types.size_t, types.size_t)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@cfunc(signature_f32)
|
|
65
|
-
def inner_product_f32(a, b, n, m):
|
|
66
|
-
a_array = carray(a, n)
|
|
67
|
-
b_array = carray(b, n)
|
|
68
|
-
c = types.float32(0)
|
|
69
|
-
for i in range(n):
|
|
70
|
-
c += a_array[i] * b_array[i]
|
|
71
|
-
return types.float32(1 - c)
|
|
72
|
-
|
|
73
53
|
|
|
74
54
|
def main(
|
|
75
55
|
vectors: str,
|
|
@@ -94,16 +74,6 @@ def main(
|
|
|
94
74
|
)
|
|
95
75
|
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
96
76
|
|
|
97
|
-
print('USearch: HNSW + Numba JIT')
|
|
98
|
-
index = Index(
|
|
99
|
-
ndim=dim,
|
|
100
|
-
expansion_add=expansion_add,
|
|
101
|
-
expansion_search=expansion_search,
|
|
102
|
-
connectivity=connectivity,
|
|
103
|
-
metric_pointer=inner_product_f32.address,
|
|
104
|
-
)
|
|
105
|
-
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
106
|
-
|
|
107
77
|
print('USearch: HNSW + Half Precision')
|
|
108
78
|
index = Index(
|
|
109
79
|
ndim=dim,
|
|
@@ -124,27 +94,94 @@ def main(
|
|
|
124
94
|
)
|
|
125
95
|
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
126
96
|
|
|
97
|
+
# Showcases how to use Numba to JIT-compile similarity measures for USearch.
|
|
98
|
+
# https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
|
|
99
|
+
try:
|
|
100
|
+
from numba import cfunc, types, carray
|
|
101
|
+
|
|
102
|
+
signature_f32 = types.float32(
|
|
103
|
+
types.CPointer(types.float32),
|
|
104
|
+
types.CPointer(types.float32),
|
|
105
|
+
types.size_t, types.size_t)
|
|
106
|
+
|
|
107
|
+
@cfunc(signature_f32)
|
|
108
|
+
def inner_product_f32(a, b, n, m):
|
|
109
|
+
a_array = carray(a, n)
|
|
110
|
+
b_array = carray(b, n)
|
|
111
|
+
c = types.float32(0)
|
|
112
|
+
for i in range(n):
|
|
113
|
+
c += a_array[i] * b_array[i]
|
|
114
|
+
return types.float32(1 - c)
|
|
115
|
+
|
|
116
|
+
signature_f8 = types.float32(
|
|
117
|
+
types.CPointer(types.int8),
|
|
118
|
+
types.CPointer(types.int8),
|
|
119
|
+
types.size_t, types.size_t)
|
|
120
|
+
|
|
121
|
+
@cfunc(signature_f8)
|
|
122
|
+
def cos_f8(a, b, n, m):
|
|
123
|
+
a_array = carray(a, n)
|
|
124
|
+
b_array = carray(b, n)
|
|
125
|
+
c = types.int32(0)
|
|
126
|
+
a_sq = types.int32(0)
|
|
127
|
+
b_sq = types.int32(0)
|
|
128
|
+
for i in range(n):
|
|
129
|
+
ai = types.int16(a_array[i])
|
|
130
|
+
bi = types.int16(b_array[i])
|
|
131
|
+
c += ai * bi
|
|
132
|
+
a_sq += ai * ai
|
|
133
|
+
b_sq += bi * bi
|
|
134
|
+
a_norm = math.sqrt(types.float32(a_sq))
|
|
135
|
+
b_norm = math.sqrt(types.float32(b_sq))
|
|
136
|
+
c_norm = types.float32(c) / (a_norm * b_norm)
|
|
137
|
+
return types.float32(1 - c_norm)
|
|
138
|
+
|
|
139
|
+
print('USearch: HNSW + Numba JIT')
|
|
140
|
+
index = Index(
|
|
141
|
+
ndim=dim,
|
|
142
|
+
expansion_add=expansion_add,
|
|
143
|
+
expansion_search=expansion_search,
|
|
144
|
+
connectivity=connectivity,
|
|
145
|
+
metric_pointer=inner_product_f32.address,
|
|
146
|
+
)
|
|
147
|
+
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
148
|
+
|
|
149
|
+
print('USearch: HNSW + Quarter Precision + Numba JIT')
|
|
150
|
+
index = Index(
|
|
151
|
+
ndim=dim,
|
|
152
|
+
dtype='f8',
|
|
153
|
+
expansion_add=expansion_add,
|
|
154
|
+
expansion_search=expansion_search,
|
|
155
|
+
connectivity=connectivity,
|
|
156
|
+
metric_pointer=cos_f8.address,
|
|
157
|
+
)
|
|
158
|
+
bench_usearch(index, vectors_mat, queries_mat, neighbors_mat)
|
|
159
|
+
except ImportError:
|
|
160
|
+
print('Skipping Numba benchmarks')
|
|
161
|
+
|
|
127
162
|
# Don't depend on the FAISS installation for benchmarks
|
|
128
163
|
try:
|
|
129
164
|
from faiss import IndexHNSWFlat, IndexIVFPQ
|
|
165
|
+
|
|
166
|
+
print('FAISS: HNSW')
|
|
167
|
+
index = IndexHNSWFlat(dim, connectivity)
|
|
168
|
+
index.hnsw.efSearch = expansion_search
|
|
169
|
+
index.hnsw.efConstruction = expansion_add
|
|
170
|
+
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
171
|
+
|
|
172
|
+
print('FAISS: HNSW + IVFPQ')
|
|
173
|
+
# Number of inverted lists (number of partitions or cells).
|
|
174
|
+
nlist = 10000
|
|
175
|
+
nsegment = 16 # Number of segments for PQ (number of subquantizers).
|
|
176
|
+
nbit = 8 # Number of bits to encode each segment.
|
|
177
|
+
coarse_quantizer = IndexHNSWFlat(dim, connectivity)
|
|
178
|
+
index = IndexIVFPQ(coarse_quantizer, dim, nlist, nsegment, nbit)
|
|
179
|
+
index.train(vectors_mat)
|
|
180
|
+
index.nprobe = 10
|
|
181
|
+
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
182
|
+
|
|
130
183
|
except ImportError:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
print('FAISS: HNSW')
|
|
134
|
-
index = IndexHNSWFlat(dim, connectivity)
|
|
135
|
-
index.hnsw.efSearch = expansion_search
|
|
136
|
-
index.hnsw.efConstruction = expansion_add
|
|
137
|
-
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
138
|
-
|
|
139
|
-
print('FAISS: HNSW + IVFPQ')
|
|
140
|
-
nlist = 10000 # Number of inverted lists (number of partitions or cells).
|
|
141
|
-
nsegment = 16 # Number of segments for PQ (number of subquantizers).
|
|
142
|
-
nbit = 8 # Number of bits to encode each segment.
|
|
143
|
-
coarse_quantizer = IndexHNSWFlat(dim, connectivity)
|
|
144
|
-
index = IndexIVFPQ(coarse_quantizer, dim, nlist, nsegment, nbit)
|
|
145
|
-
index.train(vectors_mat)
|
|
146
|
-
index.nprobe = 10
|
|
147
|
-
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
184
|
+
print('Skipping FAISS benchmarks')
|
|
148
185
|
|
|
149
186
|
|
|
150
187
|
if __name__ == '__main__':
|