usearch 0.14.0 → 0.15.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.
Files changed (64) hide show
  1. package/.github/workflows/release.yml +56 -4
  2. package/.github/workflows/update_version.sh +6 -1
  3. package/.vscode/launch.json +21 -22
  4. package/.vscode/settings.json +7 -1
  5. package/CITATION.cff +4 -4
  6. package/CMakeLists.txt +1 -1
  7. package/Cargo.toml +1 -1
  8. package/Package.swift +0 -1
  9. package/README.md +100 -27
  10. package/binding.gyp +0 -1
  11. package/build.gradle +1 -1
  12. package/build.rs +3 -3
  13. package/c/lib.cpp +32 -9
  14. package/c/usearch.h +67 -36
  15. package/conanfile.py +1 -1
  16. package/{src → cpp}/bench.cpp +52 -36
  17. package/{src → cpp}/test.cpp +17 -7
  18. package/docs/benchmarks.md +33 -10
  19. package/docs/c/index.rst +11 -0
  20. package/docs/c/reference.rst +7 -0
  21. package/docs/compilation.md +24 -9
  22. package/docs/conf.dox +6 -6
  23. package/docs/conf.py +3 -1
  24. package/docs/cpp/index.rst +11 -0
  25. package/docs/cpp/reference.rst +7 -0
  26. package/docs/index.rst +10 -0
  27. package/docs/java/index.rst +11 -0
  28. package/docs/java/reference.rst +7 -0
  29. package/docs/javascript/index.rst +11 -0
  30. package/docs/javascript/reference.rst +9 -0
  31. package/docs/python/index.rst +11 -0
  32. package/docs/python/reference.rst +35 -0
  33. package/golang/lib.go +1 -1
  34. package/include/usearch/{usearch.hpp → index.hpp} +152 -148
  35. package/include/usearch/index_punned_dense.hpp +633 -0
  36. package/include/usearch/index_punned_helpers.hpp +528 -0
  37. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +8 -7
  38. package/javascript/lib.cpp +26 -50
  39. package/javascript/usearch.js +117 -2
  40. package/objc/USearchObjective.mm +145 -49
  41. package/objc/include/USearchObjective.h +106 -30
  42. package/package.json +2 -2
  43. package/python/lib.cpp +134 -331
  44. package/python/scripts/bench.ipynb +170 -0
  45. package/python/scripts/bench.py +87 -175
  46. package/python/scripts/index_faiss.py +100 -0
  47. package/python/scripts/test.py +41 -37
  48. package/python/usearch/eval.py +353 -0
  49. package/python/usearch/index.py +117 -113
  50. package/python/usearch/io.py +7 -2
  51. package/python/usearch/join.py +100 -0
  52. package/python/usearch/numba.py +101 -0
  53. package/rust/lib.cpp +27 -14
  54. package/rust/lib.hpp +4 -10
  55. package/rust/lib.rs +50 -17
  56. package/setup.py +12 -2
  57. package/simsimd/include/simsimd/simsimd.h +32 -3
  58. package/swift/Index+Sugar.swift +55 -29
  59. package/swift/Test.swift +1 -1
  60. package/wolfram/lib.cpp +4 -4
  61. package/python/scripts/bench_params.py +0 -201
  62. package/python/usearch/synthetic.py +0 -31
  63. package/src/punned.hpp +0 -906
  64. /package/{src → cpp}/CMakeLists.txt +0 -0
@@ -27,6 +27,7 @@ jobs:
27
27
  - uses: actions/setup-node@v3
28
28
  - run: npm install --save-dev @semantic-release/exec @semantic-release/git conventional-changelog-eslint semantic-release && npx semantic-release
29
29
 
30
+
30
31
  rebase:
31
32
  name: Rebase Development Branch
32
33
  needs: versioning
@@ -50,6 +51,51 @@ jobs:
50
51
  unprotect_reviews: True
51
52
  force: True
52
53
 
