usearch 2.8.10 → 2.8.13

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.
@@ -127,7 +127,7 @@ jobs:
127
127
  wget https://apt.llvm.org/llvm.sh
128
128
  chmod +x llvm.sh
129
129
  sudo ./llvm.sh 16
130
- cmake -B build_artifacts -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSEARCH_BUILD_TEST_CPP=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_BUILD_LIB_C=1 -DDUSEARCH_USE_OPENMP=0 -DUSEARCH_USE_SIMSIMD=1 -DUSEARCH_USE_JEMALLOC=1
130
+ cmake -B build_artifacts -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSEARCH_BUILD_TEST_CPP=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_USE_OPENMP=0 -DUSEARCH_USE_SIMSIMD=1 -DUSEARCH_USE_JEMALLOC=1
131
131
  cmake --build build_artifacts --config RelWithDebInfo
132
132
  - name: Test C++
133
133
  run: ./build_artifacts/test_cpp
@@ -432,6 +432,7 @@ jobs:
432
432
  npm install
433
433
  npm ci
434
434
  npm test
435
+ npm run typegen
435
436
 
436
437
  - name: Publish
437
438
  uses: JS-DevTools/npm-publish@v2
package/CITATION.cff CHANGED
@@ -5,7 +5,7 @@ authors:
5
5
  given-names: "Ash"
6
6
  orcid: "https://orcid.org/0000-0002-4882-1815"
7
7
  title: "USearch by Unum Cloud"
8
- version: 2.8.10
8
+ version: 2.8.13
9
9
  doi: 10.5281/zenodo.7949416
10
10
  date-released: 2023-10-22
11
11
  url: "https://github.com/unum-cloud/usearch"
package/CONTRIBUTING.md CHANGED
@@ -232,21 +232,30 @@ Afterwards, in a new terminal:
232
232
 
233
233
  ```sh
234
234
  gradle clean build
235
- java -cp . -Djava.library.path="$(pwd)/build/libs/usearch/shared" java/cloud/unum/usearch/Index.java
235
+ gradle test
236
+ ```
237
+
238
+ Alternatively, to run the `Index.main`:
239
+
240
+ ```sh
241
+ java -cp "$(pwd)/build/classes/java/main" -Djava.library.path="$(pwd)/build/libs/usearch/shared" java/cloud/unum/usearch/Index.java
236
242
  ```
237
243
 
238
244
  Or step by-step:
239
245
 
240
246
  ```sh
241
- cs java/unum/cloud/usearch
242
- javac -h . Index.java
247
+ cd java/cloud/unum/usearch
248
+ javac -h . Index.java NativeUtils.java
249
+
250
+ # Ensure JAVA_HOME system environment variable has been set
251
+ # e.g. export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
243
252
 
244
253
  # Ubuntu:
245
- g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
246
- g++ -shared -fPIC -o libusearch_c.so cloud_unum_usearch_Index.o -lc
254
+ g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I../../../../include cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
255
+ g++ -shared -fPIC -o libusearch.so cloud_unum_usearch_Index.o -lc
247
256
 
248
257
  # Windows
249
- g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
258
+ g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -I..\..\..\..\include -o cloud_unum_usearch_Index.o
250
259
  g++ -shared -o USearchJNI.dll cloud_unum_usearch_Index.o -Wl,--add-stdcall-alias
251
260
 
252
261
  # MacOS
@@ -256,9 +265,11 @@ g++ -std=c++11 -c -fPIC \
256
265
  -I../../../../simsimd/include \
257
266
  -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
258
267
  g++ -dynamiclib -o libusearch.dylib cloud_unum_usearch_Index.o -lc
268
+
259
269
  # Run linking to that directory
260
- java -cp . -Djava.library.path="$(pwd)/java/cloud/unum/usearch/" Index.java
261
- java -cp . -Djava.library.path="$(pwd)/java" cloud.unum.usearch.Index
270
+ cd ../../../..
271
+ cp cloud/unum/usearch/libusearch.* .
272
+ java -cp . -Djava.library.path="$(pwd)" cloud.unum.usearch.Index
262
273
  ```
263
274
 
264
275
  ## Wolfram
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "2.8.10"
3
+ version = "2.8.13"
4
4
  authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/README.md CHANGED
@@ -36,18 +36,18 @@ Linux • MacOS • Windows • iOS • WebAssembly
36
36
  </p>
37
37
 
38
38
  <div align="center">
