usearch 0.1.10 → 0.2.2
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 +12 -1
- package/.vscode/launch.json +5 -4
- package/.vscode/settings.json +7 -1
- package/CMakeLists.txt +11 -6
- package/CMakeListsInstructions.md +39 -17
- package/Cargo.toml +1 -1
- package/Package.swift +40 -0
- package/README.md +36 -18
- package/VERSION +1 -1
- package/build.gradle +5 -1
- package/cmake/jemalloc.cmake +0 -2
- package/conanfile.py +18 -0
- package/include/usearch/usearch.hpp +337 -228
- package/javascript/lib.cpp +8 -2
- package/objc/USearchObjective.mm +150 -0
- package/objc/include/USearchObjective.h +69 -0
- package/package.json +1 -1
- package/pyproject.toml +18 -4
- package/python/bench.py +14 -2
- package/python/lib.cpp +353 -43
- package/python/test.py +10 -4
- package/python/usearch/__init__.py +0 -0
- package/rust/lib.cpp +13 -2
- package/setup.py +8 -3
- package/simsimd/include/simsimd/simsimd.h +15 -0
- package/src/advanced.hpp +232 -122
- package/src/bench.cpp +249 -79
- package/swift/Index+Sugar.swift +70 -0
- package/swift/Test.swift +20 -0
- package/swift/USearch.swift +8 -0
- package/java/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/java/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/java/gradlew +0 -244
package/javascript/lib.cpp
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* @see NodeJS docs: https://nodejs.org/api/addons.html#hello-world
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
|
+
|
|
13
|
+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
|
14
|
+
#define _USE_MATH_DEFINES
|
|
15
|
+
#define NAPI_CPP_EXCEPTIONS
|
|
16
|
+
#endif
|
|
17
|
+
|
|
12
18
|
#include <napi.h>
|
|
13
19
|
#include <node_api.h>
|
|
14
20
|
|
|
@@ -87,8 +93,8 @@ Index::Index(Napi::CallbackInfo const& ctx) : Napi::ObjectWrap<Index>(ctx) {
|
|
|
87
93
|
accuracy = accuracy_t::f64_k;
|
|
88
94
|
else if (accuracy_str == "f16")
|
|
89
95
|
accuracy = accuracy_t::f16_k;
|
|
90
|
-
else if (accuracy_str == "
|
|
91
|
-
accuracy = accuracy_t::
|
|
96
|
+
else if (accuracy_str == "f8")
|
|
97
|
+
accuracy = accuracy_t::f8_k;
|
|
92
98
|
else {
|
|
93
99
|
Napi::TypeError::New(env, "Supported metrics are: [ip, cos, l2_sq, haversine]")
|
|
94
100
|
.ThrowAsJavaScriptException();
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#import "USearchObjective.h"
|
|
2
|
+
|
|
3
|
+
#pragma clang diagnostic push
|
|
4
|
+
#pragma clang diagnostic ignored "-Wdocumentation"
|
|
5
|
+
#import "advanced.hpp"
|
|
6
|
+
#pragma clang diagnostic pop
|
|
7
|
+
|
|
8
|
+
using namespace unum::usearch;
|
|
9
|
+
using namespace unum;
|
|
10
|
+
|
|
11
|
+
using distance_t = punned_distance_t;
|
|
12
|
+
using native_index_t = auto_index_gt<UInt32>;
|
|
13
|
+
using shared_index_t = std::shared_ptr<native_index_t>;
|
|
14
|
+
|
|
15
|
+
@interface Index ()
|
|
16
|
+
|
|
17
|
+
@property (readonly) shared_index_t native;
|
|
18
|
+
|
|
19
|
+
- (instancetype)initWithIndex:(shared_index_t)native;
|
|
20
|
+
|
|
21
|
+
@end
|
|
22
|
+
|
|
23
|
+
@implementation Index
|
|
24
|
+
|
|
25
|
+
- (instancetype)initWithIndex:(shared_index_t)native {
|
|
26
|
+
self = [super init];
|
|
27
|
+
_native = native;
|
|
28
|
+
return self;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (Boolean)isEmpty {
|
|
32
|
+
return _native->size() != 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
- (UInt32)dimensions {
|
|
36
|
+
return static_cast<UInt32>(_native->dimensions());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
- (UInt32)connectivity {
|
|
40
|
+
return static_cast<UInt32>(_native->connectivity());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
- (UInt32)length {
|
|
44
|
+
return static_cast<UInt32>(_native->size());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
- (UInt32)capacity {
|
|
48
|
+
return static_cast<UInt32>(_native->capacity());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (UInt32)expansion_add {
|
|
52
|
+
return static_cast<UInt32>(_native->config().expansion_add);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
- (UInt32)expansion_search {
|
|
56
|
+
return static_cast<UInt32>(_native->config().expansion_search);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
+ (instancetype)indexIP:(UInt32)dimensions connectivity:(UInt32)connectivity {
|
|
60
|
+
std::size_t dims = static_cast<std::size_t>(dimensions);
|
|
61
|
+
config_t config;
|
|
62
|
+
config.connectivity = static_cast<std::size_t>(connectivity);
|
|
63
|
+
shared_index_t ptr = std::make_shared<native_index_t>(native_index_t::ip(dims, accuracy_t::f32_k, config));
|
|
64
|
+
return [[Index alloc] initWithIndex:ptr];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
+ (instancetype)indexL2:(UInt32)dimensions connectivity:(UInt32)connectivity {
|
|
68
|
+
std::size_t dims = static_cast<std::size_t>(dimensions);
|
|
69
|
+
config_t config;
|
|
70
|
+
config.connectivity = static_cast<std::size_t>(connectivity);
|
|
71
|
+
shared_index_t ptr = std::make_shared<native_index_t>(native_index_t::l2(dims, accuracy_t::f32_k, config));
|
|
72
|
+
return [[Index alloc] initWithIndex:ptr];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
+ (instancetype)indexHaversine:(UInt32)connectivity {
|
|
76
|
+
config_t config;
|
|
77
|
+
config.connectivity = static_cast<std::size_t>(connectivity);
|
|
78
|
+
shared_index_t ptr = std::make_shared<native_index_t>(native_index_t::haversine(accuracy_t::f32_k, config));
|
|
79
|
+
return [[Index alloc] initWithIndex:ptr];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
- (void)addSingle:(UInt32)label
|
|
83
|
+
vector:(Float32 const * _Nonnull)vector {
|
|
84
|
+
_native->add(label, vector);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
- (UInt32)searchSingle:(Float32 const* _Nonnull)vector
|
|
88
|
+
count:(UInt32)wanted
|
|
89
|
+
labels:(UInt32* _Nullable)labels
|
|
90
|
+
distances:(Float32* _Nullable)distances {
|
|
91
|
+
std::size_t found = _native->search(vector, static_cast<std::size_t>(wanted), labels, distances);
|
|
92
|
+
return static_cast<UInt32>(found);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (void)addPrecise:(UInt32)label
|
|
96
|
+
vector:(Float64 const * _Nonnull)vector {
|
|
97
|
+
_native->add(label, (f64_t const *)vector);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- (UInt32)searchPrecise:(Float64 const* _Nonnull)vector
|
|
101
|
+
count:(UInt32)wanted
|
|
102
|
+
labels:(UInt32* _Nullable)labels
|
|
103
|
+
distances:(Float32* _Nullable)distances {
|
|
104
|
+
std::size_t found = _native->search((f64_t const *)vector, static_cast<std::size_t>(wanted), labels, distances);
|
|
105
|
+
return static_cast<UInt32>(found);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (void)addImprecise:(UInt32)label
|
|
109
|
+
vector:(void const * _Nonnull)vector {
|
|
110
|
+
_native->add(label, (f16_converted_t const *)vector);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
- (UInt32)searchImprecise:(void const* _Nonnull)vector
|
|
114
|
+
count:(UInt32)wanted
|
|
115
|
+
labels:(UInt32* _Nullable)labels
|
|
116
|
+
distances:(Float32* _Nullable)distances {
|
|
117
|
+
std::size_t found = _native->search((f16_converted_t const *)vector, static_cast<std::size_t>(wanted), labels, distances);
|
|
118
|
+
return static_cast<UInt32>(found);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
- (void)clear { _native->clear(); }
|
|
122
|
+
|
|
123
|
+
- (void)save:(NSString*)path {
|
|
124
|
+
char const *path_c = [path UTF8String];
|
|
125
|
+
if (!path_c)
|
|
126
|
+
@throw [NSException exceptionWithName:@"Can't save to disk"
|
|
127
|
+
reason:@"The path must be convertible to UTF8"
|
|
128
|
+
userInfo:nil];
|
|
129
|
+
_native->save(path_c);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
- (void)load:(NSString*)path {
|
|
133
|
+
char const *path_c = [path UTF8String];
|
|
134
|
+
if (!path_c)
|
|
135
|
+
@throw [NSException exceptionWithName:@"Can't load from disk"
|
|
136
|
+
reason:@"The path must be convertible to UTF8"
|
|
137
|
+
userInfo:nil];
|
|
138
|
+
_native->load(path_c);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
- (void)view:(NSString*)path {
|
|
142
|
+
char const *path_c = [path UTF8String];
|
|
143
|
+
if (!path_c)
|
|
144
|
+
@throw [NSException exceptionWithName:@"Can't view from disk"
|
|
145
|
+
reason:@"The path must be convertible to UTF8"
|
|
146
|
+
userInfo:nil];
|
|
147
|
+
_native->view(path_c);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
typedef NS_ENUM(NSUInteger, Quantization) {
|
|
8
|
+
kSinglePrecision,
|
|
9
|
+
kHalfPrecision,
|
|
10
|
+
kDoublePrecision
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
@interface Index: NSObject
|
|
14
|
+
|
|
15
|
+
@property (readonly) UInt32 dimensions;
|
|
16
|
+
@property (readonly) UInt32 connectivity;
|
|
17
|
+
@property (readonly) UInt32 expansion_add;
|
|
18
|
+
@property (readonly) UInt32 expansion_search;
|
|
19
|
+
|
|
20
|
+
@property (readonly) UInt32 length;
|
|
21
|
+
@property (readonly) UInt32 capacity;
|
|
22
|
+
@property (readonly) Boolean isEmpty;
|
|
23
|
+
|
|
24
|
+
- (instancetype)init
|
|
25
|
+
NS_UNAVAILABLE;
|
|
26
|
+
|
|
27
|
+
+ (instancetype)indexIP:(UInt32)dimensions connectivity:(UInt32)connectivity NS_SWIFT_NAME(indexIP(dimensions:connectivity:));
|
|
28
|
+
+ (instancetype)indexL2:(UInt32)dimensions connectivity:(UInt32)connectivity NS_SWIFT_NAME(l2(dimensions:connectivity:));
|
|
29
|
+
+ (instancetype)indexHaversine:(UInt32)connectivity NS_SWIFT_NAME(indexHaversine(connectivity:));
|
|
30
|
+
|
|
31
|
+
- (void)addSingle:(UInt32)label
|
|
32
|
+
vector:(Float32 const* _Nonnull)vector
|
|
33
|
+
NS_SWIFT_NAME(addSingle(label:vector:));
|
|
34
|
+
|
|
35
|
+
- (UInt32)searchSingle:(Float32 const* _Nonnull)vector
|
|
36
|
+
count:(UInt32)count
|
|
37
|
+
labels:(UInt32* _Nullable)labels
|
|
38
|
+
distances:(Float32* _Nullable)distances
|
|
39
|
+
NS_SWIFT_NAME(searchSingle(vector:count:labels:distances:));
|
|
40
|
+
|
|
41
|
+
- (void)addPrecise:(UInt32)label
|
|
42
|
+
vector:(Float64 const* _Nonnull)vector
|
|
43
|
+
NS_SWIFT_NAME(addPrecise(label:vector:));
|
|
44
|
+
|
|
45
|
+
- (UInt32)searchPrecise:(Float64 const* _Nonnull)vector
|
|
46
|
+
count:(UInt32)count
|
|
47
|
+
labels:(UInt32* _Nullable)labels
|
|
48
|
+
distances:(Float32* _Nullable)distances
|
|
49
|
+
NS_SWIFT_NAME(searchPrecise(vector:count:labels:distances:));
|
|
50
|
+
|
|
51
|
+
- (void)addImprecise:(UInt32)label
|
|
52
|
+
vector:(void const* _Nonnull)vector
|
|
53
|
+
NS_SWIFT_NAME(addImprecise(label:vector:));
|
|
54
|
+
|
|
55
|
+
- (UInt32)searchImprecise:(void const* _Nonnull)vector
|
|
56
|
+
count:(UInt32)count
|
|
57
|
+
labels:(UInt32* _Nullable)labels
|
|
58
|
+
distances:(Float32* _Nullable)distances
|
|
59
|
+
NS_SWIFT_NAME(searchImprecise(vector:count:labels:distances:));
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
- (void)save:(NSString*)path NS_SWIFT_NAME(dump(path:));
|
|
63
|
+
- (void)load:(NSString*)path NS_SWIFT_NAME(load(path:));
|
|
64
|
+
- (void)view:(NSString*)path NS_SWIFT_NAME(view(path:));
|
|
65
|
+
- (void)clear NS_SWIFT_NAME(clear());
|
|
66
|
+
|
|
67
|
+
@end
|
|
68
|
+
|
|
69
|
+
NS_ASSUME_NONNULL_END
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -10,15 +10,15 @@ filterwarnings = ["error"]
|
|
|
10
10
|
|
|
11
11
|
[tool.cibuildwheel]
|
|
12
12
|
build-verbosity = 0
|
|
13
|
+
|
|
14
|
+
skip = ["*musllinux*", "*i686*", "pp*"]
|
|
15
|
+
|
|
16
|
+
[tool.cibuildwheel.linux]
|
|
13
17
|
before-build = [
|
|
14
18
|
"rm -rf {project}/CMakeCache.txt {project}/build {project}/build_debug {project}/CMakeFiles.txt {project}/_deps",
|
|
15
19
|
"mkdir -p build/usearch",
|
|
16
20
|
"git submodule update --init --recursive"
|
|
17
21
|
]
|
|
18
|
-
|
|
19
|
-
skip = ["*musllinux*", "*i686*", "pp*"]
|
|
20
|
-
|
|
21
|
-
[tool.cibuildwheel.linux]
|
|
22
22
|
manylinux-x86_64-image = "manylinux_2_28"
|
|
23
23
|
manylinux-aarch64-image = "manylinux_2_28"
|
|
24
24
|
|
|
@@ -29,5 +29,19 @@ before-all = [
|
|
|
29
29
|
repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
|
|
30
30
|
|
|
31
31
|
[tool.cibuildwheel.macos]
|
|
32
|
+
before-build = [
|
|
33
|
+
"rm -rf {project}/CMakeCache.txt {project}/build {project}/build_debug {project}/CMakeFiles.txt {project}/_deps",
|
|
34
|
+
"mkdir -p build/usearch",
|
|
35
|
+
"git submodule update --init --recursive"
|
|
36
|
+
]
|
|
32
37
|
archs = ["x86_64", "universal2", "arm64"]
|
|
33
38
|
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
[tool.cibuildwheel.windows]
|
|
42
|
+
before-build = [
|
|
43
|
+
"rd /s /q {project}\\CMakeCache.txt {project}\\build {project}\\build_debug {project}\\CMakeFiles.txt {project}\\_deps || echo Done",
|
|
44
|
+
"md build\\usearch",
|
|
45
|
+
"git submodule update --init --recursive"
|
|
46
|
+
]
|
|
47
|
+
archs = ["x86", "AMD64"]
|
package/python/bench.py
CHANGED
|
@@ -2,11 +2,11 @@ import datetime
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import fire
|
|
4
4
|
|
|
5
|
-
from usearchh.io import load_matrix
|
|
6
|
-
|
|
7
5
|
import faiss
|
|
8
6
|
import usearch
|
|
9
7
|
|
|
8
|
+
import struct
|
|
9
|
+
import numpy as np
|
|
10
10
|
|
|
11
11
|
def measure(f) -> float:
|
|
12
12
|
a = datetime.datetime.now()
|
|
@@ -44,11 +44,23 @@ def main(
|
|
|
44
44
|
neighbors_mat = load_matrix(neighbors)
|
|
45
45
|
dim = vectors_mat.shape[1]
|
|
46
46
|
|
|
47
|
+
# Test FAISS
|
|
47
48
|
index = faiss.IndexHNSWFlat(dim, connectivity)
|
|
48
49
|
index.hnsw.efSearch = expansion_search
|
|
49
50
|
index.hnsw.efConstruction = expansion_add
|
|
50
51
|
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
51
52
|
|
|
53
|
+
# Test FAISS with default Product Quantization
|
|
54
|
+
nlist = 10000 # Number of inverted lists (number of partitions or cells).
|
|
55
|
+
nsegment = 16 # Number of segments for PQ (number of subquantizers).
|
|
56
|
+
nbit = 8 # Number of bits to encode each segment.
|
|
57
|
+
coarse_quantizer = faiss.IndexHNSWFlat(dim, connectivity)
|
|
58
|
+
index = faiss.IndexIVFPQ(coarse_quantizer, dim, nlist, nsegment, nbit)
|
|
59
|
+
index.train(vectors_mat)
|
|
60
|
+
index.nprobe = 10
|
|
61
|
+
bench_faiss(index, vectors_mat, queries_mat, neighbors_mat)
|
|
62
|
+
|
|
63
|
+
# Test USearch `Index`
|
|
52
64
|
index = usearch.Index(
|
|
53
65
|
ndim=dim,
|
|
54
66
|
expansion_add=expansion_add,
|