54
+
55
+ publish_hashes:
56
+ name: Add hashes to release notes
57
+ runs-on: ubuntu-latest
58
+ needs: versioning
59
+ steps:
60
+ - name: Get last release ID
61
+ id: last_release
62
+ run: |
63
+ response=$(curl -s -H "Authorization: Bearer ${{ secrets.SEMANTIC_RELEASE_TOKEN }}" \
64
+ "https://api.github.com/repos/${{ github.repository }}/releases/latest")
65
+ echo "last_release_id=$(echo "$response" | jq -r '.id')" >> $GITHUB_OUTPUT
66
+
67
+ - uses: robinraju/release-downloader@v1.8
68
+ name: Download release assets
69
+ with:
70
+ latest: true
71
+ fileName: "*"
72
+ out-file-path: "downloads"
73
+ tarBall: true
74
+ zipBall: true
75
+
76
+ - name: Calculate source hashes
77
+ id: hashes
78
+ run: |
79
+ source_zip_path=$(find $GITHUB_WORKSPACE/downloads -name "*.zip")
80
+ zip_hash=$(sha256sum "$source_zip_path" | awk '{print $1}')
81
+ source_tar_path=$(find $GITHUB_WORKSPACE/downloads -name "*.tar.gz" ! -name "docs.tar.gz")
82
+ tar_hash=$(sha256sum "$source_tar_path" | awk '{print $1}')
83
+ echo "zip_hash=$zip_hash" >> $GITHUB_OUTPUT
84
+ echo "tar_hash=$tar_hash" >> $GITHUB_OUTPUT
85
+
86
+ - name: Upload hashes to release
87
+ uses: irongut/EditRelease@v1.2.0
88
+ with:
89
+ token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
90
+ id: ${{ steps.last_release.outputs.last_release_id }}
91
+ prerelease: false
92
+ replacebody: false
93
+ body: |
94
+ ## Hashes
95
+ * Source code (zip) : `${{ steps.hashes.outputs.zip_hash }}`
96
+ * Source code (tar.gz) : `${{ steps.hashes.outputs.tar_hash }}`
97
+
98
+
53
99
  build_wheels:
54
100
  name: Build Wheels for ${{ matrix.os }}
55
101
  needs: versioning
@@ -239,11 +285,15 @@ jobs:
239
285
  with:
240
286
  ref: 'main'
241
287
  - name: Install dependencies
242
- run: sudo apt update && sudo apt install -y doxygen graphviz dia git && pip install sphinx breathe furo m2r2 sphinxcontrib-googleanalytics==0.2.dev20220708 sphinxcontrib-jquery
288
+ run: |
289
+ sudo apt update &&
290
+ sudo apt install -y doxygen graphviz dia git &&
291
+ pip install sphinx sphinx-js breathe furo m2r2 sphinxcontrib-googleanalytics==0.2.dev20220708 sphinxcontrib-jquery &&
292
+ npm install -g jsdoc
243
293
  - name: Install USearch from PyPi
244
294
  run: pip install usearch
245
295
  - name: Build documentation
246
- run: cd docs && make html
296
+ run: cd docs && doxygen conf.dox && make html
247
297
  - name: Copy assets
248
298
  run: cp -r assets build/docs/html/
249
299
  - name: Compress assets
@@ -263,16 +313,19 @@ jobs:
263
313
  url: ${{ steps.deployment.outputs.page_url }}
264
314
  runs-on: ubuntu-22.04
265
315
  needs: build_docs
266
- if: ${{ github.event.build_docs.conclusion == 'success' }}
267
316
  steps:
268
317
  - uses: robinraju/release-downloader@v1.8
269
318
  with:
270
319
  latest: true
271
320
  fileName: docs.tar.gz
321
+ - name: Unpack docs
322
+ run: tar -xf ./docs.tar.gz
272
323
  - name: Setup GitHub Pages
273
324
  uses: actions/configure-pages@v2
274
325
  - name: Upload artifacts
275
326
  uses: actions/upload-pages-artifact@v1
327
+ with:
328
+ path: ./build/docs/html
276
329
  - name: Deploy to GitHub Pages
277
330
  id: deployment
278
331
  uses: actions/deploy-pages@v1
@@ -281,7 +334,6 @@ jobs:
281
334
  name: Deploy Vercel
282
335
  runs-on: ubuntu-22.04
283
336
  needs: build_docs
284
- if: ${{ github.event.build_docs.conclusion == 'success' }}
285
337
  steps:
286
338
  - name: Notify Vercel
287
339
  uses: fjogeleit/http-request-action@v1
@@ -4,4 +4,9 @@ echo $1 > VERSION &&
4
4
  sed -i "s/\"version\": \".*\"/\"version\": \"$1\"/" package.json &&
5
5
  sed -i "s/^version: .*/version: $1/" CITATION.cff &&
6
6
  sed -i "s/version = \".*\"/version = \"$1\"/" Cargo.toml &&
