usearch 2.11.1 → 2.13.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 +100 -30
- package/binding.gyp +11 -12
- package/c/README.md +3 -0
- package/c/lib.cpp +50 -13
- package/c/test.c +60 -55
- package/c/usearch.h +16 -0
- package/cpp/CMakeLists.txt +6 -0
- package/cpp/README.md +34 -13
- package/cpp/bench.cpp +99 -47
- package/cpp/test.cpp +947 -132
- package/include/usearch/index.hpp +894 -272
- package/include/usearch/index_dense.hpp +418 -156
- package/include/usearch/index_plugins.hpp +99 -49
- package/javascript/dist/cjs/usearch.d.ts +39 -1
- package/javascript/dist/cjs/usearch.js +11 -1
- package/javascript/dist/esm/usearch.d.ts +39 -1
- package/javascript/dist/esm/usearch.js +11 -1
- package/javascript/tsconfig-esm.json +3 -2
- package/javascript/usearch.ts +18 -7
- package/package.json +2 -2
- 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/simsimd/.cmake-format.py +19 -0
- package/simsimd/CONTRIBUTING.md +49 -8
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +10 -5
- package/simsimd/Package.swift +16 -0
- package/simsimd/README.md +69 -15
- package/simsimd/binding.gyp +14 -19
- package/simsimd/build.rs +2 -0
- package/simsimd/c/lib.c +18 -0
- package/simsimd/include/module.modulemap +4 -0
- package/simsimd/include/simsimd/binary.h +2 -0
- package/simsimd/include/simsimd/dot.h +466 -32
- package/simsimd/include/simsimd/probability.h +20 -10
- package/simsimd/include/simsimd/simsimd.h +444 -98
- package/simsimd/include/simsimd/spatial.h +480 -44
- package/simsimd/include/simsimd/types.h +161 -39
- package/simsimd/javascript/simsimd.ts +26 -2
- package/simsimd/package.json +1 -1
- package/simsimd/rust/benches/cosine.rs +8 -2
- package/simsimd/rust/benches/native.rs +240 -2
- package/simsimd/rust/benches/sqeuclidean.rs +8 -2
- package/simsimd/rust/lib.rs +50 -13
- package/simsimd/swift/source/SimSIMD.swift +123 -0
- package/simsimd/swift/tests/SimSIMDTests.swift +92 -0
- package/prebuilds/win32-ia32/usearch.node +0 -0
- package/prebuilds/win32-x64/usearch.node +0 -0
- package/simsimd/.releaserc +0 -87
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Linux • MacOS • Windows • iOS • WebAssembly •
|
|
|
49
49
|
|
|
50
50
|
- ✅ __[10x faster][faster-than-faiss]__ [HNSW][hnsw-algorithm] implementation than [FAISS][faiss].
|
|
51
51
|
- ✅ Simple and extensible [single C++11 header][usearch-header] __library__.
|
|
52
|
-
- ✅ [Trusted](#integrations) by giants like Google and
|
|
52
|
+
- ✅ [Trusted](#integrations) by giants like Google and DBs like [ClickHouse][clickhouse-docs] & [DuckDB][duckdb-docs].
|
|
53
53
|
- ✅ [SIMD][simd]-optimized and [user-defined metrics](#user-defined-functions) with JIT compilation.
|
|
54
54
|
- ✅ Hardware-agnostic `f16` & `i8` - [half-precision & quarter-precision support](#memory-efficiency-downcasting-and-quantization).
|
|
55
55
|
- ✅ [View large indexes from disk](#serving-index-from-disk) without loading into RAM.
|
|
@@ -67,6 +67,7 @@ Linux • MacOS • Windows • iOS • WebAssembly •
|
|
|
67
67
|
[simd]: https://en.wikipedia.org/wiki/Single_instruction,_multiple_data
|
|
68
68
|
[faster-than-faiss]: https://www.unum.cloud/blog/2023-11-07-scaling-vector-search-with-intel
|
|
69
69
|
[clickhouse-docs]: https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/annindexes#usearch
|
|
70
|
+
[duckdb-docs]: https://duckdb.org/2024/05/03/vector-similarity-search-vss.html
|
|
70
71
|
|
|
71
72
|
__Technical Insights__ and related articles:
|
|
72
73
|
|
|
@@ -118,7 +119,7 @@ USearch is compact and broadly compatible without sacrificing performance, prima
|
|
|
118
119
|
Base functionality is identical to FAISS, and the interface must be familiar if you have ever investigated Approximate Nearest Neighbors search:
|
|
119
120
|
|
|
120
121
|
```py
|
|
121
|
-
|
|
122
|
+
# pip install numpy usearch
|
|
122
123
|
|
|
123
124
|
import numpy as np
|
|
124
125
|
from usearch.index import Index
|
|
@@ -136,12 +137,13 @@ assert np.allclose(index[42], vector, atol=0.1) # Ensure high tolerance in mixed
|
|
|
136
137
|
```
|
|
137
138
|
|
|
138
139
|
More settings are always available, and the API is designed to be as flexible as possible.
|
|
140
|
+
The default storage/quantization level is hardware-dependant for efficiency, but `f16` is recommended for most modern CPUs.
|
|
139
141
|
|
|
140
142
|
```py
|
|
141
143
|
index = Index(
|
|
142
144
|
ndim=3, # Define the number of dimensions in input vectors
|
|
143
|
-
metric='cos', # Choose 'l2sq', 'haversine' or other metric, default = '
|
|
144
|
-
dtype='
|
|
145
|
+
metric='cos', # Choose 'l2sq', 'ip', 'haversine' or other metric, default = 'cos'
|
|
146
|
+
dtype='f16', # Store as 'f64', 'f32', 'f16', 'i8', 'b1'..., default = None
|
|
145
147
|
connectivity=16, # Optional: Limit number of neighbors per graph node
|
|
146
148
|
expansion_add=128, # Optional: Control the recall of indexing
|
|
147
149
|
expansion_search=64, # Optional: Control the quality of the search
|
|
@@ -360,41 +362,58 @@ In some cases, like Batch operations, feature parity is meaningless, as the host
|
|
|
360
362
|
|
|
361
363
|
## Application Examples
|
|
362
364
|
|
|
363
|
-
### USearch +
|
|
364
|
-
|
|
365
|
-
[](https://github.com/ashvardanian/usearch-images)
|
|
365
|
+
### USearch + UForm + UCall = Multimodal Semantic Search
|
|
366
366
|
|
|
367
367
|
AI has a growing number of applications, but one of the coolest classic ideas is to use it for Semantic Search.
|
|
368
|
-
One can take an encoder model, like the multi-modal [UForm](https://github.com/unum-cloud/uform), and a web-programming framework, like UCall, and build a text-to-image search platform in just 20 lines of Python.
|
|
368
|
+
One can take an encoder model, like the multi-modal [UForm](https://github.com/unum-cloud/uform), and a web-programming framework, like [UCall](https://github.com/unum-cloud/ucall), and build a text-to-image search platform in just 20 lines of Python.
|
|
369
369
|
|
|
370
370
|
```python
|
|
371
|
-
import
|
|
372
|
-
import
|
|
373
|
-
import
|
|
371
|
+
from ucall import Server
|
|
372
|
+
from uform import get_model, Modality
|
|
373
|
+
from usearch.index import Index
|
|
374
374
|
|
|
375
375
|
import numpy as np
|
|
376
376
|
import PIL as pil
|
|
377
377
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
378
|
+
processors, models = get_model('unum-cloud/uform3-image-text-english-small')
|
|
379
|
+
model_text = models[Modality.TEXT_ENCODER]
|
|
380
|
+
model_image = models[Modality.IMAGE_ENCODER]
|
|
381
|
+
processor_text = processors[Modality.TEXT_ENCODER]
|
|
382
|
+
processor_image = processors[Modality.IMAGE_ENCODER]
|
|
383
|
+
|
|
384
|
+
server = Server()
|
|
385
|
+
index = Index(ndim=256)
|
|
381
386
|
|
|
382
387
|
@server
|
|
383
388
|
def add(key: int, photo: pil.Image.Image):
|
|
384
|
-
image =
|
|
385
|
-
vector =
|
|
389
|
+
image = processor_image(photo)
|
|
390
|
+
vector = model_image(image)
|
|
386
391
|
index.add(key, vector.flatten(), copy=True)
|
|
387
392
|
|
|
388
393
|
@server
|
|
389
394
|
def search(query: str) -> np.ndarray:
|
|
390
|
-
tokens =
|
|
391
|
-
vector =
|
|
395
|
+
tokens = processor_text(query)
|
|
396
|
+
vector = model_text(tokens)
|
|
392
397
|
matches = index.search(vector.flatten(), 3)
|
|
393
398
|
return matches.keys
|
|
394
399
|
|
|
395
400
|
server.run()
|
|
396
401
|
```
|
|
397
402
|
|
|
403
|
+
Similar experiences can also be imlemented in other languages and on the client side, removing the network latency.
|
|
404
|
+
For Swift and iOS, check out the [`ashvardanian/SwiftSemanticSearch`](https://github.com/ashvardanian/SwiftSemanticSearch) repository.
|
|
405
|
+
|
|
406
|
+
<table>
|
|
407
|
+
<tr>
|
|
408
|
+
<td>
|
|
409
|
+
<img src="https://github.com/ashvardanian/ashvardanian/blob/master/demos/SwiftSemanticSearch-Dog.gif?raw=true" alt="SwiftSemanticSearch demo Dog">
|
|
410
|
+
</td>
|
|
411
|
+
<td>
|
|
412
|
+
<img src="https://github.com/ashvardanian/ashvardanian/blob/master/demos/SwiftSemanticSearch-Flowers.gif?raw=true" alt="SwiftSemanticSearch demo with Flowers">
|
|
413
|
+
</td>
|
|
414
|
+
</tr>
|
|
415
|
+
</table>
|
|
416
|
+
|
|
398
417
|
A more complete [demo with Streamlit is available on GitHub](https://github.com/ashvardanian/usearch-images).
|
|
399
418
|
We have pre-processed some commonly used datasets, cleaned the images, produced the vectors, and pre-built the index.
|
|
400
419
|
|
|
@@ -446,32 +465,83 @@ That method was used to build the ["USearch Molecules"](https://github.com/ashva
|
|
|
446
465
|
[smiles]: https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system
|
|
447
466
|
[rdkit-fingerprints]: https://www.rdkit.org/docs/RDKit_Book.html#additional-information-about-the-fingerprints
|
|
448
467
|
|
|
449
|
-
### USearch + POI Coordinates = GIS Applications
|
|
468
|
+
### USearch + POI Coordinates = GIS Applications
|
|
450
469
|
|
|
451
|
-
|
|
470
|
+
Similar to Vector and Molecule search, USearch can be used for Geospatial Information Systems.
|
|
471
|
+
The Haversine distance is available out of the box, but you can also define more complex relationships, like the Vincenty formula, that accounts for the Earth's oblateness.
|
|
452
472
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
473
|
+
```py
|
|
474
|
+
from numba import cfunc, types, carray
|
|
475
|
+
import math
|
|
476
|
+
|
|
477
|
+
# Define the dimension as 2 for latitude and longitude
|
|
478
|
+
ndim = 2
|
|
479
|
+
|
|
480
|
+
# Signature for the custom metric
|
|
481
|
+
signature = types.float32(
|
|
482
|
+
types.CPointer(types.float32),
|
|
483
|
+
types.CPointer(types.float32))
|
|
484
|
+
|
|
485
|
+
# WGS-84 ellipsoid parameters
|
|
486
|
+
a = 6378137.0 # major axis in meters
|
|
487
|
+
f = 1 / 298.257223563 # flattening
|
|
488
|
+
b = (1 - f) * a # minor axis
|
|
489
|
+
|
|
490
|
+
def vincenty_distance(a_ptr, b_ptr):
|
|
491
|
+
a_array = carray(a_ptr, ndim)
|
|
492
|
+
b_array = carray(b_ptr, ndim)
|
|
493
|
+
lat1, lon1, lat2, lon2 = a_array[0], a_array[1], b_array[0], b_array[1]
|
|
494
|
+
L, U1, U2 = lon2 - lon1, math.atan((1 - f) * math.tan(lat1)), math.atan((1 - f) * math.tan(lat2))
|
|
495
|
+
sinU1, cosU1, sinU2, cosU2 = math.sin(U1), math.cos(U1), math.sin(U2), math.cos(U2)
|
|
496
|
+
lambda_, iterLimit = L, 100
|
|
497
|
+
while iterLimit > 0:
|
|
498
|
+
iterLimit -= 1
|
|
499
|
+
sinLambda, cosLambda = math.sin(lambda_), math.cos(lambda_)
|
|
500
|
+
sinSigma = math.sqrt((cosU2 * sinLambda) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) ** 2)
|
|
501
|
+
if sinSigma == 0: return 0.0 # Co-incident points
|
|
502
|
+
cosSigma, sigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda, math.atan2(sinSigma, cosSigma)
|
|
503
|
+
sinAlpha, cos2Alpha = cosU1 * cosU2 * sinLambda / sinSigma, 1 - (cosU1 * cosU2 * sinLambda / sinSigma) ** 2
|
|
504
|
+
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cos2Alpha if not math.isnan(cosSigma - 2 * sinU1 * sinU2 / cos2Alpha) else 0 # Equatorial line
|
|
505
|
+
C = f / 16 * cos2Alpha * (4 + f * (4 - 3 * cos2Alpha))
|
|
506
|
+
lambda_, lambdaP = L + (1 - C) * f * (sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM ** 2)))), lambda_
|
|
507
|
+
if abs(lambda_ - lambdaP) <= 1e-12: break
|
|
508
|
+
if iterLimit == 0: return float('nan') # formula failed to converge
|
|
509
|
+
u2 = cos2Alpha * (a ** 2 - b ** 2) / (b ** 2)
|
|
510
|
+
A = 1 + u2 / 16384 * (4096 + u2 * (-768 + u2 * (320 - 175 * u2)))
|
|
511
|
+
B = u2 / 1024 * (256 + u2 * (-128 + u2 * (74 - 47 * u2)))
|
|
512
|
+
deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM ** 2) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)))
|
|
513
|
+
s = b * A * (sigma - deltaSigma)
|
|
514
|
+
return s / 1000.0 # Distance in kilometers
|
|
515
|
+
|
|
516
|
+
# Example usage:
|
|
517
|
+
index = Index(ndim=ndim, metric=CompiledMetric(
|
|
518
|
+
pointer=vincenty_distance.address,
|
|
519
|
+
kind=MetricKind.Haversine,
|
|
520
|
+
signature=MetricSignature.ArrayArray,
|
|
521
|
+
))
|
|
522
|
+
```
|
|
457
523
|
|
|
458
|
-
## Integrations
|
|
524
|
+
## Integrations & Users
|
|
459
525
|
|
|
460
|
-
- [x]
|
|
526
|
+
- [x] ClickHouse: [C++](https://github.com/ClickHouse/ClickHouse/pull/53447), [docs](https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/annindexes#usearch).
|
|
527
|
+
- [x] DuckDB: [post](https://duckdb.org/2024/05/03/vector-similarity-search-vss.html).
|
|
528
|
+
- [x] Google: [UniSim](https://github.com/google/unisim), [RetSim](https://arxiv.org/abs/2311.17264) paper.
|
|
529
|
+
- [x] LanternDB: [C++](https://github.com/lanterndata/lantern), [Rust](https://github.com/lanterndata/lantern_extras), [docs](https://lantern.dev/blog/hnsw-index-creation).
|
|
461
530
|
- [x] LangChain: [Python](https://github.com/langchain-ai/langchain/releases/tag/v0.0.257) and [JavaScript](https://github.com/hwchase17/langchainjs/releases/tag/0.0.125).
|
|
462
|
-
- [x] ClickHouse: [C++](https://github.com/ClickHouse/ClickHouse/pull/53447).
|
|
463
531
|
- [x] Microsoft Semantic Kernel: [Python](https://github.com/microsoft/semantic-kernel/releases/tag/python-0.3.9.dev) and C#.
|
|
464
|
-
- [x]
|
|
532
|
+
- [x] GPTCache: [Python](https://github.com/zilliztech/GPTCache/releases/tag/0.1.29).
|
|
533
|
+
- [x] Sentence-Transformers: Python [docs](https://www.sbert.net/docs/package_reference/quantization.html#sentence_transformers.quantization.semantic_search_usearch).
|
|
534
|
+
- [x] Pathway: [Rust](https://github.com/pathwaycom/pathway).
|
|
465
535
|
|
|
466
536
|
## Citations
|
|
467
537
|
|
|
468
|
-
```
|
|
538
|
+
```bibtex
|
|
469
539
|
@software{Vardanian_USearch_2023,
|
|
470
540
|
doi = {10.5281/zenodo.7949416},
|
|
471
541
|
author = {Vardanian, Ash},
|
|
472
542
|
title = {{USearch by Unum Cloud}},
|
|
473
543
|
url = {https://github.com/unum-cloud/usearch},
|
|
474
|
-
version = {2.
|
|
544
|
+
version = {2.13.1},
|
|
475
545
|
year = {2023},
|
|
476
546
|
month = oct,
|
|
477
547
|
}
|
package/binding.gyp
CHANGED
|
@@ -25,15 +25,9 @@
|
|
|
25
25
|
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
|
26
26
|
"CLANG_CXX_LIBRARY": "libc++",
|
|
27
27
|
"MACOSX_DEPLOYMENT_TARGET": "11.0",
|
|
28
|
-
"OTHER_CFLAGS": [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
],
|
|
32
|
-
"OTHER_LDFLAGS": [
|
|
33
|
-
"-arch arm64",
|
|
34
|
-
"-arch x86_64"
|
|
35
|
-
]
|
|
36
|
-
},
|
|
28
|
+
"OTHER_CFLAGS": ["-arch arm64", "-arch x86_64"],
|
|
29
|
+
"OTHER_LDFLAGS": ["-arch arm64", "-arch x86_64"],
|
|
30
|
+
},
|
|
37
31
|
"msvs_settings": {
|
|
38
32
|
"VCCLCompilerTool": {
|
|
39
33
|
"ExceptionHandling": 1,
|
|
@@ -47,18 +41,19 @@
|
|
|
47
41
|
"cflags_cc": [
|
|
48
42
|
'<!(if [ "$USEARCH_USE_OPENMP" = "1" ]; then echo \'-fopenmp\'; fi)',
|
|
49
43
|
],
|
|
50
|
-
"ldflags": [
|
|
51
|
-
'<!(if [ "$USEARCH_USE_OPENMP" = "1" ]; then echo \'-lgomp\'; fi)'
|
|
52
|
-
],
|
|
44
|
+
"ldflags": ['<!(if [ "$USEARCH_USE_OPENMP" = "1" ]; then echo \'-lgomp\'; fi)'],
|
|
53
45
|
"defines": [
|
|
54
46
|
"USEARCH_USE_OPENMP=<!(echo ${USEARCH_USE_OPENMP:-0})",
|
|
55
47
|
"USEARCH_USE_SIMSIMD=<!(echo ${USEARCH_USE_SIMSIMD:-1})",
|
|
56
48
|
"USEARCH_USE_FP16LIB=<!(echo ${USEARCH_USE_FP16LIB:-1})",
|
|
57
49
|
"SIMSIMD_TARGET_NEON=<!(echo ${SIMSIMD_TARGET_NEON:-1})",
|
|
50
|
+
"SIMSIMD_TARGET_NEON_BF16=<!(echo ${SIMSIMD_TARGET_NEON_BF16:-0})",
|
|
58
51
|
"SIMSIMD_TARGET_SVE=<!(echo ${SIMSIMD_TARGET_SVE:-1})",
|
|
52
|
+
"SIMSIMD_TARGET_SVE_BF16=<!(echo ${SIMSIMD_TARGET_SVE_BF16:-0})",
|
|
59
53
|
"SIMSIMD_TARGET_HASWELL=<!(echo ${SIMSIMD_TARGET_HASWELL:-1})",
|
|
60
54
|
"SIMSIMD_TARGET_SKYLAKE=<!(echo ${SIMSIMD_TARGET_SKYLAKE:-1})",
|
|
61
55
|
"SIMSIMD_TARGET_ICE=<!(echo ${SIMSIMD_TARGET_ICE:-1})",
|
|
56
|
+
"SIMSIMD_TARGET_GENOA=<!(echo ${SIMSIMD_TARGET_GENOA:-1})",
|
|
62
57
|
"SIMSIMD_TARGET_SAPPHIRE=<!(echo ${SIMSIMD_TARGET_SAPPHIRE:-1})",
|
|
63
58
|
],
|
|
64
59
|
},
|
|
@@ -71,10 +66,12 @@
|
|
|
71
66
|
"USEARCH_USE_SIMSIMD=<!(echo ${USEARCH_USE_SIMSIMD:-0})",
|
|
72
67
|
"USEARCH_USE_FP16LIB=<!(echo ${USEARCH_USE_FP16LIB:-1})",
|
|
73
68
|
"SIMSIMD_TARGET_NEON=<!(echo ${SIMSIMD_TARGET_NEON:-1})",
|
|
69
|
+
"SIMSIMD_TARGET_NEON_BF16=<!(echo ${SIMSIMD_TARGET_NEON_BF16:-0})",
|
|
74
70
|
"SIMSIMD_TARGET_SVE=<!(echo ${SIMSIMD_TARGET_SVE:-0})",
|
|
75
71
|
"SIMSIMD_TARGET_HASWELL=<!(echo ${SIMSIMD_TARGET_HASWELL:-1})",
|
|
76
72
|
"SIMSIMD_TARGET_SKYLAKE=<!(echo ${SIMSIMD_TARGET_SKYLAKE:-0})",
|
|
77
73
|
"SIMSIMD_TARGET_ICE=<!(echo ${SIMSIMD_TARGET_ICE:-0})",
|
|
74
|
+
"SIMSIMD_TARGET_GENOA=<!(echo ${SIMSIMD_TARGET_GENOA:-0})",
|
|
78
75
|
"SIMSIMD_TARGET_SAPPHIRE=<!(echo ${SIMSIMD_TARGET_SAPPHIRE:-0})",
|
|
79
76
|
],
|
|
80
77
|
},
|
|
@@ -89,9 +86,11 @@
|
|
|
89
86
|
"SIMSIMD_TARGET_SKYLAKE=0",
|
|
90
87
|
"SIMSIMD_TARGET_ICE=0",
|
|
91
88
|
"SIMSIMD_TARGET_SAPPHIRE=0",
|
|
89
|
+
"SIMSIMD_TARGET_GENOA=0",
|
|
92
90
|
"SIMSIMD_TARGET_SVE=0",
|
|
93
91
|
"SIMSIMD_TARGET_HASWELL=0",
|
|
94
92
|
"SIMSIMD_TARGET_NEON=0",
|
|
93
|
+
"SIMSIMD_TARGET_NEON_BF16=0",
|
|
95
94
|
],
|
|
96
95
|
},
|
|
97
96
|
],
|
package/c/README.md
CHANGED
|
@@ -207,6 +207,9 @@ By default, as many of these structures are created as there are logical cores o
|
|
|
207
207
|
Then, one can construct the index in parallel using many cores using third-party thread-pools or tools like OpenACC and OpenMP.
|
|
208
208
|
|
|
209
209
|
```c
|
|
210
|
+
usearch_change_threads_add(index, omp_get_max_threads(), &error);
|
|
211
|
+
usearch_change_threads_search(index, omp_get_max_threads(), &error);
|
|
212
|
+
|
|
210
213
|
#pragma omp parallel for
|
|
211
214
|
for (size_t i = 0; i < 1000; i++) {
|
|
212
215
|
usearch_add(index, i, &vector[0], usearch_scalar_f32_k, &error);
|
package/c/lib.cpp
CHANGED
|
@@ -94,7 +94,7 @@ std::size_t get_(index_dense_t* index, usearch_key_t key, size_t count, void* ve
|
|
|
94
94
|
case scalar_kind_t::f16_k: return index->get(key, (f16_t*)vector, count);
|
|
95
95
|
case scalar_kind_t::i8_k: return index->get(key, (i8_t*)vector, count);
|
|
96
96
|
case scalar_kind_t::b1x8_k: return index->get(key, (b1x8_t*)vector, count);
|
|
97
|
-
default: return search_result_t().failed("Unknown scalar kind!");
|
|
97
|
+
default: return search_result_t(*index).failed("Unknown scalar kind!");
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -112,7 +112,7 @@ search_result_t search_(index_dense_t* index, void const* vector, scalar_kind_t
|
|
|
112
112
|
return index->filtered_search((i8_t const*)vector, n, std::forward<predicate_at>(predicate));
|
|
113
113
|
case scalar_kind_t::b1x8_k:
|
|
114
114
|
return index->filtered_search((b1x8_t const*)vector, n, std::forward<predicate_at>(predicate));
|
|
115
|
-
default: return search_result_t().failed("Unknown scalar kind!");
|
|
115
|
+
default: return search_result_t(*index).failed("Unknown scalar kind!");
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -123,19 +123,32 @@ USEARCH_EXPORT char const* usearch_version(void) {
|
|
|
123
123
|
int minor = USEARCH_VERSION_MINOR;
|
|
124
124
|
int patch = USEARCH_VERSION_PATCH;
|
|
125
125
|
static char version[32];
|
|
126
|
-
|
|
126
|
+
std::snprintf(version, sizeof(version), "%d.%d.%d", major, minor, patch);
|
|
127
127
|
return version;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) {
|
|
131
131
|
|
|
132
|
-
USEARCH_ASSERT(
|
|
132
|
+
USEARCH_ASSERT(error && "Missing arguments");
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
// The user may want to initialize from a file.
|
|
135
|
+
// In that case he may pass NULL options, and we will try to load the metadata from the file.
|
|
136
|
+
if (!options) {
|
|
137
|
+
index_dense_t* result_ptr = new index_dense_t();
|
|
138
|
+
if (!result_ptr)
|
|
139
|
+
*error = "Out of memory!";
|
|
140
|
+
return result_ptr;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
index_dense_config_t config;
|
|
144
|
+
config.connectivity = options->connectivity;
|
|
145
|
+
config.expansion_add = options->expansion_add;
|
|
146
|
+
config.expansion_search = options->expansion_search;
|
|
135
147
|
config.multi = options->multi;
|
|
148
|
+
config.enable_key_lookups = 1;
|
|
149
|
+
|
|
136
150
|
metric_kind_t metric_kind = metric_kind_to_cpp(options->metric_kind);
|
|
137
151
|
scalar_kind_t scalar_kind = scalar_kind_to_cpp(options->quantization);
|
|
138
|
-
|
|
139
152
|
metric_punned_t metric = //
|
|
140
153
|
!options->metric ? metric_punned_t::builtin(options->dimensions, metric_kind, scalar_kind)
|
|
141
154
|
: metric_punned_t::stateless(options->dimensions, //
|
|
@@ -147,10 +160,18 @@ USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, use
|
|
|
147
160
|
return NULL;
|
|
148
161
|
}
|
|
149
162
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (!
|
|
163
|
+
using state_result_t = typename index_dense_t::state_result_t;
|
|
164
|
+
state_result_t state = index_dense_t::make(metric, config);
|
|
165
|
+
if (!state)
|
|
166
|
+
*error = state.error.release();
|
|
167
|
+
index_dense_t* result_ptr = new index_dense_t(std::move(state.index));
|
|
168
|
+
if (!result_ptr)
|
|
153
169
|
*error = "Out of memory!";
|
|
170
|
+
|
|
171
|
+
// Let's immediately make it usable by reserving enough threads for this machine:
|
|
172
|
+
if (!result_ptr->try_reserve(index_limits_t()))
|
|
173
|
+
*error = "Out of memory when preparing contexts!";
|
|
174
|
+
|
|
154
175
|
return result_ptr;
|
|
155
176
|
}
|
|
156
177
|
|
|
@@ -304,6 +325,22 @@ USEARCH_EXPORT void usearch_change_expansion_search(usearch_index_t index, size_
|
|
|
304
325
|
reinterpret_cast<index_dense_t*>(index)->change_expansion_search(expansion);
|
|
305
326
|
}
|
|
306
327
|
|
|
328
|
+
USEARCH_EXPORT void usearch_change_threads_add(usearch_index_t index, size_t threads, usearch_error_t* error) {
|
|
329
|
+
USEARCH_ASSERT(index && error && "Missing arguments");
|
|
330
|
+
auto& index_dense = *reinterpret_cast<index_dense_t*>(index);
|
|
331
|
+
index_limits_t limits = index_dense.limits();
|
|
332
|
+
limits.threads_add = threads;
|
|
333
|
+
index_dense.try_reserve(limits);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
USEARCH_EXPORT void usearch_change_threads_search(usearch_index_t index, size_t threads, usearch_error_t* error) {
|
|
337
|
+
USEARCH_ASSERT(index && error && "Missing arguments");
|
|
338
|
+
auto& index_dense = *reinterpret_cast<index_dense_t*>(index);
|
|
339
|
+
index_limits_t limits = index_dense.limits();
|
|
340
|
+
limits.threads_search = threads;
|
|
341
|
+
index_dense.try_reserve(limits);
|
|
342
|
+
}
|
|
343
|
+
|
|
307
344
|
USEARCH_EXPORT void usearch_change_metric_kind(usearch_index_t index, usearch_metric_kind_t kind,
|
|
308
345
|
usearch_error_t* error) {
|
|
309
346
|
USEARCH_ASSERT(index && error && "Missing arguments");
|
|
@@ -317,9 +354,9 @@ USEARCH_EXPORT void usearch_change_metric(usearch_index_t index, usearch_metric_
|
|
|
317
354
|
USEARCH_ASSERT(index && error && "Missing arguments");
|
|
318
355
|
auto& index_dense = *reinterpret_cast<index_dense_t*>(index);
|
|
319
356
|
auto metric_punned =
|
|
320
|
-
state ? metric_punned_t::
|
|
321
|
-
|
|
322
|
-
|
|
357
|
+
state ? metric_punned_t::stateful(reinterpret_cast<std::uintptr_t>(metric),
|
|
358
|
+
reinterpret_cast<std::uintptr_t>(state), metric_kind_to_cpp(kind),
|
|
359
|
+
index_dense.scalar_kind())
|
|
323
360
|
: metric_punned_t::stateless(index_dense.dimensions(), reinterpret_cast<std::uintptr_t>(metric),
|
|
324
361
|
metric_punned_signature_t::array_array_k, metric_kind_to_cpp(kind),
|
|
325
362
|
index_dense.scalar_kind());
|
|
@@ -328,7 +365,7 @@ USEARCH_EXPORT void usearch_change_metric(usearch_index_t index, usearch_metric_
|
|
|
328
365
|
|
|
329
366
|
USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t* error) {
|
|
330
367
|
USEARCH_ASSERT(index && error && "Missing arguments");
|
|
331
|
-
if (!reinterpret_cast<index_dense_t*>(index)->
|
|
368
|
+
if (!reinterpret_cast<index_dense_t*>(index)->try_reserve(capacity))
|
|
332
369
|
*error = "Out of memory!";
|
|
333
370
|
}
|
|
334
371
|
|