39
- <a href="https://pypi.org/project/usearch/"> <img alt="PyPI" src="https://img.shields.io/pypi/dm/usearch?label=PyPi%20pulls"> </a>
40
- <a href="https://www.npmjs.com/package/usearch"> <img alt="NPM" src="https://img.shields.io/npm/dy/usearch?label=NPM%20pulls"> </a>
41
- <a href="https://crates.io/crates/usearch"> <img alt="Crate" src="https://img.shields.io/crates/d/usearch?label=Crate%20pulls"> </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?label=NuGet%20pulls"> </a>
39
+ <a href="https://pepy.tech/project/usearch"> <img alt="PyPI" src="https://static.pepy.tech/personalized-badge/usearch?period=month&units=abbreviation&left_color=black&right_color=blue&left_text=Python%20monthly%20installs"> </a>
40
+ <a href="https://www.npmjs.com/package/usearch"> <img alt="NPM" src="https://img.shields.io/npm/dy/usearch?label=NPM%20installs"> </a>
41
+ <a href="https://crates.io/crates/usearch"> <img alt="Crate" src="https://img.shields.io/crates/d/usearch?label=Crate%20installs"> </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?label=NuGet%20installs"> </a>
43
43
  <a href="https://central.sonatype.com/artifact/cloud.unum/usearch/overview"> <img alt="Maven" src="https://img.shields.io/nexus/r/cloud.unum/usearch?server=https%3A%2F%2Fs01.oss.sonatype.org%2F&label=Maven%20version"> </a>
44
- <a href="https://hub.docker.com/r/unum/usearch"> <img alt="Docker" src="https://img.shields.io/docker/pulls/unum/usearch?label=Docker%20pulls"> </a>
44
+ <a href="https://hub.docker.com/r/unum/usearch"> <img alt="Docker" src="https://img.shields.io/docker/pulls/unum/usearch?label=Docker%20installs"> </a>
45
45
  <img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/unum-cloud/usearch?label=Repo%20size">
46
46
  </div>
47
47
 
48
48
  ---
49
49
 
50
- - ✅ [20x faster than][faster-than-faiss] [FAISS][faiss] implementation of [HNSW algorithm][hnsw-algorithm].
50
+ - ✅ [10x faster][faster-than-faiss] [HNSW][hnsw-algorithm] implementation than [FAISS][faiss].
51
51
  - ✅ Simple and extensible [single C++11 header][usearch-header] implementation.
52
52
  - ✅ Compatible with a dozen programming languages out of the box.