7
- sed -i "s/version = '.*'/version = '$1'/" conanfile.py
7
+ sed -i "s/version = '.*'/version = '$1'/" conanfile.py &&
8
+ sed -i "s/^\(#define USEARCH_VERSION_MAJOR \).*/\1$(echo "$1" | cut -d. -f1)/" ./include/usearch/index.hpp &&
9
+ sed -i "s/^\(#define USEARCH_VERSION_MINOR \).*/\1$(echo "$1" | cut -d. -f2)/" ./include/usearch/index.hpp &&
10
+ sed -i "s/^\(#define USEARCH_VERSION_PATCH \).*/\1$(echo "$1" | cut -d. -f3)/" ./include/usearch/index.hpp &&
11
+ sed -i "s/<version>[0-9]\+\.[0-9]\+\.[0-9]\+/<version>$1/" README.md &&
12
+ sed -i "s/version = {0\.[0-9]\+\.[0-9]\+}/version = {$1}/" README.md
@@ -113,33 +113,32 @@
113
113
  "MIMode": "gdb"
114
114
  },
115
115
  {
116
- "name": "Debug PyBind11 Test",
117
- "type": "pythoncpp",
116
+ "name": "Debug PyTest",
117
+ "type": "python",
118
118
  "request": "launch",
119
- "pythonLaunchName": "Python: Current File",
120
- "cppAttachName": "Debug PyBind11: Attach with GDB"
121
- },
122
- {
123
- "name": "Debug PyBind11: Attach with GDB",
124
- "type": "cppdbg",
125
- "request": "attach",
126
- "program": "/Users/av/miniconda3/bin/python",
127
- "processId": "",
128
- "MIMode": "lldb",
129
- "setupCommands": [
130
- {
131
- "description": "Enable pretty-printing for gdb",
132
- "text": "-enable-pretty-printing",
133
- "ignoreFailures": true
134
- }
135
- ]
119
+ "module": "pytest",
120
+ "console": "internalConsole",
121
+ "args": [
122
+ "${file}"
123
+ ],
124
+ "justMyCode": true
136
125
  },
137
126
  {
138
- "name": "Python: Current File",
127
+ "name": "Debug Python Benchmark",
139
128
  "type": "python",
140
129
  "request": "launch",
141
- "program": "${file}",
142
- "console": "integratedTerminal"
130
+ "program": "python/scripts/bench.py",
131
+ "console": "internalConsole",
132
+ "args": [
133
+ "speed",
134
+ "--vectors",
135
+ "datasets/wiki_1M/base.1M.fbin",
136
+ "--queries",
137
+ "datasets/wiki_1M/query.public.100K.fbin",
138
+ "--neighbors",
139
+ "datasets/wiki_1M/groundtruth.public.100K.ibin",
140
+ ],
141
+ "justMyCode": true
143
142
  }
144
143
  ]
145
144
  }
@@ -107,6 +107,7 @@
107
107
  "arange",
108
108
  "astype",
109
109
  "Availible",
110
+ "bidict",
110
111
  "BLAS",
111
112
  "cflags",
112
113
  "citerator",
@@ -146,5 +147,10 @@
146
147
  "usecases"
147
148
  ],
148
149
  "autoDocstring.docstringFormat": "sphinx",
149
- "java.configuration.updateBuildConfiguration": "interactive"
150
+ "java.configuration.updateBuildConfiguration": "interactive",
151
+ "python.testing.pytestArgs": [
152
+ "python"
153
+ ],
154
+ "python.testing.unittestEnabled": false,
155
+ "python.testing.pytestEnabled": true
150
156
  }
package/CITATION.cff CHANGED
@@ -2,10 +2,10 @@ cff-version: 1.2.0
2
2
  message: "If you use this software, please cite it as below."
3
3
  authors:
4
4
  - family-names: "Vardanian"
5
- given-names: "Ashot"
5
+ given-names: "Ash"
6
6
  orcid: "https://orcid.org/0000-0002-4882-1815"
7
- title: "USearch"
8
- version: 0.3.0
7
+ title: "USearch by Unum Cloud"
8
+ version: 0.13.0
9
9
  doi: 10.5281/zenodo.7949416
10
10
  date-released: 2022-06-12
11
- url: "https://github.com/unum-cloud/usearch"
11
+ url: "https://github.com/unum-cloud/usearch"
package/CMakeLists.txt CHANGED
@@ -112,7 +112,7 @@ if(USEARCH_INSTALL)
112
112
  endif()
113
113
 
114
114
  if(${USEARCH_BUILD_TEST} OR ${USEARCH_BUILD_BENCHMARK})
