usearch 2.9.2 → 2.10.1
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/README.md +39 -38
- package/javascript/{types → dist/cjs}/usearch.d.ts +90 -115
- package/javascript/dist/cjs/usearch.js +531 -0
- package/javascript/dist/esm/usearch.d.ts +212 -0
- package/javascript/{usearch.js → dist/esm/usearch.js} +199 -153
- package/package.json +25 -23
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/prebuilds/win32-ia32/usearch.node +0 -0
- package/prebuilds/win32-x64/usearch.node +0 -0
- package/.clang-format +0 -10
- package/.clang-tidy +0 -60
- package/.cmake-format.py +0 -19
- package/.dockerignore +0 -93
- package/.gitattributes +0 -9
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
- package/.github/ISSUE_TEMPLATE/config.yml +0 -5
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -64
- package/.github/workflows/Dockerfile.libs +0 -36
- package/.github/workflows/prerelease.yml +0 -331
- package/.github/workflows/release.yml +0 -722
- package/.github/workflows/update_version.sh +0 -15
- package/.gitmodules +0 -9
- package/.releaserc +0 -90
- package/CITATION.cff +0 -11
- package/CMakeLists.txt +0 -324
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -361
- package/Cargo.toml +0 -19
- package/Dockerfile +0 -16
- package/VERSION +0 -1
- package/assets/unum.png +0 -0
- package/assets/usearch-approaches-transparent.png +0 -0
- package/assets/usearch-approaches-white.png +0 -0
- package/assets/usearch-neighbor-types.png +0 -0
- package/binding.gyp +0 -87
- package/build.gradle +0 -204
- package/build.rs +0 -18
- package/c/CMakeLists.txt +0 -25
- package/c/README.md +0 -34
- package/c/lib.cpp +0 -358
- package/c/test.c +0 -382
- package/c/usearch.h +0 -311
- package/cmake/clipp.cmake +0 -7
- package/cmake/config.cmake.in +0 -15
- package/cmake/emscripten.cmake +0 -8
- package/cmake/jemalloc.cmake +0 -62
- package/cmake/pkg-config.pc.in +0 -4
- package/cmake/usearchConfigVersion.cmake.in +0 -20
- package/conanfile.py +0 -26
- package/cpp/CMakeLists.txt +0 -14
- package/cpp/README.md +0 -161
- package/cpp/bench.cpp +0 -618
- package/cpp/test.cpp +0 -323
- package/fp16/CMakeLists.txt +0 -176
- package/fp16/LICENSE +0 -11
- package/fp16/README.md +0 -20
- package/fp16/bench/alt-element.cc +0 -89
- package/fp16/bench/from-alt-array.cc +0 -175
- package/fp16/bench/from-ieee-array.cc +0 -399
- package/fp16/bench/ieee-element.cc +0 -272
- package/fp16/bench/to-alt-array.cc +0 -91
- package/fp16/bench/to-ieee-array.cc +0 -303
- package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
- package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
- package/fp16/cmake/DownloadPSimd.cmake +0 -15
- package/fp16/configure.py +0 -65
- package/fp16/confu.yaml +0 -8
- package/fp16/include/fp16/__init__.py +0 -0
- package/fp16/include/fp16/avx.py +0 -50
- package/fp16/include/fp16/avx2.py +0 -53
- package/fp16/include/fp16/bitcasts.h +0 -92
- package/fp16/include/fp16/fp16.h +0 -451
- package/fp16/include/fp16/psimd.h +0 -131
- package/fp16/include/fp16.h +0 -11
- package/fp16/jni/Android.mk +0 -6
- package/fp16/jni/Application.mk +0 -4
- package/fp16/test/alt-from-fp32-value.cc +0 -498
- package/fp16/test/alt-to-fp32-bits.cc +0 -263
- package/fp16/test/alt-to-fp32-psimd.cc +0 -145
- package/fp16/test/alt-to-fp32-value.cc +0 -348
- package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
- package/fp16/test/bitcasts.cc +0 -56
- package/fp16/test/ieee-from-fp32-value.cc +0 -523
- package/fp16/test/ieee-to-fp32-bits.cc +0 -333
- package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
- package/fp16/test/ieee-to-fp32-value.cc +0 -420
- package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
- package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
- package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
- package/fp16/test/peachpy/stubs.py +0 -38
- package/fp16/test/tables.cc +0 -530
- package/fp16/test/tables.h +0 -13
- package/fp16/third-party/THHalf.h +0 -92
- package/fp16/third-party/eigen-half.h +0 -170
- package/fp16/third-party/float16-compressor.h +0 -80
- package/fp16/third-party/half.hpp +0 -2916
- package/fp16/third-party/npy-halffloat.h +0 -328
- package/include/usearch/index.hpp +0 -3836
- package/include/usearch/index_dense.hpp +0 -2004
- package/include/usearch/index_plugins.hpp +0 -2213
- package/javascript/lib.cpp +0 -341
- package/javascript/types/usearch.d.ts.map +0 -1
- package/javascript/usearch.test.js +0 -74
- package/package-ci.json +0 -10
- package/pyproject.toml +0 -51
- package/setup.py +0 -201
- package/simsimd/.releaserc +0 -86
- package/simsimd/CONTRIBUTING.md +0 -88
- package/simsimd/Cargo.lock +0 -709
- package/simsimd/Cargo.toml +0 -44
- package/simsimd/LICENSE +0 -201
- package/simsimd/README.md +0 -317
- package/simsimd/binding.gyp +0 -36
- package/simsimd/build.rs +0 -20
- package/simsimd/include/simsimd/binary.h +0 -245
- package/simsimd/include/simsimd/complex.h +0 -174
- package/simsimd/include/simsimd/probability.h +0 -548
- package/simsimd/include/simsimd/simsimd.h +0 -465
- package/simsimd/include/simsimd/spatial.h +0 -1267
- package/simsimd/include/simsimd/types.h +0 -208
- package/simsimd/javascript/bench.js +0 -127
- package/simsimd/javascript/fallback.ts +0 -193
- package/simsimd/javascript/lib.c +0 -102
- package/simsimd/javascript/node-gyp-build.d.ts +0 -1
- package/simsimd/javascript/simsimd.ts +0 -124
- package/simsimd/javascript/test.mjs +0 -153
- package/simsimd/javascript/tsconfig-base.json +0 -25
- package/simsimd/javascript/tsconfig-cjs.json +0 -8
- package/simsimd/javascript/tsconfig-esm.json +0 -8
- package/simsimd/package-lock.json +0 -1688
- package/simsimd/package.json +0 -61
- package/simsimd/rust/benches/cosine.rs +0 -27
- package/simsimd/rust/benches/native.rs +0 -30
- package/simsimd/rust/benches/sqeuclidean.rs +0 -27
- package/simsimd/rust/lib.c +0 -45
- package/simsimd/rust/lib.rs +0 -550
- package/sqlite/README.md +0 -178
- package/sqlite/lib.cpp +0 -325
- package/stringzilla/CONTRIBUTING.md +0 -446
- package/stringzilla/Cargo.lock +0 -25
- package/stringzilla/Cargo.toml +0 -26
- package/stringzilla/LICENSE +0 -201
- package/stringzilla/README.md +0 -1422
- package/stringzilla/VERSION +0 -1
- package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
- package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
- package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
- package/stringzilla/binding.gyp +0 -16
- package/stringzilla/build.rs +0 -87
- package/stringzilla/c/lib.c +0 -337
- package/stringzilla/cli/README.md +0 -65
- package/stringzilla/cli/__init__.py +0 -0
- package/stringzilla/cli/split.py +0 -105
- package/stringzilla/cli/wc.py +0 -117
- package/stringzilla/include/stringzilla/experimental.h +0 -845
- package/stringzilla/include/stringzilla/module.modulemap +0 -9
- package/stringzilla/include/stringzilla/stringzilla.h +0 -5370
- package/stringzilla/include/stringzilla/stringzilla.hpp +0 -3838
- package/stringzilla/javascript/lib.c +0 -125
- package/stringzilla/javascript/stringzilla.js +0 -22
- package/stringzilla/package-ci.json +0 -10
- package/stringzilla/package.json +0 -32
- package/stringzilla/rust/lib.rs +0 -873
- package/stringzilla/swift/StringProtocol+StringZilla.swift +0 -280
- package/stringzilla/swift/Test.swift +0 -60
- package/tsconfig.json +0 -21
- package/wasmer.toml +0 -12
- package/winlibinstaller.bat +0 -42
- /package/{simsimd/javascript/dist-package-cjs.json → javascript/dist/cjs/package.json} +0 -0
- /package/{simsimd/javascript/dist-package-esm.json → javascript/dist/esm/package.json} +0 -0
|
@@ -1,45 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import build from "node-gyp-build";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { getFileName, getRoot } from "bindings";
|
|
5
|
+
const compiled = build(getBuildDir(getDirName()));
|
|
3
6
|
/**
|
|
4
7
|
* Enumeration representing the various metric kinds used to measure the distance between vectors in the index.
|
|
5
8
|
* @enum {string}
|
|
6
9
|
* @readonly
|
|
7
10
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
export var MetricKind;
|
|
12
|
+
(function (MetricKind) {
|
|
13
|
+
MetricKind["Unknown"] = "unknown";
|
|
14
|
+
MetricKind["Cos"] = "cos";
|
|
15
|
+
MetricKind["IP"] = "ip";
|
|
16
|
+
MetricKind["L2sq"] = "l2sq";
|
|
17
|
+
MetricKind["Haversine"] = "haversine";
|
|
18
|
+
MetricKind["Divergence"] = "divergence";
|
|
19
|
+
MetricKind["Pearson"] = "pearson";
|
|
20
|
+
MetricKind["Jaccard"] = "jaccard";
|
|
21
|
+
MetricKind["Hamming"] = "hamming";
|
|
22
|
+
MetricKind["Tanimoto"] = "tanimoto";
|
|
23
|
+
MetricKind["Sorensen"] = "sorensen";
|
|
24
|
+
})(MetricKind || (MetricKind = {}));
|
|
22
25
|
/**
|
|
23
26
|
* Enumeration representing the various scalar kinds used to define the type of scalar values in vectors.
|
|
24
27
|
* @enum {string}
|
|
25
28
|
* @readonly
|
|
26
29
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
export var ScalarKind;
|
|
31
|
+
(function (ScalarKind) {
|
|
32
|
+
ScalarKind["Unknown"] = "unknown";
|
|
33
|
+
ScalarKind["F32"] = "f32";
|
|
34
|
+
ScalarKind["F64"] = "f64";
|
|
35
|
+
ScalarKind["F16"] = "f16";
|
|
36
|
+
ScalarKind["I8"] = "i8";
|
|
37
|
+
ScalarKind["B1"] = "b1";
|
|
38
|
+
})(ScalarKind || (ScalarKind = {}));
|
|
36
39
|
/**
|
|
37
40
|
* Represents a set of search results.
|
|
38
41
|
*/
|
|
39
|
-
class Matches {
|
|
42
|
+
export class Matches {
|
|
43
|
+
keys;
|
|
44
|
+
distances;
|
|
40
45
|
/**
|
|
41
46
|
* Constructs a Matches object.
|
|
42
|
-
*
|
|
47
|
+
*
|
|
43
48
|
* @param {BigUint64Array} keys - The keys of the nearest neighbors found.
|
|
44
49
|
* @param {Float32Array} distances - The distances of the nearest neighbors found.
|
|
45
50
|
*/
|
|
@@ -48,18 +53,21 @@ class Matches {
|
|
|
48
53
|
this.distances = distances;
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
|
-
|
|
52
56
|
/**
|
|
53
57
|
* Represents a set of batched search results.
|
|
54
58
|
*/
|
|
55
|
-
class BatchMatches {
|
|
59
|
+
export class BatchMatches {
|
|
60
|
+
keys;
|
|
61
|
+
distances;
|
|
62
|
+
counts;
|
|
63
|
+
k;
|
|
56
64
|
/**
|
|
57
65
|
* Constructs a BatchMatches object.
|
|
58
|
-
*
|
|
66
|
+
*
|
|
59
67
|
* @param {BigUint64Array} keys - The keys of the nearest neighbors found in the batch.
|
|
60
68
|
* @param {Float32Array} distances - The distances of the nearest neighbors found in the batch.
|
|
61
69
|
* @param {BigUint64Array} counts - The number of neighbors found for each query in the batch.
|
|
62
|
-
* @param {
|
|
70
|
+
* @param {number} k - The limit for search results per query in the batch.
|
|
63
71
|
*/
|
|
64
72
|
constructor(keys, distances, counts, k) {
|
|
65
73
|
this.keys = keys;
|
|
@@ -67,10 +75,9 @@ class BatchMatches {
|
|
|
67
75
|
this.counts = counts;
|
|
68
76
|
this.k = k;
|
|
69
77
|
}
|
|
70
|
-
|
|
71
78
|
/**
|
|
72
79
|
* Retrieves a Matches object at the specified index in the batch.
|
|
73
|
-
*
|
|
80
|
+
*
|
|
74
81
|
* @param {number} i - The index at which to retrieve the Matches object.
|
|
75
82
|
* @returns {Matches} - A Matches object representing the search results at the specified index in the batch.
|
|
76
83
|
*/
|
|
@@ -82,61 +89,74 @@ class BatchMatches {
|
|
|
82
89
|
return new Matches(keysSlice, distancesSlice);
|
|
83
90
|
}
|
|
84
91
|
}
|
|
85
|
-
|
|
86
92
|
function isOneKey(keys) {
|
|
87
|
-
return (!Number.isNaN(keys) && typeof keys ===
|
|
93
|
+
return ((!Number.isNaN(keys) && typeof keys === "number") ||
|
|
94
|
+
typeof keys === "bigint");
|
|
88
95
|
}
|
|
89
|
-
|
|
90
96
|
function normalizeKeys(keys) {
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
if (keys instanceof BigUint64Array) {
|
|
98
|
+
return keys;
|
|
99
|
+
}
|
|
100
|
+
let normalizedKeys;
|
|
101
|
+
if ((typeof keys === "number" && !Number.isNaN(keys)) ||
|
|
102
|
+
typeof keys === "bigint") {
|
|
103
|
+
normalizedKeys = BigUint64Array.of(BigInt(keys));
|
|
104
|
+
}
|
|
105
|
+
else if (Array.isArray(keys)) {
|
|
106
|
+
const bigintkeys = keys.map((key) => {
|
|
107
|
+
if (typeof key === "bigint") {
|
|
108
|
+
return key;
|
|
109
|
+
}
|
|
110
|
+
else if (typeof key === "number" &&
|
|
111
|
+
!Number.isNaN(key) &&
|
|
112
|
+
Number.isInteger(key) &&
|
|
113
|
+
key >= 0) {
|
|
114
|
+
return BigInt(key);
|
|
115
|
+
}
|
|
116
|
+
throw new Error("All keys must be positive integers or bigints.");
|
|
98
117
|
});
|
|
99
|
-
|
|
100
|
-
}
|
|
118
|
+
normalizedKeys = BigUint64Array.from(bigintkeys);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
101
121
|
throw new Error("Keys must be a number, bigint, an array of numbers or bigints, or a BigUint64Array.");
|
|
102
122
|
}
|
|
103
|
-
return
|
|
123
|
+
return normalizedKeys;
|
|
104
124
|
}
|
|
105
|
-
|
|
106
125
|
function isVector(vectors) {
|
|
107
|
-
return vectors instanceof Float32Array ||
|
|
126
|
+
return (vectors instanceof Float32Array ||
|
|
127
|
+
vectors instanceof Float64Array ||
|
|
128
|
+
vectors instanceof Int8Array);
|
|
108
129
|
}
|
|
109
|
-
|
|
110
130
|
function normalizeVectors(vectors, dimensions, targetType = Float32Array) {
|
|
111
131
|
let flattenedVectors;
|
|
112
132
|
if (isVector(vectors)) {
|
|
113
|
-
flattenedVectors =
|
|
114
|
-
|
|
133
|
+
flattenedVectors =
|
|
134
|
+
vectors.constructor === targetType
|
|
135
|
+
? vectors
|
|
136
|
+
: new targetType(vectors);
|
|
137
|
+
}
|
|
138
|
+
else if (Array.isArray(vectors)) {
|
|
115
139
|
let totalLength = 0;
|
|
116
|
-
for (const vec of vectors)
|
|
117
|
-
|
|
140
|
+
for (const vec of vectors)
|
|
141
|
+
totalLength += vec.length;
|
|
118
142
|
flattenedVectors = new targetType(totalLength);
|
|
119
143
|
let offset = 0;
|
|
120
144
|
for (const vec of vectors) {
|
|
121
145
|
flattenedVectors.set(vec, offset);
|
|
122
146
|
offset += vec.length;
|
|
123
147
|
}
|
|
124
|
-
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
125
150
|
throw new Error("Vectors must be a TypedArray or an array of arrays.");
|
|
126
151
|
}
|
|
127
|
-
|
|
128
152
|
if (flattenedVectors.length % dimensions !== 0)
|
|
129
153
|
throw new Error("The size of the flattened vectors must be a multiple of the dimension of the vectors.");
|
|
130
|
-
|
|
131
154
|
return flattenedVectors;
|
|
132
155
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class Index {
|
|
136
|
-
|
|
156
|
+
export class Index {
|
|
137
157
|
/**
|
|
138
158
|
* Constructs a new index.
|
|
139
|
-
*
|
|
159
|
+
*
|
|
140
160
|
* @param {(number | {dimensions: number, metric: MetricKind = MetricKind.Cos, quantization: ScalarKind = ScalarKind.F32, connectivity: number = 0, expansion_add: number = 0, expansion_search: number = 0, multi: boolean = false})} dimensionsOrConfigs
|
|
141
161
|
* @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
|
|
142
162
|
* @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
|
|
@@ -148,53 +168,62 @@ class Index {
|
|
|
148
168
|
*/
|
|
149
169
|
constructor(dimensionsOrConfigs, metric = MetricKind.Cos, quantization = ScalarKind.F32, connectivity = 0, expansion_add = 0, expansion_search = 0, multi = false) {
|
|
150
170
|
let dimensions;
|
|
151
|
-
if (typeof dimensionsOrConfigs ===
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
} else if ((typeof dimensionsOrConfigs === 'number' && !Number.isNaN(dimensionsOrConfigs)) || typeof dimensionsOrConfigs === 'bigint') {
|
|
171
|
+
if ((typeof dimensionsOrConfigs === "number" &&
|
|
172
|
+
!Number.isNaN(dimensionsOrConfigs)) ||
|
|
173
|
+
typeof dimensionsOrConfigs === "bigint") {
|
|
155
174
|
// Parameters are provided as individual arguments
|
|
156
175
|
dimensions = dimensionsOrConfigs;
|
|
157
|
-
}
|
|
176
|
+
}
|
|
177
|
+
else if (typeof dimensionsOrConfigs === "object" &&
|
|
178
|
+
dimensionsOrConfigs !== null) {
|
|
179
|
+
// Parameters are provided as an object
|
|
180
|
+
({
|
|
181
|
+
dimensions,
|
|
182
|
+
metric = MetricKind.Cos,
|
|
183
|
+
quantization = ScalarKind.F32,
|
|
184
|
+
connectivity = 0,
|
|
185
|
+
expansion_add = 0,
|
|
186
|
+
expansion_search = 0,
|
|
187
|
+
multi = false,
|
|
188
|
+
} = dimensionsOrConfigs);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
158
191
|
throw new Error("Invalid arguments. Expected either individual arguments or a single object argument.");
|
|
159
192
|
}
|
|
160
|
-
|
|
161
|
-
if ((typeof dimensions !== 'bigint' && (!Number.isInteger(dimensions) || dimensions <= 0)) ||
|
|
193
|
+
if ((typeof dimensions !== 'bigint' && (!Number.isInteger(dimensions) || dimensions <= 0)) ||
|
|
162
194
|
(typeof connectivity !== 'bigint' && (!Number.isInteger(connectivity) || connectivity < 0)) ||
|
|
163
195
|
(typeof expansion_add !== 'bigint' && (!Number.isInteger(expansion_add) || expansion_add < 0)) ||
|
|
164
196
|
(typeof expansion_search !== 'bigint' && (!Number.isInteger(expansion_search) || expansion_search < 0))) {
|
|
165
197
|
throw new Error("`dimensions`, `connectivity`, `expansion_add`, and `expansion_search` must be non-negative integers, with `dimensions` being positive.");
|
|
166
198
|
}
|
|
167
|
-
|
|
168
|
-
if (typeof multi !== 'boolean') {
|
|
199
|
+
if (typeof multi !== "boolean") {
|
|
169
200
|
throw new Error("`multi` must be a boolean value.");
|
|
170
201
|
}
|
|
171
|
-
|
|
172
202
|
if (!Object.values(MetricKind).includes(metric)) {
|
|
173
|
-
throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(
|
|
203
|
+
throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(", ")}`);
|
|
174
204
|
}
|
|
175
|
-
|
|
176
205
|
if (!Object.values(ScalarKind).includes(quantization)) {
|
|
177
|
-
throw new Error(`Invalid quantization: ${quantization}. It must be one of: ${Object.values(ScalarKind).join(
|
|
206
|
+
throw new Error(`Invalid quantization: ${quantization}. It must be one of: ${Object.values(ScalarKind).join(", ")}`);
|
|
178
207
|
}
|
|
179
|
-
|
|
180
|
-
this
|
|
208
|
+
// @ts-expect-error
|
|
209
|
+
this.#compiledIndex = new compiled.CompiledIndex(dimensions, metric, quantization, connectivity, expansion_add, expansion_search, multi);
|
|
181
210
|
}
|
|
182
|
-
|
|
211
|
+
#compiledIndex;
|
|
183
212
|
/**
|
|
184
213
|
* Add vectors to the index.
|
|
185
|
-
*
|
|
214
|
+
*
|
|
186
215
|
* This method accepts vectors and their corresponding keys for indexing.
|
|
187
216
|
* Each key should correspond to a vector. If a single key is provided,
|
|
188
217
|
* it is broadcasted to match the number of provided vectors.
|
|
189
|
-
*
|
|
218
|
+
*
|
|
190
219
|
* Vectors should be provided as a flat typed array representing a matrix
|
|
191
220
|
* where each row is a vector to be indexed. The matrix should have a size
|
|
192
221
|
* of n * d, where n is the number of vectors, and d is the dimensionality
|
|
193
222
|
* of the vectors.
|
|
194
|
-
*
|
|
223
|
+
*
|
|
195
224
|
* Keys should be provided as a BigInt or an array-like object of BigInts
|
|
196
225
|
* representing the unique identifier for each vector.
|
|
197
|
-
*
|
|
226
|
+
*
|
|
198
227
|
* @param {bigint|bigint[]|BigUint64Array} keys - Input identifiers for every vector.
|
|
199
228
|
* If a single key is provided, it is associated with all provided vectors.
|
|
200
229
|
* @param {Float32Array|Float64Array|Int8Array} vectors - Input matrix representing vectors,
|
|
@@ -204,173 +233,169 @@ class Index {
|
|
|
204
233
|
*/
|
|
205
234
|
add(keys, vectors) {
|
|
206
235
|
let normalizedKeys = normalizeKeys(keys);
|
|
207
|
-
let normalizedVectors = normalizeVectors(vectors, this.
|
|
208
|
-
let countVectors = normalizedVectors.length / this.
|
|
209
|
-
|
|
236
|
+
let normalizedVectors = normalizeVectors(vectors, this.#compiledIndex.dimensions());
|
|
237
|
+
let countVectors = normalizedVectors.length / this.#compiledIndex.dimensions();
|
|
210
238
|
// If a single key is provided but there are multiple vectors,
|
|
211
239
|
// broadcast the single key value to match the number of vectors
|
|
212
240
|
if (normalizedKeys.length === 1 && countVectors > 1) {
|
|
213
241
|
normalizedKeys = BigUint64Array.from({ length: countVectors }, () => normalizedKeys[0]);
|
|
214
|
-
}
|
|
242
|
+
}
|
|
243
|
+
else if (normalizedKeys.length !== countVectors) {
|
|
215
244
|
throw new Error(`The length of keys (${normalizedKeys.length}) must match the number of vectors (${countVectors}) or be a single key.`);
|
|
216
245
|
}
|
|
217
|
-
|
|
218
246
|
// Call the compiled method
|
|
219
|
-
this.
|
|
247
|
+
this.#compiledIndex.add(normalizedKeys, normalizedVectors);
|
|
220
248
|
}
|
|
221
|
-
|
|
222
249
|
/**
|
|
223
250
|
* Perform a k-nearest neighbor search on the index.
|
|
224
|
-
*
|
|
251
|
+
*
|
|
225
252
|
* This method accepts a matrix of query vectors and returns the closest vectors
|
|
226
253
|
* from the index for each query. The method returns an object containing the keys,
|
|
227
254
|
* distances, and counts of the matches found.
|
|
228
|
-
*
|
|
255
|
+
*
|
|
229
256
|
* Vectors should be provided as a flat typed array representing a matrix where
|
|
230
257
|
* each row is a vector. The matrix should be of size n * d, where n is the
|
|
231
258
|
* number of query vectors, and d is their dimensionality.
|
|
232
|
-
*
|
|
259
|
+
*
|
|
233
260
|
* The parameter `k` specifies the number of nearest neighbors to return for each
|
|
234
261
|
* query vector. If there are not enough results for a query, the result array is
|
|
235
262
|
* padded with -1s.
|
|
236
|
-
*
|
|
237
|
-
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of
|
|
263
|
+
*
|
|
264
|
+
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of TypedArray.
|
|
238
265
|
* @param {number} k - The number of nearest neighbors to search for each query vector.
|
|
239
266
|
* @return {Matches|BatchMatches} - Search results for one or more queries, containing keys, distances, and counts of the matches found.
|
|
240
267
|
* @throws Will throw an error if `k` is not a positive integer or if the size of the vectors is not a multiple of dimensions.
|
|
241
|
-
* @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of
|
|
268
|
+
* @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of TypedArray) or if its flattened size is not a multiple of dimensions.
|
|
242
269
|
*/
|
|
243
270
|
search(vectors, k) {
|
|
244
|
-
if ((!Number.isNaN(k) && typeof k !==
|
|
271
|
+
if ((!Number.isNaN(k) && typeof k !== "number") || k <= 0) {
|
|
245
272
|
throw new Error("`k` must be a positive integer representing the number of nearest neighbors to search for.");
|
|
246
273
|
}
|
|
247
|
-
|
|
248
|
-
const normalizedVectors = normalizeVectors(vectors, this._compiledIndex.dimensions());
|
|
249
|
-
|
|
274
|
+
const normalizedVectors = normalizeVectors(vectors, this.#compiledIndex.dimensions());
|
|
250
275
|
// Call the compiled method and create Matches or BatchMatches object with the result
|
|
251
|
-
const result = this.
|
|
252
|
-
const countInQueries = normalizedVectors.length / Number(this.
|
|
253
|
-
const batchMatches = new BatchMatches(result
|
|
254
|
-
|
|
276
|
+
const result = this.#compiledIndex.search(normalizedVectors, k);
|
|
277
|
+
const countInQueries = normalizedVectors.length / Number(this.#compiledIndex.dimensions());
|
|
278
|
+
const batchMatches = new BatchMatches(...result, k);
|
|
255
279
|
if (countInQueries === 1) {
|
|
256
280
|
return batchMatches.get(0);
|
|
257
|
-
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
258
283
|
return batchMatches;
|
|
259
284
|
}
|
|
260
285
|
}
|
|
261
|
-
|
|
262
286
|
/**
|
|
263
287
|
* Verifies the presence of one or more keys in the index.
|
|
264
|
-
*
|
|
265
|
-
* This method accepts one or multiple keys as input and returns a boolean or
|
|
288
|
+
*
|
|
289
|
+
* This method accepts one or multiple keys as input and returns a boolean or
|
|
266
290
|
* an array of booleans indicating whether each key is present in the index.
|
|
267
|
-
*
|
|
291
|
+
*
|
|
268
292
|
* @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be checked for presence in the index.
|
|
269
293
|
* @return {boolean|boolean[]} - Returns true if a single key is contained in the index, false otherwise. Returns an array of booleans corresponding to the presence of each key in the index when multiple keys are provided.
|
|
270
294
|
* @throws Will throw an error if keys are not integers.
|
|
271
295
|
*/
|
|
272
296
|
contains(keys) {
|
|
273
297
|
let normalizedKeys = normalizeKeys(keys);
|
|
274
|
-
let normalizedResults = this.
|
|
298
|
+
let normalizedResults = this.#compiledIndex.contains(normalizedKeys);
|
|
275
299
|
if (isOneKey(keys))
|
|
276
300
|
return normalizedResults[0];
|
|
277
301
|
else
|
|
278
302
|
return normalizedResults;
|
|
279
303
|
}
|
|
280
|
-
|
|
281
304
|
/**
|
|
282
305
|
* Counts the number of times keys shows up in the index.
|
|
283
|
-
*
|
|
306
|
+
*
|
|
284
307
|
* @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be enumerated.
|
|
285
308
|
* @return {number|number[]} - Returns the number of vectors found when a single key is provided. Returns an array of big integers corresponding to the number of vectors found for each key when multiple keys are provided.
|
|
286
309
|
* @throws Will throw an error if keys are not integers.
|
|
287
310
|
*/
|
|
288
311
|
count(keys) {
|
|
289
312
|
let normalizedKeys = normalizeKeys(keys);
|
|
290
|
-
let normalizedResults = this.
|
|
313
|
+
let normalizedResults = this.#compiledIndex.count(normalizedKeys);
|
|
291
314
|
if (isOneKey(keys))
|
|
292
315
|
return normalizedResults[0];
|
|
293
316
|
else
|
|
294
317
|
return normalizedResults;
|
|
295
318
|
}
|
|
296
|
-
|
|
297
319
|
/**
|
|
298
320
|
* Removes one or multiple vectors from the index.
|
|
299
|
-
*
|
|
321
|
+
*
|
|
300
322
|
* This method accepts one or multiple keys as input and removes the corresponding vectors from the index.
|
|
301
323
|
* It returns the number of vectors actually removed for each key provided.
|
|
302
|
-
*
|
|
324
|
+
*
|
|
303
325
|
* @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be removed.
|
|
304
326
|
* @return {number|number[]} - Returns the number of vectors deleted when a single key is provided. Returns an array of big integers corresponding to the number of vectors deleted for each key when multiple keys are provided.
|
|
305
327
|
* @throws Will throw an error if keys are not integers.
|
|
306
328
|
*/
|
|
307
329
|
remove(keys) {
|
|
308
330
|
let normalizedKeys = normalizeKeys(keys);
|
|
309
|
-
let normalizedResults = this.
|
|
331
|
+
let normalizedResults = this.#compiledIndex.remove(normalizedKeys);
|
|
310
332
|
if (isOneKey(keys))
|
|
311
333
|
return normalizedResults[0];
|
|
312
334
|
else
|
|
313
335
|
return normalizedResults;
|
|
314
336
|
}
|
|
315
|
-
|
|
316
337
|
/**
|
|
317
338
|
* Returns the dimensionality of vectors.
|
|
318
339
|
* @return {number} The dimensionality of vectors.
|
|
319
340
|
*/
|
|
320
|
-
dimensions() {
|
|
321
|
-
|
|
341
|
+
dimensions() {
|
|
342
|
+
return this.#compiledIndex.dimensions();
|
|
343
|
+
}
|
|
322
344
|
/**
|
|
323
345
|
* Returns connectivity.
|
|
324
346
|
* @return {number} The connectivity of index.
|
|
325
347
|
*/
|
|
326
|
-
connectivity() {
|
|
327
|
-
|
|
348
|
+
connectivity() {
|
|
349
|
+
return this.#compiledIndex.connectivity();
|
|
350
|
+
}
|
|
328
351
|
/**
|
|
329
352
|
* Returns the number of vectors currently indexed.
|
|
330
353
|
* @return {number} The number of vectors currently indexed.
|
|
331
354
|
*/
|
|
332
|
-
size() {
|
|
333
|
-
|
|
355
|
+
size() {
|
|
356
|
+
return this.#compiledIndex.size();
|
|
357
|
+
}
|
|
334
358
|
/**
|
|
335
359
|
* Returns index capacity.
|
|
336
360
|
* @return {number} The capacity of index.
|
|
337
361
|
*/
|
|
338
|
-
capacity() {
|
|
339
|
-
|
|
340
|
-
|
|
362
|
+
capacity() {
|
|
363
|
+
return this.#compiledIndex.capacity();
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
341
366
|
* Write index to a file.
|
|
342
367
|
* @param {string} path File path to write.
|
|
343
368
|
* @throws Will throw an error if `path` is not a string.
|
|
344
369
|
*/
|
|
345
370
|
save(path) {
|
|
346
|
-
if (typeof path !==
|
|
347
|
-
|
|
371
|
+
if (typeof path !== "string")
|
|
372
|
+
throw new Error("`path` must be a string representing the file path to write.");
|
|
373
|
+
this.#compiledIndex.save(path);
|
|
348
374
|
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
375
|
+
/**
|
|
351
376
|
* Load index from a file.
|
|
352
377
|
* @param {string} path File path to read.
|
|
353
378
|
* @throws Will throw an error if `path` is not a string.
|
|
354
379
|
*/
|
|
355
380
|
load(path) {
|
|
356
|
-
if (typeof path !==
|
|
357
|
-
|
|
381
|
+
if (typeof path !== "string")
|
|
382
|
+
throw new Error("`path` must be a string representing the file path to read.");
|
|
383
|
+
this.#compiledIndex.load(path);
|
|
358
384
|
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
385
|
+
/**
|
|
361
386
|
* View index from a file, without loading into RAM.
|
|
362
387
|
* @param {string} path File path to read.
|
|
363
388
|
* @throws Will throw an error if `path` is not a string.
|
|
364
389
|
*/
|
|
365
390
|
view(path) {
|
|
366
|
-
if (typeof path !==
|
|
367
|
-
|
|
391
|
+
if (typeof path !== "string")
|
|
392
|
+
throw new Error("`path` must be a string representing the file path to read.");
|
|
393
|
+
this.#compiledIndex.view(path);
|
|
368
394
|
}
|
|
369
395
|
}
|
|
370
|
-
|
|
371
396
|
/**
|
|
372
397
|
* Performs an exact search on the given dataset to find the best matching vectors for each query.
|
|
373
|
-
*
|
|
398
|
+
*
|
|
374
399
|
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} dataset - The dataset containing vectors to be searched. It can be a TypedArray or an array of arrays.
|
|
375
400
|
* @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} queries - The queries containing vectors to search for in the dataset. It can be a TypedArray or an array of arrays.
|
|
376
401
|
* @param {number} dimensions - The dimensionality of the vectors in both the dataset and the queries. It defines the number of elements in each vector.
|
|
@@ -382,41 +407,40 @@ class Index {
|
|
|
382
407
|
* @throws Will throw an error if `dataset` and `queries` are not valid input types (TypedArray or an array of arrays).
|
|
383
408
|
* @throws Will throw an error if the sizes of the flattened `dataset` and `queries` are not multiples of `dimensions`.
|
|
384
409
|
* @throws Will throw an error if `count` is greater than the number of vectors in the `dataset`.
|
|
385
|
-
*
|
|
410
|
+
*
|
|
386
411
|
* @example
|
|
387
412
|
* const dataset = [[1.0, 2.0], [3.0, 4.0]]; // Two vectors: [1.0, 2.0] and [3.0, 4.0]
|
|
388
413
|
* const queries = [[1.5, 2.5]]; // One vector: [1.5, 2.5]
|
|
389
414
|
* const dimensions = 2; // The number of elements in each vector.
|
|
390
415
|
* const count = 1; // The number of nearest neighbors to return for each query.
|
|
391
416
|
* const metric = MetricKind.IP; // Using the Inner Product distance metric.
|
|
392
|
-
*
|
|
417
|
+
*
|
|
393
418
|
* const result = exactSearch(dataset, queries, dimensions, count, metric);
|
|
394
|
-
* // result might be:
|
|
419
|
+
* // result might be:
|
|
395
420
|
* // {
|
|
396
421
|
* // keys: BigUint64Array [ 1n ],
|
|
397
422
|
* // distances: Float32Array [ some_value ],
|
|
398
423
|
* // }
|
|
399
424
|
*/
|
|
400
425
|
function exactSearch(dataset, queries, dimensions, count, metric) {
|
|
401
|
-
|
|
402
426
|
// Validate and normalize the dimensions and count
|
|
403
427
|
dimensions = Number(dimensions);
|
|
404
428
|
count = Number(count);
|
|
405
429
|
if (count <= 0 || dimensions <= 0) {
|
|
406
430
|
throw new Error("Dimensions and count must be positive integers.");
|
|
407
431
|
}
|
|
408
|
-
|
|
409
432
|
// Validate metric
|
|
410
433
|
if (!Object.values(MetricKind).includes(metric)) {
|
|
411
|
-
throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(
|
|
434
|
+
throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(", ")}`);
|
|
412
435
|
}
|
|
413
|
-
|
|
414
436
|
// Flatten and normalize dataset and queries if they are arrays of arrays
|
|
415
437
|
let targetType;
|
|
416
|
-
if (dataset instanceof Float64Array)
|
|
417
|
-
|
|
418
|
-
else
|
|
419
|
-
|
|
438
|
+
if (dataset instanceof Float64Array)
|
|
439
|
+
targetType = Float64Array;
|
|
440
|
+
else if (dataset instanceof Int8Array)
|
|
441
|
+
targetType = Int8Array;
|
|
442
|
+
else
|
|
443
|
+
targetType = Float32Array; // default to Float32Array if dataset is not Float64Array or Int8Array
|
|
420
444
|
dataset = normalizeVectors(dataset, dimensions, targetType);
|
|
421
445
|
queries = normalizeVectors(queries, dimensions, targetType);
|
|
422
446
|
const countInDataset = dataset.length / dimensions;
|
|
@@ -424,18 +448,16 @@ function exactSearch(dataset, queries, dimensions, count, metric) {
|
|
|
424
448
|
if (count > countInDataset) {
|
|
425
449
|
throw new Error("Count must be equal or smaller than the number of vectors in the dataset.");
|
|
426
450
|
}
|
|
427
|
-
|
|
428
451
|
// Call the compiled function with the normalized input
|
|
429
452
|
const result = compiled.exactSearch(dataset, queries, dimensions, count, metric);
|
|
430
|
-
|
|
431
453
|
// Create and return a Matches or BatchMatches object with the result
|
|
432
454
|
if (countInQueries == 1) {
|
|
433
455
|
return new Matches(result[0], result[1]);
|
|
434
|
-
}
|
|
435
|
-
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
return new BatchMatches(...result, count);
|
|
436
459
|
}
|
|
437
460
|
}
|
|
438
|
-
|
|
439
461
|
module.exports = {
|
|
440
462
|
Index,
|
|
441
463
|
MetricKind,
|
|
@@ -444,3 +466,27 @@ module.exports = {
|
|
|
444
466
|
BatchMatches,
|
|
445
467
|
exactSearch,
|
|
446
468
|
};
|
|
469
|
+
// utility functions to help find native builds
|
|
470
|
+
function getBuildDir(dir) {
|
|
471
|
+
if (existsSync(path.join(dir, "build")))
|
|
472
|
+
return dir;
|
|
473
|
+
if (existsSync(path.join(dir, "prebuilds")))
|
|
474
|
+
return dir;
|
|
475
|
+
if (path.basename(dir) === ".next") {
|
|
476
|
+
// special case for next.js on custom node (not vercel)
|
|
477
|
+
const sideways = path.join(dir, "..", "node_modules", "usearch");
|
|
478
|
+
if (existsSync(sideways))
|
|
479
|
+
return getBuildDir(sideways);
|
|
480
|
+
}
|
|
481
|
+
if (dir === "/")
|
|
482
|
+
throw new Error("Could not find native build for usearch");
|
|
483
|
+
return getBuildDir(path.join(dir, ".."));
|
|
484
|
+
}
|
|
485
|
+
function getDirName() {
|
|
486
|
+
try {
|
|
487
|
+
if (__dirname)
|
|
488
|
+
return __dirname;
|
|
489
|
+
}
|
|
490
|
+
catch (e) { }
|
|
491
|
+
return getRoot(getFileName());
|
|
492
|
+
}
|