53
53
  - ✅ [Trusted](#integrations) by some of the most loved Datalakes and Databases, like [ClickHouse][clickhouse-docs].
@@ -86,28 +86,32 @@ FAISS is a widely recognized standard for high-performance vector search engines
86
86
  USearch and FAISS both employ the same HNSW algorithm, but they differ significantly in their design principles.
87
87
  USearch is compact and broadly compatible without sacrificing performance, primarily focusing on user-defined metrics and fewer dependencies.
88
88
 
89
- | | FAISS | USearch | Improvement |
90
- | :------------------------------------------- | ----------------------------: | -----------------------------: | ---------------------: |
91
- | Indexing time ⁰ | | | |
92
- | 100 Million 96d `f32`, `f16`, `i8` vectors | 2.6 h, 2.6 h, 2.6 h | 0.3 h, 0.2 h, 0.2 h | __9.6x, 10.4x, 10.7x__ |
93
- | 100 Million 1536d `f32`, `f16`, `i8` vectors | 5.0 h, 4.1 h, 3.8 h | 2.1 h, 1.1 h, 0.8 h | __2.3x 3.6x, 4.4x__ |
94
- | | | | |
95
- | Codebase length ¹ | 84 K [SLOC][sloc] in `faiss/` | 3 K [SLOC][sloc] in `usearch/` | maintainable |
96
- | Supported metrics ² | 9 fixed metrics | any user-defined metrics | extendible |
97
- | Supported languages ³ | C++, Python | 10 languages | portable |
98
- | Supported ID types ⁴ | 32-bit, 64-bit | 32-bit, 40-bit, 64-bit | efficient |
99
- | Required dependencies ⁵ | BLAS, OpenMP | - | light-weight |
100
- | Bindings ⁶ | SWIG | Native | low-latency |
89
+ | | FAISS | USearch | Improvement |
90
+ | :------------------------------------------- | ----------------------: | -----------------------: | ----------------------: |
91
+ | Indexing time ⁰ | | | |
92
+ | 100 Million 96d `f32`, `f16`, `i8` vectors | 2.6 · 2.6 · 2.6 h | 0.3 · 0.2 · 0.2 h | __9.6 · 10.4 · 10.7 x__ |
93
+ | 100 Million 1536d `f32`, `f16`, `i8` vectors | 5.0 · 4.1 · 3.8 h | 2.1 · 1.1 · 0.8 h | __2.3 · 3.6 · 4.4 x__ |
94
+ | | | | |
95
+ | Codebase length ¹ | 84 K [SLOC][sloc] | 3 K [SLOC][sloc] | maintainable |
96
+ | Supported metrics ² | 9 fixed metrics | any metric | extendible |
97
+ | Supported languages ³ | C++, Python | 10 languages | portable |
98
+ | Supported ID types ⁴ | 32-bit, 64-bit | 32-bit, 40-bit, 64-bit | efficient |
99
+ | Required dependencies ⁵ | BLAS, OpenMP | - | light-weight |
100
+ | Bindings ⁶ | SWIG | Native | low-latency |
101
+ | Python binding size ⁷ | [~ 10 MB][faiss-weight] | [< 1 MB][usearch-weight] | deployable |
101
102
 
102
103
  [sloc]: https://en.wikipedia.org/wiki/Source_lines_of_code
104
+ [faiss-weight]: https://pypi.org/project/faiss-cpu/#files
105
+ [usearch-weight]: https://pypi.org/project/usearch/#files
103
106
 
104
107
  > ⁰ [Tested][intel-benchmarks] on Intel Sapphire Rapids, with the simplest inner-product distance, equivalent recall, and memory consumption while also providing far superior search speed.
105
- > ¹ A shorter codebase makes the project easier to maintain and audit.
108
+ > ¹ A shorter codebase of `usearch/` over `faiss/` makes the project easier to maintain and audit.
106
109
  > ² User-defined metrics allow you to customize your search for various applications, from GIS to creating custom metrics for composite embeddings from multiple AI models or hybrid full-text and semantic search.
107
110
  > ³ With USearch, you can reuse the same preconstructed index in various programming languages.
108
111
  > ⁴ The 40-bit integer allows you to store 4B+ vectors without allocating 8 bytes for every neighbor reference in the proximity graph.
109
112
  > ⁵ Lack of obligatory dependencies makes USearch much more portable.
110
113
  > ⁶ Native bindings introduce lower call latencies than more straightforward approaches.
114
+ > ⁷ Lighter bindings make downloads and deployments faster.
111
115
 
112
116
  [intel-benchmarks]: https://www.unum.cloud/blog/2023-11-07-scaling-vector-search-with-intel
113
117
 
@@ -141,6 +145,7 @@ index = Index(
141
145
  connectivity=16, # Optional: Limit number of neighbors per graph node
142
146
  expansion_add=128, # Optional: Control the recall of indexing
143
147
  expansion_search=64, # Optional: Control the quality of the search
148
+ multi=False, # Optional: Allow multiple vectors per key, default = False
144
149
  )
145
150
  ```
146
151
 
@@ -161,7 +166,7 @@ index.save("index.usearch")
161
166
  loaded_copy = index.load("index.usearch")
162
167
  view = Index.restore("index.usearch", view=True)
163
168
 
164
- other_view = Index(ndim=..., metric=CompiledMetric(...))
169
+ other_view = Index(ndim=..., metric=...)
165
170
  other_view.view("index.usearch")
166
171
  ```
167
172
 
@@ -208,7 +213,9 @@ multi_index.search(...)
208
213
 
209
214
  ## Clustering
210
215
 
211
- Once the index is constructed, it can cluster entries much faster than using a separate clustering algorithm implementation.
216
+ Once the index is constructed, USearch can perform K-Nearest Neighbors Clustering much faster than standalone clustering libraries, like SciPy,
217
+ UMap, and tSNE.
218
+ Same for dimensionality reduction with PCA.
212
219
  Essentially, the `Index` itself can be seen as a clustering, allowing iterative deepening.
213
220
 
214
221
  ```py
@@ -396,6 +403,8 @@ index.add(keys, fingerprints)
396
403
  matches = index.search(fingerprints, 10)
397
404
  ```
398
405
 
406
+ That method was used to build the ["USearch Molecules"](https://github.com/ashvardanian/usearch-molecules), one of the largest Chem-Informatics datasets, containing 7 billion small molecules and 28 billion fingerprints.
407
+
399
408
  [smiles]: https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system
400
409
  [rdkit-fingerprints]: https://www.rdkit.org/docs/RDKit_Book.html#additional-information-about-the-fingerprints
401
410
 
@@ -424,7 +433,7 @@ doi = {10.5281/zenodo.7949416},
424
433
  author = {Vardanian, Ash},
425
434
  title = {{USearch by Unum Cloud}},
426
435
  url = {https://github.com/unum-cloud/usearch},
427
- version = {2.8.10},
436
+ version = {2.8.13},
428
437
  year = {2023},
429
438
  month = oct,
430
439
  }
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.8.10
1
+ 2.8.13
package/build.gradle CHANGED
@@ -23,6 +23,11 @@ sourceSets {
23
23
  srcDir "java/"
24
24
  exclude "test/"
25
25
  }
26
+ // Ensure native libraries are included in the main source set
27
+ resources {
28
+ srcDirs "$buildDir/libs"
29
+ include "usearch/**"
30
+ }
26
31
  }
27
32
  test {
28
33
  java {
@@ -98,15 +103,25 @@ test {
98
103
 
99
104
  // Rearrange test classpath, add compiled JAR instead of main classes
100
105
  classpath = project.sourceSets.test.output + configurations.testRuntimeClasspath + files(jar.archiveFile)
106
+
107
+ // Enable detailed test logging
108
+ testLogging {
109
+ // Ensure that we get output from all tests, not just failing ones
110
+ showStandardStreams = true
111
+ exceptionFormat = 'full' // Can be 'short', 'full', or 'summary'
112
+
113
+ // More detailed test result logging
114
+ events "passed", "skipped", "failed"
115
+ }
101
116
  }
102
117
 
103
118
  jar {
104
119
  dependsOn("usearchSharedLibrary")
105
- into("usearch") {
106
- from("$buildDir/libs/usearch/shared/libusearch.dylib")
107
- from("$buildDir/libs/usearch/shared/libusearch.dll")
108
- from("$buildDir/libs/usearch/shared/libusearch.so")
120
+ from("$buildDir/libs/usearch/shared") {
121
+ include "libusearch.so", "libusearch.dylib", "libusearch.dll"
122
+ into "usearch"
109
123
  }
124
+ from sourceSets.main.output
110
125
  }
111
126
 
112
127
  signing {
@@ -180,4 +195,10 @@ publishing {
180
195
  }
181
196
  }
182
197
  }
183
- }
198
+ }
199
+
200
+ // Ensure that the native library is built before the Java code
201
+ compileJava.dependsOn tasks.withType(SharedLibraryBinarySpec)
202
+ tasks.named("processResources").configure {
203
+ dependsOn tasks.named("linkUsearchSharedLibrary")
204
+ }
package/conanfile.py CHANGED
@@ -7,7 +7,7 @@ required_conan_version = ">=1.53.0"
7
7
  class USearchConan(ConanFile):
8
8
 
9
9
  name = "usearch"
10
- version = '2.8.10'
10
+ version = '2.8.13'
11
11
  license = "Apache-2.0"
12
12
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
13
13
  homepage = "https://github.com/unum-cloud/usearch"
@@ -11,7 +11,7 @@
11
11
 
12
12
  #define USEARCH_VERSION_MAJOR 2
13
13
  #define USEARCH_VERSION_MINOR 8
14
- #define USEARCH_VERSION_PATCH 10
14
+ #define USEARCH_VERSION_PATCH 13
15
15
 
16
16
  // Inferring C++ version
17
17
  // https://stackoverflow.com/a/61552074
@@ -108,13 +108,10 @@ struct index_dense_config_t : public index_config_t {
108
108
 
109
109
  index_dense_config_t(index_config_t base) noexcept : index_config_t(base) {}
110
110
 
111
- index_dense_config_t( //
112
- std::size_t connectivity = default_connectivity(), //
113
- std::size_t expansion_add = default_expansion_add(),
114
- std::size_t expansion_search = default_expansion_search()) noexcept
115
- : index_config_t(connectivity), //
116
- expansion_add(expansion_add ? expansion_add : default_expansion_add()),
117
- expansion_search(expansion_search ? expansion_search : default_expansion_search()) {}
111
+ index_dense_config_t(std::size_t c = default_connectivity(), std::size_t ea = default_expansion_add(),
112
+ std::size_t es = default_expansion_search()) noexcept
113
+ : index_config_t(c), expansion_add(ea ? ea : default_expansion_add()),
114
+ expansion_search(es ? es : default_expansion_search()) {}
118
115
  };
119
116
 
120
117
  struct index_dense_clustering_config_t {
@@ -1312,11 +1309,10 @@ class index_dense_gt {
1312
1309
  shared_lock_t lock(slot_lookup_mutex_);
1313
1310
  offset = (std::min)(offset, slot_lookup_.size());
1314
1311
  slot_lookup_.for_each([&](key_and_slot_t const& key_and_slot) {
1315
- if (offset) {
1312
+ if (offset)
1313
+ // Skip the first `offset` entries
1316
1314
  --offset;
1317
- return;
1318
- }
1319
- if (limit) {
1315
+ else if (limit) {
1320
1316
  *keys = key_and_slot.key;
1321
1317
  ++keys;
1322
1318
  --limit;
@@ -1343,11 +1343,13 @@ class metric_punned_t {
1343
1343
  scalar_kind_(scalar_kind) {
1344
1344
 
1345
1345
  #if USEARCH_USE_SIMSIMD
1346
- if (configure_with_simsimd())
1347
- return;
1346
+ if (!configure_with_simsimd())
1347
+ configure_with_auto_vectorized();
1348
1348
  #endif
1349
-
1350
1349
  configure_with_auto_vectorized();
1350
+
1351
+ if (scalar_kind == scalar_kind_t::b1x8_k)
1352
+ raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_);
1351
1353
  }
1352
1354
 
1353
1355
  inline metric_punned_t( //
@@ -1499,18 +1501,9 @@ class metric_punned_t {
1499
1501
  break;
1500
1502
  }
1501
1503
  case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
1502
- case metric_kind_t::tanimoto_k:
1503
- raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>,
1504
- raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1505
- break;
1506
- case metric_kind_t::hamming_k:
1507
- raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>,
1508
- raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1509
- break;
1510
- case metric_kind_t::sorensen_k:
1511
- raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>,
1512
- raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
1513
- break;
1504
+ case metric_kind_t::tanimoto_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>; break;
1505
+ case metric_kind_t::hamming_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>; break;
1506
+ case metric_kind_t::sorensen_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>; break;
1514
1507
  default: return;
1515
1508
  }
1516
1509
  }
@@ -0,0 +1,237 @@
1
+ export class Index {
2
+ /**
3
+ * Constructs a new index.
4
+ *
5
+ * @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
6
+ * @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
7
+ * @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
8
+ * @param {number} [connectivity=0] - Optional, default is 0.
9
+ * @param {number} [expansion_add=0] - Optional, default is 0.
10
+ * @param {number} [expansion_search=0] - Optional, default is 0.
11
+ * @param {boolean} [multi=false] - Optional, default is false.
12
+ * @throws Will throw an error if any of the parameters are of incorrect type or invalid value.
13
+ */
14
+ constructor(dimensionsOrConfigs: number | {
15
+ dimensions: number;
16
+ metric: MetricKind;
17
+ quantization: ScalarKind;
18
+ connectivity: number;
19
+ expansion_add: number;
20
+ expansion_search: number;
21
+ multi: boolean;
22
+ }, metric?: MetricKind, quantization?: ScalarKind, connectivity?: number, expansion_add?: number, expansion_search?: number, multi?: boolean);
23
+ _compiledIndex: any;
24
+ /**
25
+ * Add vectors to the index.
26
+ *
27
+ * This method accepts vectors and their corresponding keys for indexing.
28
+ * Each key should correspond to a vector. If a single key is provided,
29
+ * it is broadcasted to match the number of provided vectors.
30
+ *
31
+ * Vectors should be provided as a flat typed array representing a matrix
32
+ * where each row is a vector to be indexed. The matrix should have a size
33
+ * of n * d, where n is the number of vectors, and d is the dimensionality
34
+ * of the vectors.
35
+ *
36
+ * Keys should be provided as a BigInt or an array-like object of BigInts
37
+ * representing the unique identifier for each vector.
38
+ *
39
+ * @param {bigint|bigint[]|BigUint64Array} keys - Input identifiers for every vector.
40
+ * If a single key is provided, it is associated with all provided vectors.
41
+ * @param {Float32Array|Float64Array|Int8Array} vectors - Input matrix representing vectors,
42
+ * matrix of size n * d, where n is the number of vectors, and d is their dimensionality.
43
+ * @throws Will throw an error if the length of keys doesn't match the number of vectors
44
+ * or if it's not a single key.
45
+ */
46
+ add(keys: bigint | bigint[] | BigUint64Array, vectors: Float32Array | Float64Array | Int8Array): void;
47
+ /**
48
+ * Perform a k-nearest neighbor search on the index.
49
+ *
50
+ * This method accepts a matrix of query vectors and returns the closest vectors
51
+ * from the index for each query. The method returns an object containing the keys,
52
+ * distances, and counts of the matches found.
53
+ *
54
+ * Vectors should be provided as a flat typed array representing a matrix where
55
+ * each row is a vector. The matrix should be of size n * d, where n is the
56
+ * number of query vectors, and d is their dimensionality.
57
+ *
58
+ * The parameter `k` specifies the number of nearest neighbors to return for each
59
+ * query vector. If there are not enough results for a query, the result array is
60
+ * padded with -1s.
61
+ *
62
+ * @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of arrays.
63
+ * @param {number} k - The number of nearest neighbors to search for each query vector.
64
+ * @return {Matches|BatchMatches} - Search results for one or more queries, containing keys, distances, and counts of the matches found.
65
+ * @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.
66
+ * @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of arrays) or if its flattened size is not a multiple of dimensions.
67
+ */
68
+ search(vectors: Float32Array | Float64Array | Int8Array | Array<Array<number>>, k: number): Matches | BatchMatches;
69
+ /**
70
+ * Verifies the presence of one or more keys in the index.
71
+ *
72
+ * This method accepts one or multiple keys as input and returns a boolean or
73
+ * an array of booleans indicating whether each key is present in the index.
74
+ *
75
+ * @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be checked for presence in the index.
76
+ * @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.
77
+ * @throws Will throw an error if keys are not integers.
78
+ */
79
+ contains(keys: bigint | bigint[] | BigUint64Array): boolean | boolean[];
80
+ /**
81
+ * Counts the number of times keys shows up in the index.
82
+ *
83
+ * @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be enumerated.
84
+ * @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.
85
+ * @throws Will throw an error if keys are not integers.
86
+ */
87
+ count(keys: bigint | bigint[] | BigUint64Array): number | number[];
88
+ /**
89
+ * Removes one or multiple vectors from the index.
90
+ *
91
+ * This method accepts one or multiple keys as input and removes the corresponding vectors from the index.
92
+ * It returns the number of vectors actually removed for each key provided.
93
+ *
94
+ * @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be removed.
95
+ * @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.
96
+ * @throws Will throw an error if keys are not integers.
97
+ */
98
+ remove(keys: bigint | bigint[] | BigUint64Array): number | number[];
99
+ /**
100
+ * Returns the dimensionality of vectors.
101
+ * @return {number} The dimensionality of vectors.
102
+ */
103
+ dimensions(): number;
104
+ /**
105
+ * Returns connectivity.
106
+ * @return {number} The connectivity of index.
107
+ */
108
+ connectivity(): number;
109
+ /**
110
+ * Returns the number of vectors currently indexed.
111
+ * @return {number} The number of vectors currently indexed.
112
+ */
113
+ size(): number;
114
+ /**
115
+ * Returns index capacity.
116
+ * @return {number} The capacity of index.
117
+ */
118
+ capacity(): number;
119
+ /**
120
+ * Write index to a file.
121
+ * @param {string} path File path to write.
122
+ * @throws Will throw an error if `path` is not a string.
123
+ */
124
+ save(path: string): void;
125
+ /**
126
+ * Load index from a file.
127
+ * @param {string} path File path to read.
128
+ * @throws Will throw an error if `path` is not a string.
129
+ */
130
+ load(path: string): void;
131
+ /**
132
+ * View index from a file, without loading into RAM.
133
+ * @param {string} path File path to read.
134
+ * @throws Will throw an error if `path` is not a string.
135
+ */
136
+ view(path: string): void;
137
+ }
138
+ /**
139
+ * *
140
+ */
141
+ export type MetricKind = string;
142
+ export namespace MetricKind {
143
+ let Unknown: string;
144
+ let Cos: string;
145
+ let IP: string;
146
+ let L2sq: string;
147
+ let Haversine: string;
148
+ let Divergence: string;
149
+ let Pearson: string;
150
+ let Jaccard: string;
151
+ let Hamming: string;
152
+ let Tanimoto: string;
153
+ let Sorensen: string;
154
+ }
155
+ /**
156
+ * *
157
+ */
158
+ export type ScalarKind = string;
159
+ export namespace ScalarKind {
160
+ let Unknown_1: string;
161
+ export { Unknown_1 as Unknown };
162
+ export let F32: string;
163
+ export let F64: string;
164
+ export let F16: string;
165
+ export let I8: string;
166
+ export let B1: string;
167
+ }
168
+ /**
169
+ * Represents a set of search results.
170
+ */
171
+ export class Matches {
172
+ /**
173
+ * Constructs a Matches object.
174
+ *
175
+ * @param {BigUint64Array} keys - The keys of the nearest neighbors found.
176
+ * @param {Float32Array} distances - The distances of the nearest neighbors found.
177
+ */
178
+ constructor(keys: BigUint64Array, distances: Float32Array);
179
+ keys: BigUint64Array;
180
+ distances: Float32Array;
181
+ }
182
+ /**
183
+ * Represents a set of batched search results.
184
+ */
185
+ export class BatchMatches {
186
+ /**
187
+ * Constructs a BatchMatches object.
188
+ *
189
+ * @param {BigUint64Array} keys - The keys of the nearest neighbors found in the batch.
190
+ * @param {Float32Array} distances - The distances of the nearest neighbors found in the batch.
191
+ * @param {BigUint64Array} counts - The number of neighbors found for each query in the batch.
192
+ * @param {bigint} k - The limit for search results per query in the batch.
193
+ */
194
+ constructor(keys: BigUint64Array, distances: Float32Array, counts: BigUint64Array, k: bigint);
195
+ keys: BigUint64Array;
196
+ distances: Float32Array;
197
+ counts: BigUint64Array;
198
+ k: bigint;
199
+ /**
200
+ * Retrieves a Matches object at the specified index in the batch.
201
+ *
202
+ * @param {number} i - The index at which to retrieve the Matches object.
203
+ * @returns {Matches} - A Matches object representing the search results at the specified index in the batch.
204
+ */
205
+ get(i: number): Matches;
206
+ }
207
+ /**
208
+ * Performs an exact search on the given dataset to find the best matching vectors for each query.
209
+ *
210
+ * @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.
211
+ * @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.
212
+ * @param {number} dimensions - The dimensionality of the vectors in both the dataset and the queries. It defines the number of elements in each vector.
213
+ * @param {number} count - The number of nearest neighbors to return for each query. If the dataset contains fewer vectors than the specified count, the result will contain only the available vectors.
214
+ * @param {MetricKind} metric - The distance metric to be used for the search.
215
+ * @return {Matches|BatchMatches} - Returns a `Matches` or `BatchMatches` object containing the results of the search.
216
+ * @throws Will throw an error if `dimensions` and `count` are not positive integers.
217
+ * @throws Will throw an error if `metric` is not a valid MetricKind.
218
+ * @throws Will throw an error if `dataset` and `queries` are not valid input types (TypedArray or an array of arrays).
219
+ * @throws Will throw an error if the sizes of the flattened `dataset` and `queries` are not multiples of `dimensions`.
220
+ * @throws Will throw an error if `count` is greater than the number of vectors in the `dataset`.
221
+ *
222
+ * @example
223
+ * const dataset = [[1.0, 2.0], [3.0, 4.0]]; // Two vectors: [1.0, 2.0] and [3.0, 4.0]
224
+ * const queries = [[1.5, 2.5]]; // One vector: [1.5, 2.5]
225
+ * const dimensions = 2; // The number of elements in each vector.
226
+ * const count = 1; // The number of nearest neighbors to return for each query.
227
+ * const metric = MetricKind.IP; // Using the Inner Product distance metric.
228
+ *
229
+ * const result = exactSearch(dataset, queries, dimensions, count, metric);
230
+ * // result might be:
231
+ * // {
232
+ * // keys: BigUint64Array [ 1n ],
233
+ * // distances: Float32Array [ some_value ],
234
+ * // }
235
+ */
236
+ export function exactSearch(dataset: Float32Array | Float64Array | Int8Array | Array<Array<number>>, queries: Float32Array | Float64Array | Int8Array | Array<Array<number>>, dimensions: number, count: number, metric: MetricKind): Matches | BatchMatches;
237
+ //# sourceMappingURL=usearch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usearch.d.ts","sourceRoot":"","sources":["../usearch.js"],"names":[],"mappings":"AAsIA;IAEI;;;;;;;;;;;OAWG;IACH;oBATkC,MAAM;gBAAU,UAAU;sBAAiC,UAAU;sBAAiC,MAAM;uBAAqB,MAAM;0BAAwB,MAAM;eAAa,OAAO;gBAChN,UAAU,iBACV,UAAU,iBACV,MAAM,kBACN,MAAM,qBACN,MAAM,UACN,OAAO,EAgCjB;IADG,oBAAwI;IAG5I;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAPW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,WAE9B,YAAY,GAAC,YAAY,GAAC,SAAS,QAoB7C;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBANW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,KACxD,MAAM,GACL,OAAO,GAAC,YAAY,CAqB/B;IAED;;;;;;;;;OASG;IACH,eAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,OAAO,GAAC,OAAO,EAAE,CAU5B;IAED;;;;;;OAMG;IACH,YAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;;;;;;;OASG;IACH,aAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;OAGG;IACH,cAFY,MAAM,CAEsC;IAExD;;;OAGG;IACH,gBAFY,MAAM,CAE0C;IAE5D;;;OAGG;IACH,QAFY,MAAM,CAE0B;IAE5C;;;OAGG;IACH,YAFY,MAAM,CAEkC;IAEpD;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;CACJ;;;;yBAzWS,MAAM;;;;;;;;;;;;;;;;;yBAmBN,MAAM;;;;;;;;;;AAYhB;;GAEG;AACH;IACI;;;;;OAKG;IACH,kBAHW,cAAc,aACd,YAAY,EAKtB;IAFG,qBAAgB;IAChB,wBAA0B;CAEjC;AAED;;GAEG;AACH;IACI;;;;;;;OAOG;IACH,kBALW,cAAc,aACd,YAAY,UACZ,cAAc,KACd,MAAM,EAOhB;IAJG,qBAAgB;IAChB,wBAA0B;IAC1B,uBAAoB;IACpB,UAAU;IAGd;;;;;OAKG;IACH,OAHW,MAAM,GACJ,OAAO,CAQnB;CACJ;AA4RD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qCA1BW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,WACxD,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,cACxD,MAAM,SACN,MAAM,UACN,UAAU,GACT,OAAO,GAAC,YAAY,CA0D/B"}
@@ -137,7 +137,7 @@ class Index {
137
137
  /**
138
138
  * Constructs a new index.
139
139
  *
140
- * @param {number} dimensionsOrConfigs
140
+ * @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
141
  * @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
142
142
  * @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
143
143
  * @param {number} [connectivity=0] - Optional, default is 0.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.8.10",
3
+ "version": "2.8.13",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ash Vardanian (https://ashvardanian.com/)",
6
6
  "license": "Apache 2.0",
@@ -25,9 +25,24 @@
25
25
  },
26
26
  "scripts": {
27
27
  "test": "node --test ./javascript/usearch.test.js",
28
- "install": "node-gyp rebuild"
28
+ "install": "node-gyp rebuild",
29
+ "typegen": "npx tsc"
29
30
  },
30
31
  "devDependencies": {
31
32
  "typescript": "^5.1.6"
32
- }
33
+ },
34
+ "exports": {
35
+ ".": {
36
+ "types": "./javascript/types/usearch.d.ts",
37
+ "require": "./javascript/usearch.js"
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "typesVersions": {
42
+ "*": {
43
+ "*": [
44
+ "./javascript/types/usearch.d.ts"
45
+ ]
46
+ }
47
+ }
33
48
  }
package/setup.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import sys
3
- import platform
3
+ import subprocess
4
4
  from setuptools import setup
5
5
 
6
6
  from pybind11.setup_helpers import Pybind11Extension
@@ -19,11 +19,24 @@ def get_bool_env_w_name(name: str, preference: bool) -> tuple:
19
19
 
20
20
 
21
21
  # Check the environment variables
22
- is_gcc = "GCC" in platform.python_compiler().upper()
23
22
  is_linux: bool = sys.platform == "linux"
24
23
  is_macos: bool = sys.platform == "darwin"
25
24
  is_windows: bool = sys.platform == "win32"
26
25
 
26
+
27
+ is_gcc = False
28
+ if is_linux:
29
+ cxx = os.environ.get("CXX")
30
+ if cxx:
31
+ try:
32
+ command = "where" if os.name == "nt" else "which"
33
+ full_path = subprocess.check_output([command, cxx], text=True).strip()
34
+ compiler_name = os.path.basename(full_path)
35
+ is_gcc = ("g++" in compiler_name) and ("clang++" not in compiler_name)
36
+ except subprocess.CalledProcessError:
37
+ pass
38
+
39
+
27
40
  prefer_simsimd: bool = is_linux or is_macos
28
41
  prefer_fp16lib: bool = True
29
42
  prefer_openmp: bool = is_linux and is_gcc
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ // Change this to match your project
3
+ "include": ["javascript/usearch.js"],
4
+ "compilerOptions": {
5
+ // Tells TypeScript to read JS files, as
6
+ // normally they are ignored as source files
7
+ "allowJs": true,
8
+ // Generate d.ts files
9
+ "declaration": true,
10
+ // This compiler run should
11
+ // only output d.ts files
12
+ "emitDeclarationOnly": true,
13
+ // Types should go into this directory.
14
+ // Removing this would place the .d.ts files
15
+ // next to the .js files
16
+ "outDir": "javascript/types",
17
+ // go to js file when using IDE functions like
18
+ // "Go to Definition" in VSCode
19
+ "declarationMap": true
20
+ }
21
+ }
package/wasmer.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name="unum/usearch"
3
- version="2.8.10"
3
+ version="2.8.13"
4
4
  description="Smaller & Faster Single-File Vector Search Engine from Unum"
5
5
  license="Apache-2.0"
6
6
  readme="README.md"