115
- add_subdirectory(src)
115
+ add_subdirectory(cpp)
116
116
  endif()
117
117
 
118
118
  if(${USEARCH_BUILD_WOLFRAM})
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "0.14.0"
3
+ version = "0.15.1"
4
4
  authors = ["Ashot Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/Package.swift CHANGED
@@ -18,7 +18,6 @@ let package = Package(
18
18
  sources: ["USearchObjective.mm"],
19
19
  cxxSettings: [
20
20
  .headerSearchPath("../include/"),
21
- .headerSearchPath("../src/"),
22
21
  .headerSearchPath("../fp16/include/"),
23
22
  .headerSearchPath("../robin-map/include/"),
24
23
  .headerSearchPath("../simismd/include/")
package/README.md CHANGED
@@ -48,9 +48,9 @@ Linux • MacOS • Windows • Docker • WebAssembly 🔜
48
48
  - [ ] Thread-safe `reserve`.
49
49
  - [ ] On-the-fly deletions.
50
50
  - [x] USearch + UForm Transformers = [Semantic Search](#ai--vector-search--semantic-search).
51
- - [x] USearch + RDKit = [Molecular Search](#ai--vector-search--semantic-search).
51
+ - [x] USearch + RDKit = [Molecule Search](#ai--vector-search--semantic-search).
52
52
 
53
- [usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/usearch.hpp
53
+ [usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/index.hpp
54
54
  [obscure-use-cases]: https://ashvardanian.com/posts/abusing-vector-search
55
55
 
56
56
  ---
@@ -133,14 +133,14 @@ Of course, the latency of external memory access will be higher, but it is in pa
133
133
 
134
134
  There are two usage patters:
135
135
 
136
- 1. Bare-bones with `usearch/usearch.hpp`, only available in C++.
136
+ 1. Bare-bones with `usearch/index.hpp`, only available in C++.
137
137
  2. Full-fat version with it's own threads, mutexes, type-punning, quantization, that is available both in C++ and is wrapped for higher-level bindings.
138
138
 
139
139
  ### C++
140
140
 
141
141
  #### Installation
142
142
 
143
- To use in a C++ project simply copy the `include/usearch/usearch.hpp` header into your project.
143
+ To use in a C++ project simply copy the `include/usearch/index.hpp` header into your project.
144
144
  Alternatively fetch it with CMake:
145
145
 
146
146
  ```cmake
@@ -164,7 +164,7 @@ index.add(/* label: */ 42, /* vector: */ {&vec[0], 3});
164
164
  auto results = index.search(/* query: */ {&vec[0], 3}, 5 /* neighbors */);
165
165
 
166
166
  for (std::size_t i = 0; i != results.size(); ++i)
167
- results[i].member.label, results[i].member.vector, results[i].distance;
167
+ results[i].element.label, results[i].element.vector, results[i].distance;
168
168
  ```
169
169
 
170
170
  The `add` is thread-safe for concurrent index construction.
@@ -205,9 +205,9 @@ The following distances are pre-packaged:
205
205
  - `ip_gt<scalar_t>` for "Inner Product" or "Dot Product" distance.
206
206
  - `l2sq_gt<scalar_t>` for the squared "L2" or "Euclidean" distance.
207
207
  - `jaccard_gt<scalar_t>` for "Jaccard" distance between two ordered sets of unique elements.
208
- - `bitwise_hamming_gt<scalar_t>` for "Hamming" distance, as the number of shared bits in hashes.
209
- - `bitwise_tanimoto_gt<scalar_t>` for "Tanimoto" coefficient for bit-strings.
210
- - `bitwise_sorensen_gt<scalar_t>` for "Dice-Sorensen" coefficient for bit-strings.
208
+ - `hamming_gt<scalar_t>` for "Hamming" distance, as the number of shared bits in hashes.
209
+ - `tanimoto_gt<scalar_t>` for "Tanimoto" coefficient for bit-strings.
210
+ - `sorensen_gt<scalar_t>` for "Dice-Sorensen" coefficient for bit-strings.
211
211
  - `pearson_correlation_gt<scalar_t>` for "Pearson" correlation between probability distributions.
212
212
  - `haversine_gt<scalar_t>` for "Haversine" or "Great Circle" distance between coordinates used in GIS applications.
213
213
 
@@ -299,19 +299,19 @@ Luckily, with the help of [Numba][numba], we can JIT compile a function with a m
299
299
  ```py
300
300
  from numba import cfunc, types, carray
301
301
 
302
+ ndim = 256
302
303
  signature = types.float32(
303
304
  types.CPointer(types.float32),
304
- types.CPointer(types.float32),
305
- types.uint64, types.uint64)
305
+ types.CPointer(types.float32))
306
306
 
307
307
  @cfunc(signature)
308
- def python_dot(a, b, n, m):
309
- a_array = carray(a, n)
310
- b_array = carray(b, n)
308
+ def python_dot(a, b):
309
+ a_array = carray(a, ndim)
310
+ b_array = carray(b, ndim)
311
311
  c = 0.0
312
- for i in range(n):
312
+ for i in range(ndim):
313
313
  c += a_array[i] * b_array[i]
314
- return c
314
+ return 1 - c
315
315
 
316
316
  index = Index(ndim=ndim, metric=python_dot.address)
317
317
  ```
@@ -374,8 +374,17 @@ cargo add usearch
374
374
  #### Quickstart
375
375
 
376
376
  ```rust
377
- let quant: &str = "f16";
378
- let index = new_ip(3, &quant, 0, 0, 0).unwrap();
377
+
378
+ let options = IndexOptions {
379
+ dimensions: 5,
380
+ metric: MetricKind::IP,
381
+ quantization: ScalarKind::F16,
382
+ connectivity: 0,
383
+ expansion_add: 0,
384
+ expansion_search: 0
385
+ };
386
+
387
+ let index = new_index(&options).unwrap();
379
388
 
380
389
  assert!(index.reserve(10).is_ok());
381
390
  assert!(index.capacity() >= 10);
@@ -497,11 +506,6 @@ func main() {
497
506
 
498
507
  ### Wolfram
499
508
 
500
- ## TODO
501
-
502
- - JavaScript: Allow calling from "worker threads".
503
- - Rust: Allow passing a custom thread ID.
504
- - C# .NET bindings.
505
509
 
506
510
  ## Application Examples
507
511
 
@@ -538,12 +542,33 @@ def search(query: str) -> np.ndarray:
538
542
  server.run()
539
543
  ```
540
544
 
545
+ We have pre-processed some commonly used datasets, cleaning the images, producing the vectors, and pre-building the index.
546
+
547
+ | Dataset | Size | Images | Preprocessed |
548
+ | :---------------------------------- | ---: | -----: | --------------------: |
549
+ | [Unsplash 25K][unsplash-25k-origin] | - | 25 K | [HF][unsplash-25k-hf] |
550
+ | [Unsplash 2M][unsplash-2m-origin] | - | 2 M | [HF][unsplash-2m-hf] |
551
+ | [LAION 400M][laion-400m-origin] | - | 400 M | [HF][laion-400m-hf] |
552
+ | [LAION 5B][laion-5b-origin] | - | 5 B | [HF][laion-5b-hf] |
553
+
554
+
555
+ [unsplash-25k-origin]: https://unum.cloud
556
+ [unsplash-2m-origin]: https://unum.cloud
557
+ [laion-400m-origin]: https://unum.cloud
558
+ [laion-5b-origin]: https://unum.cloud
559
+ [unsplash-25k-hf]: https://unum.cloud
560
+ [unsplash-2m-hf]: https://unum.cloud
561
+ [laion-400m-hf]: https://unum.cloud
562
+ [laion-5b-hf]: https://unum.cloud
563
+
541
564
  ### USearch + RDKit = Molecular Search
542
565
 
543
- Research in natural sciences is expensive.
544
- To search for properties of different molecules, large databases are used.
545
- Comparing the molecules, however, is hard.
546
- Instead, binary hash-like fingerprints are produced from the graph structure, using packages like RDKit.
566
+ Comparing molecule graphs and searching for similar structures is expensive and slow.
567
+ It can be seen as a special case of the NP-Complete Subgraph Isomorphism problem.
568
+ Luckily, domain-specific approximate methods exists.
569
+ The one commonly used in Chemistry, is to generate structures from [SMILES][smiles], and later hash them into binary fingerprints.
570
+ The later are searchable with bitwise similarity metrics, like the Tanimoto coefficient.
571
+ Below is na example using the RDKit package.
547
572
 
548
573
  ```python
549
574
  from usearch.index import Index, MetricKind
@@ -565,10 +590,58 @@ index.add(labels, fingerprints)
565
590
  matches = index.search(fingerprints, 10)
566
591
  ```
567
592
 
568
- RDKit [provides][rdkit-fingerprints] Atom-Pair, Topological Torsion, Morgan, and Layered Fingerprints, among other options.
593
+ RDKit [provides][rdkit-fingerprints] following fingerprinting techniques:
594
+
595
+ - Atom-Pair,
596
+ - Topological Torsion,
597
+ - Morgan,
598
+ - Layered Fingerprints.
599
+
600
+ We have preprocessed some of the most commonly used datasets, and made it available for free on the HuggingFace portal, together with visual interface.
601
+
602
+ | Dataset | Size | Molecules | Preprocessed |
603
+ | :------------------------ | -------: | ----------: | ---------------: |
604
+ | [PubChem][pubchem-origin] | 8 GB | 115'034'339 | [HF][pubchem-hf] |
605
+ | [GDB 13][gdb13-origin] | 2.3 GB | 977'468'301 | [HF][gdb13-hf] |
606
+ | [REAL][real-origin] | > 100 GB | 6 B | [HF][real-hf] |
569
607
 
608
+ [smiles]: https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system
570
609
  [rdkit-fingerprints]: https://www.rdkit.org/docs/RDKit_Book.html#additional-information-about-the-fingerprints
571
610
 
611
+ [gdb13-origin]: https://zenodo.org/record/5172018/files/gdb13.tgz?download=1
612
+ [pubchem-origin]: ftp://ftp.ncbi.nlm.nih.gov/pubchem/Compound/Extras/CID-SMILES.gz
613
+ [real-origin]: https://enamine.net/compound-collections/real-compounds/real-database
614
+ [pubchem-hf]: https://unum.cloud
615
+ [gdb13-hf]: https://unum.cloud
616
+ [real-hf]: https://unum.cloud
617
+
618
+ ## TODO
619
+
620
+ - JavaScript: Allow calling from "worker threads".
621
+ - Rust: Allow passing a custom thread ID.
622
+ - C# .NET bindings.
623
+
624
+ ## Integrations
625
+
626
+ - [x] GPT-Cache.
627
+ - [ ] Langchain.
628
+ - [ ] Microsoft Semantic Kernel.
629
+ - [ ] PyTorch.
630
+
631
+ ## Citations
632
+
633
+ ```txt
634
+ @software{Vardanian_USearch_2022,
635
+ doi = {10.5281/zenodo.7949416},
636
+ author = {Vardanian, Ash},
637
+ title = {{USearch by Unum Cloud}},
638
+ url = {https://github.com/unum-cloud/usearch},
639
+ version = {0.13.0},
640
+ year = {2022}
641
+ month = jun,
642
+ }
643
+ ```
644
+
572
645
  ---
573
646
 
574
647
  Check [that](https://github.com/ashvardanian/image-search) and [other](https://github.com/unum-cloud/examples) examples on our corporate GitHub 🤗
package/binding.gyp CHANGED
@@ -8,7 +8,6 @@
8
8
  "include_dirs": [
9
9
  "<!@(node -p \"require('node-addon-api').include\")",
10
10
  "include",
11
- "src",
12
11
  "fp16/include",
13
12
  "robin-map/include",
14
13
  "simsimd/include"
package/build.gradle CHANGED
@@ -45,7 +45,7 @@ model {
45
45
  include "**/*.cpp"
46
46
  }
47
47
  exportedHeaders {
48
- srcDirs "include", "src", "fp16/include", "robin-map/include", "simsimd/include", "${Jvm.current().javaHome}/include"
48
+ srcDirs "include", "fp16/include", "robin-map/include", "simsimd/include", "${Jvm.current().javaHome}/include"
49
49
  }
50
50
  }
51
51
  }
package/build.rs CHANGED
@@ -5,7 +5,6 @@ fn main() {
5
5
  .flag_if_supported("-Wno-unknown-pragmas")
6
6
  .include("include")
7
7
  .include("rust")
8
- .include("src")
9
8
  .include("fp16/include")
10
9
  .include("robin-map/include")
11
10
  .include("simsimd/include")
@@ -14,6 +13,7 @@ fn main() {
14
13
  println!("cargo:rerun-if-changed=rust/lib.rs");
15
14
  println!("cargo:rerun-if-changed=rust/lib.cpp");
16
15
  println!("cargo:rerun-if-changed=rust/lib.hpp");
17
- println!("cargo:rerun-if-changed=src/punned.hpp");
18
- println!("cargo:rerun-if-changed=include/usearch/usearch.hpp");
16
+ println!("cargo:rerun-if-changed=include/index_punned_helpers.hpp");
17
+ println!("cargo:rerun-if-changed=include/index_punned_dense.hpp");
18
+ println!("cargo:rerun-if-changed=include/usearch/index.hpp");
19
19
  }
package/c/lib.cpp CHANGED
@@ -1,4 +1,4 @@
1
- #include "../src/punned.hpp"
1
+ #include "../src/index_punned_dense.hpp"
2
2
 
3
3
  extern "C" {
4
4
  #include "usearch.h"
@@ -9,13 +9,36 @@ using namespace unum;
9
9
 
10
10
  using label_t = int;
11
11
  using distance_t = float;
12
- using punned_index_t = punned_gt<label_t>;
12
+ using punned_index_t = index_punned_dense_gt<label_t>;
13
13
  using span_t = span_gt<float>;
14
14
 
15
- void* usearch_new( //
16
- usearch_metric_t metric, usearch_accuracy_t accuracy, //
17
- int dimensions, int capacity, int connectivity, //
18
- int expansion_add, int expansion_search) {
15
+ metric_kind_t to_native_metric(usearch_metric_kind_t kind) {
16
+ switch (kind) {
17
+ case usearch_metric_ip_k: return metric_kind_t::ip_k;
18
+ case usearch_metric_l2sq_k: return metric_kind_t::l2sq_k;
19
+ case usearch_metric_cos_k: return metric_kind_t::cos_k;
20
+ case usearch_metric_haversine_k: return metric_kind_t::haversine_k;
21
+ case usearch_metric_pearson_k: return metric_kind_t::pearson_k;
22
+ case usearch_metric_jaccard_k: return metric_kind_t::jaccard_k;
23
+ case usearch_metric_hamming_k: return metric_kind_t::hamming_k;
24
+ case usearch_metric_tanimoto_k: return metric_kind_t::tanimoto_k;
25
+ case usearch_metric_sorensen_k: return metric_kind_t::sorensen_k;
26
+ default: return metric_kind_t::unknown_k;
27
+ }
28
+ }
29
+
30
+ scalar_kind_t to_native_scalar(usearch_scalar_kind_t kind) {
31
+ switch (kind) {
32
+ case usearch_scalar_f32_k: return scalar_kind_t::f32_k;
33
+ case usearch_scalar_f64_k: return scalar_kind_t::f64_k;
34
+ case usearch_scalar_f16_k: return scalar_kind_t::f16_k;
35
+ case usearch_scalar_f8_k: return scalar_kind_t::f8_k;
36
+ case usearch_scalar_b1_k: return scalar_kind_t::b1_k;
37
+ default: return scalar_kind_t::unknown_k;
38
+ }
39
+ }
40
+
41
+ void usearch_init(usearch_init_options_t* options, usearch_index_t* index, usearch_error_t* error) {
19
42
 
20
43
  try {
21
44
  config_t config;
@@ -37,8 +60,8 @@ void* usearch_new( //
37
60
  // std::size_t bytes_to_alloc = sizeof(punned_index_t) + bytes_for_error_messages;
38
61
  // safe_punned_index_t* result = (safe_punned_index_t*)std::malloc(bytes_to_alloc);
39
62
 
40
- accuracy_t accuracy = accuracy_from_name(accuracy_str, accuracy_len);
41
- common_metric_kind_t metric_kind = common_metric_from_name(metric_str, metric_len);
63
+ scalar_kind_t accuracy = scalar_kind_from_name(accuracy_str, accuracy_len);
64
+ metric_kind_t metric_kind = metric_from_name(metric_str, metric_len);
42
65
  punned_index_t index = make_punned<punned_index_t>( //
43
66
  metric_kind, static_cast<std::size_t>(dimensions), accuracy, config);
44
67
 
@@ -49,7 +72,7 @@ void* usearch_new( //
49
72
  return NULL;
50
73
  }
51
74
 
52
- void usearch_destroy(punned_index_t* index) { delete index; }
75
+ void usearch_free(punned_index_t* index) { delete index; }
53
76
 
54
77
  char const* usearch_save(punned_index_t* index, char const* path) {
55
78
  try {
package/c/usearch.h CHANGED
@@ -1,43 +1,74 @@
1
1
  #ifndef UNUM_USEARCH_H
2
2
  #define UNUM_USEARCH_H
3
3
 
4
- #include <stdlib.h>
4
+ #include <stdint.h> // `size_t`
5
5
 
6
- typedef enum {
6
+ typedef void* usearch_index_t;
7
+ typedef uint32_t usearch_label_t;
8
+ typedef char const* usearch_error_t;
9
+
10
+ typedef float (*)(void const*, void const*) usearch_metric_t;
11
+
12
+ typedef enum usearch_metric_kind_t {
7
13
  usearch_metric_ip_k = 0,
8
- usearch_metric_l2sq_k = 1,
9
- usearch_metric_cos_k = 2,
10
- usearch_metric_haversine_k = 3
11
- } usearch_metric_t;
12
-
13
- typedef enum {
14
- usearch_accuracy_f32_k = 0,
15
- usearch_accuracy_f16_k = 1,
16
- usearch_accuracy_f8_k = 2,
17
- } usearch_accuracy_t;
18
-
19
- void* usearch_new( //
20
- usearch_metric_t metric, usearch_accuracy_t accuracy, //
21
- int dimensions, int capacity, int connectivity, //
22
- int expansion_add, int expansion_search);
23
-
24
- void usearch_destroy(void* index);
25
-
26
- char const* usearch_save(void* index, char const* path);
27
- char const* usearch_load(void* index, char const* path);
28
- char const* usearch_view(void* index, char const* path);
29
-
30
- int usearch_size(void* index);
31
- int usearch_connectivity(void* index);
32
- int usearch_dimensions(void* index);
33
- int usearch_capacity(void* index);
34
-
35
- char const* usearch_reserve(void* index, int capacity);
36
- char const* usearch_add(void* index, int label, float const* vec);
37
-
38
- char const* usearch_search( //
39
- void* index, //
40
- float const* query, int limit, //
41
- int* labels, float* distances, int* count);
14
+ usearch_metric_l2sq_k,
15
+ usearch_metric_cos_k,
16
+ usearch_metric_haversine_k,
17
+ usearch_metric_pearson_k,
18
+ usearch_metric_jaccard_k,
19
+ usearch_metric_hamming_k,
20
+ usearch_metric_tanimoto_k,
21
+ usearch_metric_sorensen_k,
22
+ usearch_metric_unknown_k,
23
+ } usearch_metric_kind_t;
24
+
25
+ typedef enum usearch_scalar_kind_t {
26
+ usearch_scalar_f32_k = 0,
27
+ usearch_scalar_f64_k,
28
+ usearch_scalar_f16_k,
29
+ usearch_scalar_f8_k,
30
+ usearch_scalar_b1_k,
31
+ usearch_scalar_unknown_k,
32
+ } usearch_scalar_kind_t;
33
+
34
+ typedef struct usearch_init_options_t {
35
+
36
+ usearch_metric_kind_t metric_kind;
37
+ usearch_metric_t metric;
38
+
39
+ usearch_scalar_kind_t quantization;
40
+ size_t dimensions;
41
+ size_t capacity;
42
+ size_t connectivity;
43
+ size_t expansion_add;
44
+ size_t expansion_search;
45
+ } usearch_init_options_t;
46
+
47
+ void usearch_init(usearch_init_options_t*, usearch_index_t*, usearch_error_t*);
48
+ void usearch_free(usearch_index_t, usearch_error_t*);
49
+
50
+ void usearch_save(usearch_index_t, char const* path, usearch_error_t*);
51
+ void usearch_load(usearch_index_t, char const* path, usearch_error_t*);
52
+ void usearch_view(usearch_index_t, char const* path, usearch_error_t*);
53
+
54
+ size_t usearch_size(usearch_index_t, usearch_error_t*);
55
+ size_t usearch_capacity(usearch_index_t, usearch_error_t*);
56
+ size_t usearch_dimensions(usearch_index_t, usearch_error_t*);
57
+ size_t usearch_connectivity(usearch_index_t, usearch_error_t*);
58
+
59
+ void usearch_reserve(usearch_index_t, size_t capacity, usearch_error_t*);
60
+ void usearch_add(usearch_index_t, usearch_label_t, float const* vector, usearch_error_t*);
61
+
62
+ /**
63
+ * @brief Performs k-Approximate Nearest Neighbors Search.
64
+ * @return Number of found matches.
65
+ */
66
+ size_t usearch_search( //
67
+ usearch_index_t, float const* query_vector, size_t results_limit, //
68
+ usearch_label_t* found_labels, float* found_distances, usearch_error_t*);
69
+
70
+ bool usearch_contains(usearch_index_t, usearch_label_t, usearch_error_t*);
71
+ void usearch_get(usearch_index_t, usearch_label_t, float* vector, usearch_error_t*);
72
+ void usearch_remove(usearch_index_t, usearch_label_t, usearch_error_t*);
42
73
 
43
74
  #endif