usearch 2.0.1 → 2.1.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/Dockerfile.libs +34 -0
- package/.github/workflows/release.yml +56 -0
- package/Cargo.toml +1 -1
- package/README.md +3 -0
- package/conanfile.py +1 -1
- package/csharp/README.md +6 -9
- package/docs/csharp/index.rst +6 -0
- package/docs/index.rst +1 -0
- package/golang/README.md +58 -37
- package/golang/lib.go +16 -12
- package/include/usearch/index.hpp +6 -2
- package/package.json +1 -1
- package/wasmer.toml +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
FROM ubuntu:22.04
|
|
2
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
3
|
+
|
|
4
|
+
ENV user_name="runner"
|
|
5
|
+
ARG TARGETPLATFORM
|
|
6
|
+
ARG docker_ip
|
|
7
|
+
ARG user_pass
|
|
8
|
+
|
|
9
|
+
RUN apt-get update && \
|
|
10
|
+
apt-get install -y --no-install-recommends python3.11 python3-pip cmake g++-12 build-essential libjemalloc-dev golang openssh-client sshpass && \
|
|
11
|
+
apt-get clean && \
|
|
12
|
+
rm -rf /var/lib/apt/lists/*
|
|
13
|
+
|
|
14
|
+
COPY . .
|
|
15
|
+
|
|
16
|
+
RUN cd golang && make -C ../c libusearch_c.so && \
|
|
17
|
+
mv ../c/libusearch_c.so libusearch.a && cp ../c/usearch.h usearch.h && \
|
|
18
|
+
go test -v && cd ..
|
|
19
|
+
|
|
20
|
+
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
|
21
|
+
export arch="amd"; \
|
|
22
|
+
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
|
23
|
+
export arch="arm"; \
|
|
24
|
+
fi && \
|
|
25
|
+
version=$(cat VERSION) && \
|
|
26
|
+
mkdir -p usearch_"$arch"_"$version"/DEBIAN && \
|
|
27
|
+
touch usearch_"$arch"_"$version"/DEBIAN/control && \
|
|
28
|
+
mkdir -p usearch_"$arch"_"$version"/usr/local/lib && \
|
|
29
|
+
mkdir usearch_"$arch"_"$version"/usr/local/include && \
|
|
30
|
+
cp c/usearch.h usearch_"$arch"_"$version"/usr/local/include/ && \
|
|
31
|
+
cp golang/libusearch.a usearch_"$arch"_"$version"/usr/local/lib/ && \
|
|
32
|
+
echo "Package: usearch\nVersion: $version\nMaintainer: Ashot Vardanian\nArchitecture: "$arch"\nDescription: Faster & Smaller Single-File Search Engine for Vectors & Texts" > usearch_"$arch"_"$version"/DEBIAN/control && \
|
|
33
|
+
dpkg-deb --build usearch_"$arch"_"$version" && \
|
|
34
|
+
sshpass -p "$user_pass" scp -o StrictHostKeyChecking=no usearch_"$arch"_"$version".deb "$user_name"@"$docker_ip":/home/$user_name/work/usearch/usearch/
|
|
@@ -96,6 +96,62 @@ jobs:
|
|
|
96
96
|
* Source code (tar.gz) : `${{ steps.hashes.outputs.tar_hash }}`
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
create_deb_packages_linux:
|
|
100
|
+
name: Create deb package for Linux on ${{ matrix.arch }}
|
|
101
|
+
needs: versioning
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
strategy:
|
|
104
|
+
matrix:
|
|
105
|
+
arch: ["linux/amd64", "linux/arm64"]
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@v3
|
|
108
|
+
with:
|
|
109
|
+
persist-credentials: false
|
|
110
|
+
ref: 'main'
|
|
111
|
+
- run: git submodule update --init --recursive
|
|
112
|
+
|
|
113
|
+
- name: Set up QEMU
|
|
114
|
+
uses: docker/setup-qemu-action@v2
|
|
115
|
+
|
|
116
|
+
- name: Set up Docker Buildx
|
|
117
|
+
uses: docker/setup-buildx-action@v2
|
|
118
|
+
|
|
119
|
+
- name: Get docker daemon IP
|
|
120
|
+
id: dockerip
|
|
121
|
+
run: |
|
|
122
|
+
docker_ip=$(ip addr show docker0 | grep -Po 'inet \K[\d.]+') && \
|
|
123
|
+
echo "docker_ip=$docker_ip" >> "$GITHUB_OUTPUT"
|
|
124
|
+
|
|
125
|
+
- name: Set new password for runner user
|
|
126
|
+
id: userpass
|
|
127
|
+
run: |
|
|
128
|
+
user_pass="1122"
|
|
129
|
+
echo "runner:$user_pass" | sudo chpasswd
|
|
130
|
+
echo "user_pass=$user_pass" >> "$GITHUB_OUTPUT"
|
|
131
|
+
|
|
132
|
+
- name: Build Usearch lib
|
|
133
|
+
run: |
|
|
134
|
+
docker buildx create --use
|
|
135
|
+
docker buildx build \
|
|
136
|
+
--platform ${{ matrix.arch }} \
|
|
137
|
+
--build-arg docker_ip=${{ steps.dockerip.outputs.docker_ip }} \
|
|
138
|
+
--build-arg user_pass=${{ steps.userpass.outputs.user_pass }} \
|
|
139
|
+
--file .github/workflows/Dockerfile.libs \
|
|
140
|
+
--tag unum/ustore-deps:latest \
|
|
141
|
+
--load .
|
|
142
|
+
|
|
143
|
+
- name: Clear Docker buildx cache
|
|
144
|
+
run: docker buildx prune --all --force
|
|
145
|
+
|
|
146
|
+
- name: Upload archive to release Assets
|
|
147
|
+
uses: xresloader/upload-to-github-release@v1
|
|
148
|
+
env:
|
|
149
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
150
|
+
with:
|
|
151
|
+
file: "*.deb"
|
|
152
|
+
update_latest_release: true
|
|
153
|
+
|
|
154
|
+
|
|
99
155
|
build_wheels:
|
|
100
156
|
name: Build Wheels for ${{ matrix.os }}
|
|
101
157
|
needs: versioning
|
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Euclidean • Angular • Bitwise • Haversine • User-Defined Metrics
|
|
|
28
28
|
<a href="https://unum-cloud.github.io/usearch/c">C 99</a> •
|
|
29
29
|
<a href="https://unum-cloud.github.io/usearch/objective-c">Objective-C</a> •
|
|
30
30
|
<a href="https://unum-cloud.github.io/usearch/swift">Swift</a> •
|
|
31
|
+
<a href="https://unum-cloud.github.io/usearch/csharp">C#</a> •
|
|
31
32
|
<a href="https://unum-cloud.github.io/usearch/golang">GoLang</a> •
|
|
32
33
|
<a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a>
|
|
33
34
|
<br/>
|
|
@@ -38,6 +39,8 @@ Linux • MacOS • Windows • iOS • Docker • WebAssembly
|
|
|
38
39
|
<a href="https://pypi.org/project/usearch/"> <img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/usearch?label=pypi%20downloads"> </a>
|
|
39
40
|
<a href="https://www.npmjs.com/package/usearch"> <img alt="npm" src="https://img.shields.io/npm/dy/usearch?label=npm%20dowloads"> </a>
|
|
40
41
|
<a href="https://crates.io/crates/usearch"> <img alt="Crates.io" src="https://img.shields.io/crates/d/usearch?label=crate%20downloads"> </a>
|
|
42
|
+
<a href="https://www.nuget.org/packages/Cloud.Unum.USearch"> <img alt="Nuget" src="https://img.shields.io/nuget/dt/Cloud.Unum.USearch?style=social&label=NuGet"> </a>
|
|
43
|
+
<a href="https://hub.docker.com/r/unum/usearch"> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/unum/usearch?style=social&label=Docker"> </a>
|
|
41
44
|
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/unum-cloud/usearch">
|
|
42
45
|
</div>
|
|
43
46
|
|
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 = '2.0
|
|
7
|
+
version = '2.1.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/csharp/README.md
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
## Installation
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
dotnet add package Cloud.Unum.USearch
|
|
6
|
+
dotnet add package Cloud.Unum.USearch
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## Quickstart
|
|
10
10
|
|
|
11
|
-
```
|
|
11
|
+
```csharp
|
|
12
12
|
using System.Diagnostics;
|
|
13
13
|
using Cloud.Unum.USearch;
|
|
14
14
|
|
|
15
15
|
using var index = new USearchIndex(
|
|
16
|
-
metricKind: MetricKind.Cos, //Choose cosine metric
|
|
16
|
+
metricKind: MetricKind.Cos, // Choose cosine metric
|
|
17
17
|
quantization: ScalarKind.Float32, // Only quantization to Float32, Float64 is currently supported
|
|
18
18
|
dimensions: 3, // Define the number of dimensions in input vectors
|
|
19
19
|
connectivity: 16, // How frequent should the connections in the graph be, optional
|
|
@@ -29,21 +29,18 @@ Trace.Assert(index.Size() == 1);
|
|
|
29
29
|
Trace.Assert(matches == 1);
|
|
30
30
|
Trace.Assert(keys[0] == 42);
|
|
31
31
|
Trace.Assert(distances[0] <= 0.001f);
|
|
32
|
-
// USearchIndex obj created with "using statement" and implements IDisposable,
|
|
33
|
-
// otherwise call it explicitly:
|
|
34
|
-
// index.Dispose();
|
|
35
32
|
```
|
|
36
33
|
|
|
37
34
|
## Serialization
|
|
38
35
|
|
|
39
|
-
```
|
|
36
|
+
```csharp
|
|
40
37
|
index.Save("index.usearch")
|
|
41
38
|
|
|
42
39
|
// Copy the whole index into memory
|
|
43
40
|
using var indexLoaded = new USearchIndex("index.usearch");
|
|
44
41
|
|
|
45
42
|
// Or view from disk without loading in memory
|
|
46
|
-
// using var indexLoaded = new USearchIndex("index.usearch", view:
|
|
43
|
+
// using var indexLoaded = new USearchIndex("index.usearch", view: true);
|
|
47
44
|
|
|
48
45
|
Trace.Assert(indexLoaded.Size() == 1);
|
|
49
46
|
Trace.Assert(indexLoaded.Dimensions() == 3);
|
|
@@ -55,7 +52,7 @@ Trace.Assert(indexLoaded.Contains(42));
|
|
|
55
52
|
|
|
56
53
|
Adding a batch of entries is identical to adding a single vector.
|
|
57
54
|
|
|
58
|
-
```
|
|
55
|
+
```csharp
|
|
59
56
|
using var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, dimensions: 3);
|
|
60
57
|
|
|
61
58
|
// Generate keys and random vectors
|
package/docs/index.rst
CHANGED
package/golang/README.md
CHANGED
|
@@ -2,51 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
## Installation
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Download and install from the Debian package from the latest release.
|
|
6
|
+
Substitute `<release_tag>`, `<arch>`, and `<usearch_version>` with your settings.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
wget https://github.com/unum-cloud/usearch/releases/download/<release_tag>/usearch_<arch>_<usearch_version>.deb
|
|
10
|
+
dpkg -i usearch_<arch>_<usearch_version>.deb
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Quickstart
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
package main
|
|
15
|
+
1. Create a `go.mod` file:
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"github.com/unum-cloud/usearch/golang-go"
|
|
19
|
-
)
|
|
17
|
+
```
|
|
18
|
+
module usearch_example
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
conf := DefaultConfig(dim)
|
|
24
|
-
ind, err := NewIndex(conf)
|
|
25
|
-
if err != nil {
|
|
26
|
-
panic("Failed to construct the index: %s", err)
|
|
27
|
-
}
|
|
28
|
-
defer ind.Destroy()
|
|
20
|
+
go <go_version>
|
|
21
|
+
```
|
|
29
22
|
|
|
30
|
-
|
|
31
|
-
if err != nil {
|
|
32
|
-
panic("Failed to reserve capacity: %s", err)
|
|
33
|
-
}
|
|
23
|
+
2. Create an `example.go`:
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
vec[1] = 2.0
|
|
25
|
+
```golang
|
|
26
|
+
package main
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
import (
|
|
29
|
+
"fmt"
|
|
30
|
+
usearch "github.com/unum-cloud/usearch/golang"
|
|
31
|
+
)
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
func main() {
|
|
34
|
+
|
|
35
|
+
// Create Index
|
|
36
|
+
vector_size := 3
|
|
37
|
+
vectors_count := 100
|
|
38
|
+
conf := usearch.DefaultConfig(uint(vector_size))
|
|
39
|
+
index,err := usearch.NewIndex(conf)
|
|
40
|
+
if err != nil {
|
|
41
|
+
panic("Failed to create Index")
|
|
42
|
+
}
|
|
43
|
+
defer index.Destroy()
|
|
44
|
+
|
|
45
|
+
// Add to Index
|
|
46
|
+
err = index.Reserve(uint(vectors_count))
|
|
47
|
+
for i := 0; i < vectors_count; i++ {
|
|
48
|
+
err = index.Add(usearch.Key(i), []float32{float32(i), float32(i+1), float32(i+2)})
|
|
49
|
+
if err != nil {
|
|
50
|
+
panic("Failed to add")
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Search
|
|
55
|
+
keys, distances, err := index.Search([]float32{0.0, 1.0, 2.0}, 3)
|
|
56
|
+
if err != nil {
|
|
57
|
+
panic("Failed to search")
|
|
58
|
+
}
|
|
59
|
+
fmt.Println(keys, distances)
|
|
50
60
|
}
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. Get USearch:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
go get github.com/unum-cloud/usearch/golang
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
4. Run:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
go run example.go
|
|
73
|
+
```
|
package/golang/lib.go
CHANGED
|
@@ -6,9 +6,8 @@ import (
|
|
|
6
6
|
)
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
|
-
#cgo
|
|
10
|
-
#
|
|
11
|
-
#include <usearch.h>
|
|
9
|
+
#cgo LDFLAGS: -L. -lusearch
|
|
10
|
+
#include "usearch.h"
|
|
12
11
|
#include <stdlib.h>
|
|
13
12
|
*/
|
|
14
13
|
import "C"
|
|
@@ -91,6 +90,8 @@ type IndexConfig struct {
|
|
|
91
90
|
ExpansionAdd uint
|
|
92
91
|
// The @b optional expansion factor used for index construction during search operations.
|
|
93
92
|
ExpansionSearch uint
|
|
93
|
+
// The @b optional multi flag.
|
|
94
|
+
Multi bool
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
func DefaultConfig(dimensions uint) IndexConfig {
|
|
@@ -101,6 +102,7 @@ func DefaultConfig(dimensions uint) IndexConfig {
|
|
|
101
102
|
c.Connectivity = 0
|
|
102
103
|
c.ExpansionAdd = 0
|
|
103
104
|
c.ExpansionSearch = 0
|
|
105
|
+
c.Multi = false
|
|
104
106
|
return c
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -114,16 +116,18 @@ func NewIndex(conf IndexConfig) (index *Index, err error) {
|
|
|
114
116
|
index = &Index{config: conf}
|
|
115
117
|
|
|
116
118
|
conf = index.config
|
|
117
|
-
dimensions := C.
|
|
118
|
-
connectivity := C.
|
|
119
|
-
expansion_add := C.
|
|
120
|
-
expansion_search := C.
|
|
119
|
+
dimensions := C.size_t(conf.Dimensions)
|
|
120
|
+
connectivity := C.size_t(conf.Connectivity)
|
|
121
|
+
expansion_add := C.size_t(conf.ExpansionAdd)
|
|
122
|
+
expansion_search := C.size_t(conf.ExpansionSearch)
|
|
123
|
+
multi := C.bool(conf.Multi)
|
|
121
124
|
|
|
122
125
|
options := C.struct_usearch_init_options_t{}
|
|
123
126
|
options.dimensions = dimensions
|
|
124
127
|
options.connectivity = connectivity
|
|
125
128
|
options.expansion_add = expansion_add
|
|
126
129
|
options.expansion_search = expansion_search
|
|
130
|
+
options.multi = multi
|
|
127
131
|
|
|
128
132
|
// Map the metric kind to a C enum
|
|
129
133
|
switch conf.Metric {
|
|
@@ -281,21 +285,21 @@ func (index *Index) Contains(key Key) (found bool, err error) {
|
|
|
281
285
|
return found, nil
|
|
282
286
|
}
|
|
283
287
|
|
|
284
|
-
func (index *Index) Get(key Key) (
|
|
288
|
+
func (index *Index) Get(key Key, count uint) (vectors []float32, err error) {
|
|
285
289
|
if index.opaque_handle == nil {
|
|
286
290
|
panic("Index is uninitialized")
|
|
287
291
|
}
|
|
288
292
|
|
|
289
|
-
|
|
293
|
+
vectors = make([]float32, index.config.Dimensions * count)
|
|
290
294
|
var errorMessage *C.char
|
|
291
|
-
found :=
|
|
295
|
+
found := uint(C.usearch_get((C.usearch_index_t)(unsafe.Pointer(index.opaque_handle)), (C.usearch_key_t)(key), (C.size_t)(count), unsafe.Pointer(&vectors[0]), C.usearch_scalar_f32_k, (*C.usearch_error_t)(&errorMessage)))
|
|
292
296
|
if errorMessage != nil {
|
|
293
297
|
return nil, errors.New(C.GoString(errorMessage))
|
|
294
298
|
}
|
|
295
|
-
if
|
|
299
|
+
if found == 0 {
|
|
296
300
|
return nil, nil
|
|
297
301
|
}
|
|
298
|
-
return
|
|
302
|
+
return vectors, nil
|
|
299
303
|
}
|
|
300
304
|
|
|
301
305
|
// Performs k-Approximate Nearest Neighbors Search for closest vectors to query.
|
|
@@ -409,7 +409,10 @@ template <typename allocator_at = std::allocator<byte_t>> class bitset_gt {
|
|
|
409
409
|
~bitset_gt() noexcept { reset(); }
|
|
410
410
|
|
|
411
411
|
explicit operator bool() const noexcept { return slots_; }
|
|
412
|
-
void clear() noexcept {
|
|
412
|
+
void clear() noexcept {
|
|
413
|
+
if (slots_)
|
|
414
|
+
std::memset(slots_, 0, count_ * sizeof(compressed_slot_t));
|
|
415
|
+
}
|
|
413
416
|
|
|
414
417
|
void reset() noexcept {
|
|
415
418
|
if (slots_)
|
|
@@ -857,7 +860,8 @@ class growing_hash_set_gt {
|
|
|
857
860
|
std::size_t size() const noexcept { return count_; }
|
|
858
861
|
|
|
859
862
|
void clear() noexcept {
|
|
860
|
-
|
|
863
|
+
if (slots_)
|
|
864
|
+
std::memset((void*)slots_, 0xFF, capacity_ * sizeof(element_t));
|
|
861
865
|
count_ = 0;
|
|
862
866
|
}
|
|
863
867
|
|
package/package.json
CHANGED