usearch 2.26.3 → 2.26.4
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 +26 -7
- package/c/README.md +1 -1
- package/cpp/test.cpp +46 -0
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_dense.hpp +3 -0
- package/include/usearch/index_plugins.hpp +55 -19
- package/javascript/README.md +2 -1
- package/package.json +1 -1
- 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-x64/usearch.node +0 -0
package/README.md
CHANGED
|
@@ -525,20 +525,39 @@ index = Index(ndim=ndim, metric=CompiledMetric(
|
|
|
525
525
|
|
|
526
526
|
## Integrations & Users
|
|
527
527
|
|
|
528
|
+
### Databases & Query Engines
|
|
529
|
+
|
|
528
530
|
- [x] ClickHouse: [C++](https://github.com/ClickHouse/ClickHouse/pull/53447), [docs](https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/annindexes#usearch).
|
|
529
531
|
- [x] DuckDB: [post](https://duckdb.org/2024/05/03/vector-similarity-search-vss.html).
|
|
530
532
|
- [x] ScyllaDB: [Rust](https://github.com/scylladb/vector-store), [presentation](https://www.slideshare.net/slideshow/vector-search-with-scylladb-by-szymon-wasik/276571548).
|
|
531
533
|
- [x] TiDB & TiFlash: [C++](https://github.com/pingcap/tiflash), [announcement](https://www.pingcap.com/article/introduce-vector-search-indexes-in-tidb/).
|
|
532
|
-
- [x] YugaByte: [C++](https://github.com/yugabyte/yugabyte-db/blob/366b9f5e3c4df3a1a17d553db41d6dc50146f488/src/yb/vector_index/usearch_wrapper.cc).
|
|
534
|
+
- [x] YugaByte: [C++](https://github.com/yugabyte/yugabyte-db/blob/366b9f5e3c4df3a1a17d553db41d6dc50146f488/src/yb/vector_index/usearch_wrapper.cc), [architecture](https://www.yugabyte.com/blog/yugabytedb-vector-indexing-architecture/), [1B-vector benchmark](https://www.yugabyte.com/blog/benchmarking-1-billion-vectors-in-yugabytedb/).
|
|
533
535
|
- [x] MemGraph: [C++](https://github.com/memgraph/memgraph/blob/784dd8520f65050d033aea8b29446e84e487d091/src/storage/v2/indices/vector_index.cpp), [announcement](https://memgraph.com/blog/simplify-data-retrieval-memgraph-vector-search).
|
|
534
|
-
- [x]
|
|
535
|
-
- [x]
|
|
536
|
-
- [x]
|
|
536
|
+
- [x] MatrixOne: [GoLang](https://github.com/matrixorigin/matrixone).
|
|
537
|
+
- [x] GreptimeDB: [Rust](https://github.com/GreptimeTeam/greptimedb/blob/main/src/index/src/vector.rs).
|
|
538
|
+
- [x] KurrentDB: [C#](https://github.com/kurrent-io/KurrentDB/blob/master/src/KurrentDB.Kontext/Indexing/USearch/USearchVectorStore.cs), [credits](https://github.com/kurrent-io/KurrentDB/blob/master/NOTICE.md).
|
|
539
|
+
- [x] Chroma: [Rust](https://github.com/chroma-core/chroma/blob/main/rust/index/src/usearch.rs), behind their RaBitQ-quantized SPANN index.
|
|
540
|
+
|
|
541
|
+
### AI Frameworks & Memory
|
|
542
|
+
|
|
543
|
+
- [x] Google: [Perch](https://github.com/google-research/perch-hoplite) bioacoustics DB, [UniSim](https://github.com/google/unisim), [RetSim](https://arxiv.org/abs/2311.17264) paper.
|
|
544
|
+
- [x] Microsoft: [Semantic Kernel](https://github.com/microsoft/semantic-kernel/releases/tag/python-0.3.9.dev) and [vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/usearch).
|
|
545
|
+
- [x] LangChain: [Python](https://github.com/langchain-ai/langchain/releases/tag/v0.0.257) and [JavaScript](https://github.com/langchain-ai/langchainjs/releases/tag/0.0.125).
|
|
546
|
+
- [x] Hugging Face: Sentence-Transformers [docs](https://www.sbert.net/docs/package_reference/quantization.html#sentence_transformers.quantization.semantic_search_usearch), [quantization benchmarks](https://huggingface.co/blog/embedding-quantization).
|
|
537
547
|
- [x] GPTCache: [Python](https://github.com/zilliztech/GPTCache/releases/tag/0.1.29).
|
|
538
|
-
- [x]
|
|
548
|
+
- [x] MemMachine: [Python](https://github.com/MemMachine/MemMachine/blob/main/packages/server/src/memmachine_server/common/vector_store/vector_search_engine/usearch_engine.py).
|
|
539
549
|
- [x] Pathway: [Rust](https://github.com/pathwaycom/pathway).
|
|
540
550
|
- [x] Vald: [GoLang](https://github.com/vdaas/vald).
|
|
541
|
-
|
|
551
|
+
|
|
552
|
+
### Applications & Products
|
|
553
|
+
|
|
554
|
+
- [x] Arm MCP: [Python](https://github.com/arm/mcp/blob/main/arm_kb_search/loaders.py).
|
|
555
|
+
- [x] GPT4All: [C++](https://github.com/nomic-ai/gpt4all/blob/main/gpt4all-chat/src/database.cpp).
|
|
556
|
+
- [x] Ente: [Rust](https://github.com/ente-io/ente/blob/main/rust/crates/photos/src/ml/vector_db.rs), [post](https://ente.com/blog/vector-db/).
|
|
557
|
+
- [x] Tracy profiler: [C++](https://github.com/wolfpld/tracy/blob/master/profiler/src/profiler/TracyLlmEmbeddings.cpp), behind the embeddings index of its LLM assistant.
|
|
558
|
+
- [x] Copilot for Xcode by Intitni: [Swift](https://github.com/intitni/CopilotForXcode/blob/main/Tool/Sources/USearchIndex/UsearchIndex.swift).
|
|
559
|
+
- [x] LLM for Unity: [C#](https://github.com/undreamai/LLMUnity/tree/main/Runtime/RAG/usearch), [docs](https://undream.ai/LLMUnity/).
|
|
560
|
+
- [x] Music Assistant: [Python](https://github.com/music-assistant/server/blob/dev/music_assistant/providers/sonic_similarity/vectors.py), [docs](https://www.music-assistant.io/plugins/sonic-similarity/).
|
|
542
561
|
|
|
543
562
|
|
|
544
563
|
## Citations
|
|
@@ -549,7 +568,7 @@ doi = {10.5281/zenodo.7949415},
|
|
|
549
568
|
author = {Vardanian, Ash},
|
|
550
569
|
title = {{USearch by Unum Cloud}},
|
|
551
570
|
url = {https://github.com/unum-cloud/USearch},
|
|
552
|
-
version = {2.26.
|
|
571
|
+
version = {2.26.4},
|
|
553
572
|
year = {2026},
|
|
554
573
|
}
|
|
555
574
|
```
|
package/c/README.md
CHANGED
|
@@ -19,7 +19,7 @@ int main() {
|
|
|
19
19
|
usearch_error_t error = NULL;
|
|
20
20
|
usearch_init_options_t opts = {
|
|
21
21
|
.metric_kind = usearch_metric_cos_k,
|
|
22
|
-
.
|
|
22
|
+
.quantization = usearch_scalar_f16_k, // or f32_k, bf16_k, e5m2_k, e4m3_k, e3m2_k, e2m3_k, i8_k, u8_k, b1_k
|
|
23
23
|
.dimensions = dimensions,
|
|
24
24
|
.expansion_add = 0, // for defaults
|
|
25
25
|
.expansion_search = 0 // for defaults
|
package/cpp/test.cpp
CHANGED
|
@@ -1319,6 +1319,51 @@ void test_isolate() {
|
|
|
1319
1319
|
}
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
1322
|
+
/**
|
|
1323
|
+
* @brief Regression test: `compact()` permutes the slots of all members, so the
|
|
1324
|
+
* key-to-slot lookup and the free-slot list must follow the permutation.
|
|
1325
|
+
* Otherwise `get` returns other members' vectors and later `add` calls
|
|
1326
|
+
* overwrite live members.
|
|
1327
|
+
*/
|
|
1328
|
+
void test_compact() {
|
|
1329
|
+
constexpr std::size_t dataset_count = 256;
|
|
1330
|
+
constexpr std::size_t added_count = 64;
|
|
1331
|
+
constexpr std::size_t dimensions = 16;
|
|
1332
|
+
metric_punned_t metric(dimensions, metric_kind_t::l2sq_k, scalar_kind_t::f32_k);
|
|
1333
|
+
|
|
1334
|
+
std::mt19937 generator(42);
|
|
1335
|
+
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
|
1336
|
+
std::vector<std::vector<float>> vectors(dataset_count + added_count, std::vector<float>(dimensions));
|
|
1337
|
+
for (auto& vector : vectors)
|
|
1338
|
+
std::generate(vector.begin(), vector.end(), [&] { return distribution(generator); });
|
|
1339
|
+
|
|
1340
|
+
index_dense_t index = index_dense_t::make(metric);
|
|
1341
|
+
index.reserve(dataset_count + added_count);
|
|
1342
|
+
for (std::size_t idx = 0; idx < dataset_count; ++idx)
|
|
1343
|
+
expect(index.add(idx, vectors[idx].data()));
|
|
1344
|
+
for (std::size_t idx = 0; idx < dataset_count; idx += 3)
|
|
1345
|
+
expect(index.remove(idx));
|
|
1346
|
+
std::size_t const removed_count = (dataset_count + 2) / 3;
|
|
1347
|
+
|
|
1348
|
+
expect(index.compact());
|
|
1349
|
+
expect_eq(index.size(), dataset_count - removed_count);
|
|
1350
|
+
|
|
1351
|
+
// Freed slots must be reused without clobbering live members
|
|
1352
|
+
for (std::size_t idx = dataset_count; idx < dataset_count + added_count; ++idx)
|
|
1353
|
+
expect(index.add(idx, vectors[idx].data()));
|
|
1354
|
+
expect_eq(index.size(), dataset_count - removed_count + added_count);
|
|
1355
|
+
|
|
1356
|
+
std::vector<float> reconstructed(dimensions);
|
|
1357
|
+
for (std::size_t idx = 0; idx < dataset_count + added_count; ++idx) {
|
|
1358
|
+
bool const removed = idx < dataset_count && idx % 3 == 0;
|
|
1359
|
+
expect_eq(index.contains(idx), !removed);
|
|
1360
|
+
if (removed)
|
|
1361
|
+
continue;
|
|
1362
|
+
expect_eq(index.get(idx, reconstructed.data()), std::size_t(1));
|
|
1363
|
+
expect(std::equal(reconstructed.begin(), reconstructed.end(), vectors[idx].begin()));
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1322
1367
|
static void usearch_write_backtrace(int signal_number) {
|
|
1323
1368
|
std::fprintf(stderr, "\n[usearch] Fatal signal %d. Back-trace:\n", signal_number);
|
|
1324
1369
|
#if USEARCH_HAS_STD_STACKTRACE
|
|
@@ -1584,6 +1629,7 @@ int main(int, char**) {
|
|
|
1584
1629
|
|
|
1585
1630
|
test_filtered_search();
|
|
1586
1631
|
test_isolate();
|
|
1632
|
+
test_compact();
|
|
1587
1633
|
test_load_after_metric_make();
|
|
1588
1634
|
test_view_of_truncated_file();
|
|
1589
1635
|
return 0;
|
|
@@ -1923,6 +1923,9 @@ class index_dense_gt {
|
|
|
1923
1923
|
std::forward<executor_at>(executor), std::forward<progress_at>(progress));
|
|
1924
1924
|
vectors_lookup_ = std::move(new_vectors_lookup);
|
|
1925
1925
|
vectors_tape_allocator_ = std::move(new_vectors_allocator);
|
|
1926
|
+
|
|
1927
|
+
// Slots were permuted, so the key-to-slot lookup and the free-slot list are stale
|
|
1928
|
+
reindex_keys_();
|
|
1926
1929
|
return result;
|
|
1927
1930
|
}
|
|
1928
1931
|
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
#include <float.h> // `_Float16`
|
|
4
4
|
#include <stdlib.h> // `aligned_alloc`
|
|
5
5
|
|
|
6
|
-
#include <atomic>
|
|
7
|
-
#include <chrono>
|
|
8
|
-
#include <cstring>
|
|
9
|
-
#include <thread>
|
|
6
|
+
#include <atomic> // `std::atomic`
|
|
7
|
+
#include <chrono> // `std::chrono`
|
|
8
|
+
#include <cstring> // `std::strncmp`
|
|
9
|
+
#include <thread> // `std::thread`
|
|
10
|
+
#include <type_traits> // `std::is_floating_point`
|
|
10
11
|
|
|
11
12
|
#include <usearch/index.hpp> // `expected_gt` and macros
|
|
12
13
|
|
|
@@ -374,10 +375,10 @@ inline expected_gt<scalar_kind_t> scalar_kind_from_name(char const* name, std::s
|
|
|
374
375
|
parsed.result = scalar_kind_t::i8_k;
|
|
375
376
|
else if (str_equals(name, len, "u8"))
|
|
376
377
|
parsed.result = scalar_kind_t::u8_k;
|
|
377
|
-
else if (str_equals(name, len, "b1"))
|
|
378
|
+
else if (str_equals(name, len, "b1") || str_equals(name, len, "b1x8"))
|
|
378
379
|
parsed.result = scalar_kind_t::b1x8_k;
|
|
379
380
|
else
|
|
380
|
-
parsed.failed("Unknown type, choose: f64, f32, bf16, f16, e5m2, e4m3, e3m2, e2m3, i8, u8, b1");
|
|
381
|
+
parsed.failed("Unknown type, choose: f64, f32, bf16, f16, e5m2, e4m3, e3m2, e2m3, i8, u8, b1, b1x8");
|
|
381
382
|
return parsed;
|
|
382
383
|
}
|
|
383
384
|
|
|
@@ -2280,8 +2281,23 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct met
|
|
|
2280
2281
|
|
|
2281
2282
|
inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t dim) const noexcept {
|
|
2282
2283
|
result_t ab{}, a2{}, b2{};
|
|
2284
|
+
// OpenMP only accepts `x += expr` as a reduction update, not a comma-expression.
|
|
2285
|
+
// With a function-call cast, GCC leaves `a2` and `b2` at zero and every distance becomes 0.
|
|
2286
|
+
add_products_(a, b, dim, ab, a2, b2, std::is_floating_point<scalar_t>());
|
|
2287
|
+
|
|
2288
|
+
result_t result_if_zero[2][2];
|
|
2289
|
+
result_if_zero[0][0] = 1 - ab / (std::sqrt(a2) * std::sqrt(b2));
|
|
2290
|
+
result_if_zero[0][1] = result_if_zero[1][0] = 1;
|
|
2291
|
+
result_if_zero[1][1] = 0;
|
|
2292
|
+
return result_if_zero[a2 == 0][b2 == 0];
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
private:
|
|
2296
|
+
inline void add_products_(scalar_t const* a, scalar_t const* b, std::size_t dim, result_t& ab, result_t& a2,
|
|
2297
|
+
result_t& b2, std::true_type) const noexcept {
|
|
2298
|
+
result_t product{}, first_squared{}, second_squared{};
|
|
2283
2299
|
#if USEARCH_USE_OPENMP
|
|
2284
|
-
#pragma omp simd reduction(+ :
|
|
2300
|
+
#pragma omp simd reduction(+ : product, first_squared, second_squared)
|
|
2285
2301
|
#elif defined(USEARCH_DEFINED_CLANG)
|
|
2286
2302
|
#pragma clang loop vectorize(enable)
|
|
2287
2303
|
#elif defined(USEARCH_DEFINED_GCC)
|
|
@@ -2290,14 +2306,24 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct met
|
|
|
2290
2306
|
for (std::size_t i = 0; i != dim; ++i) {
|
|
2291
2307
|
result_t ai = static_cast<result_t>(a[i]);
|
|
2292
2308
|
result_t bi = static_cast<result_t>(b[i]);
|
|
2293
|
-
|
|
2309
|
+
product += ai * bi;
|
|
2310
|
+
first_squared += square(ai);
|
|
2311
|
+
second_squared += square(bi);
|
|
2294
2312
|
}
|
|
2313
|
+
ab = product;
|
|
2314
|
+
a2 = first_squared;
|
|
2315
|
+
b2 = second_squared;
|
|
2316
|
+
}
|
|
2295
2317
|
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2318
|
+
inline void add_products_(scalar_t const* a, scalar_t const* b, std::size_t dim, result_t& ab, result_t& a2,
|
|
2319
|
+
result_t& b2, std::false_type) const noexcept {
|
|
2320
|
+
for (std::size_t i = 0; i != dim; ++i) {
|
|
2321
|
+
result_t ai = static_cast<result_t>(a[i]);
|
|
2322
|
+
result_t bi = static_cast<result_t>(b[i]);
|
|
2323
|
+
ab += ai * bi;
|
|
2324
|
+
a2 += square(ai);
|
|
2325
|
+
b2 += square(bi);
|
|
2326
|
+
}
|
|
2301
2327
|
}
|
|
2302
2328
|
};
|
|
2303
2329
|
|
|
@@ -2544,9 +2570,14 @@ struct metric_cos_i8_t {
|
|
|
2544
2570
|
a2 += square(ai);
|
|
2545
2571
|
b2 += square(bi);
|
|
2546
2572
|
}
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2573
|
+
// Same tail as `nk_angular_i8_serial`: a zero dot product is orthogonal, not identical.
|
|
2574
|
+
if (a2 == 0 && b2 == 0)
|
|
2575
|
+
return 0;
|
|
2576
|
+
if (ab == 0)
|
|
2577
|
+
return 1;
|
|
2578
|
+
result_t norms = std::sqrt(static_cast<result_t>(a2)) * std::sqrt(static_cast<result_t>(b2));
|
|
2579
|
+
result_t unclipped = 1 - static_cast<result_t>(ab) / norms;
|
|
2580
|
+
return unclipped < 0 ? 0 : unclipped;
|
|
2550
2581
|
}
|
|
2551
2582
|
};
|
|
2552
2583
|
|
|
@@ -2596,9 +2627,14 @@ struct metric_cos_u8_t {
|
|
|
2596
2627
|
a2 += square(ai);
|
|
2597
2628
|
b2 += square(bi);
|
|
2598
2629
|
}
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2630
|
+
// Same tail as `nk_angular_u8_serial`: a zero dot product is orthogonal, not identical.
|
|
2631
|
+
if (a2 == 0 && b2 == 0)
|
|
2632
|
+
return 0;
|
|
2633
|
+
if (ab == 0)
|
|
2634
|
+
return 1;
|
|
2635
|
+
result_t norms = std::sqrt(static_cast<result_t>(a2)) * std::sqrt(static_cast<result_t>(b2));
|
|
2636
|
+
result_t unclipped = 1 - static_cast<result_t>(ab) / norms;
|
|
2637
|
+
return unclipped < 0 ? 0 : unclipped;
|
|
2602
2638
|
}
|
|
2603
2639
|
};
|
|
2604
2640
|
|
package/javascript/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# USearch for JavaScript
|
|
2
2
|
|
|
3
|
-
USearch is a high-performance library for building and querying vector search indexes
|
|
3
|
+
USearch is a high-performance library for building and querying vector search indexes.
|
|
4
|
+
This page is the Node.js addon. The browser build is the C library compiled with Emscripten, documented in `wasm/README.md`.
|
|
4
5
|
|
|
5
6
|
## Installation
|
|
6
7
|
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|