usearch 2.16.3 → 2.16.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 CHANGED
@@ -544,7 +544,7 @@ doi = {10.5281/zenodo.7949416},
544
544
  author = {Vardanian, Ash},
545
545
  title = {{USearch by Unum Cloud}},
546
546
  url = {https://github.com/unum-cloud/usearch},
547
- version = {2.16.3},
547
+ version = {2.16.4},
548
548
  year = {2023},
549
549
  month = oct,
550
550
  }
@@ -9,7 +9,7 @@
9
9
 
10
10
  #define USEARCH_VERSION_MAJOR 2
11
11
  #define USEARCH_VERSION_MINOR 16
12
- #define USEARCH_VERSION_PATCH 3
12
+ #define USEARCH_VERSION_PATCH 4
13
13
 
14
14
  // Inferring C++ version
15
15
  // https://stackoverflow.com/a/61552074
@@ -1651,7 +1651,7 @@ enum class metric_punned_signature_t {
1651
1651
  /**
1652
1652
  * @brief Type-punned metric class, which unlike STL's `std::function` avoids any memory allocations.
1653
1653
  * It also provides additional APIs to check, if SIMD hardware-acceleration is available.
1654
- * Wraps the `simsimd_metric_punned_t` when available. The auto-vectorized backend otherwise.
1654
+ * Wraps the `simsimd_metric_dense_punned_t` when available. The auto-vectorized backend otherwise.
1655
1655
  */
1656
1656
  class metric_punned_t {
1657
1657
  public:
@@ -1860,9 +1860,10 @@ class metric_punned_t {
1860
1860
  case scalar_kind_t::b1x8_k: datatype = simsimd_datatype_b8_k; break;
1861
1861
  default: break;
1862
1862
  }
1863
- simsimd_metric_punned_t simd_metric = NULL;
1863
+ simsimd_metric_dense_punned_t simd_metric = NULL;
1864
1864
  simsimd_capability_t simd_kind = simsimd_cap_any_k;
1865
- simsimd_find_metric_punned(kind, datatype, simd_caps, allowed, &simd_metric, &simd_kind);
1865
+ simsimd_find_kernel_punned(kind, datatype, simd_caps, allowed, (simsimd_kernel_punned_t*)&simd_metric,
1866
+ &simd_kind);
1866
1867
  if (simd_metric == nullptr)
1867
1868
  return false;
1868
1869
 
@@ -1885,7 +1886,7 @@ class metric_punned_t {
1885
1886
  invoke_simsimd(uptr_t a, uptr_t b) const noexcept {
1886
1887
  simsimd_distance_t result;
1887
1888
  // Here `reinterpret_cast` raises warning and UBSan reports an issue... we know what we are doing!
1888
- auto function_pointer = (simsimd_metric_punned_t)(metric_ptr_);
1889
+ auto function_pointer = (simsimd_metric_dense_punned_t)(metric_ptr_);
1889
1890
  function_pointer(reinterpret_cast<void const*>(a), reinterpret_cast<void const*>(b), metric_third_arg_,
1890
1891
  &result);
1891
1892
  return (result_t)result;
@@ -72,7 +72,7 @@ export declare class BatchMatches {
72
72
  */
73
73
  get(i: number): Matches;
74
74
  }
75
- export interface IndexCongif {
75
+ export interface IndexConfig {
76
76
  dimensions: number;
77
77
  metric: MetricKind;
78
78
  quantization: ScalarKind;
@@ -95,7 +95,7 @@ export declare class Index {
95
95
  * @param {boolean} [multi=false] - Optional, default is false.
96
96
  * @throws Will throw an error if any of the parameters are of incorrect type or invalid value.
97
97
  */
98
- constructor(dimensionsOrConfigs: number | bigint | IndexCongif, metric?: MetricKind, quantization?: ScalarKind, connectivity?: number, expansion_add?: number, expansion_search?: number, multi?: boolean);
98
+ constructor(dimensionsOrConfigs: number | bigint | IndexConfig, metric?: MetricKind, quantization?: ScalarKind, connectivity?: number, expansion_add?: number, expansion_search?: number, multi?: boolean);
99
99
  /**
100
100
  * Add vectors to the index.
101
101
  *
@@ -72,7 +72,7 @@ export declare class BatchMatches {
72
72
  */
73
73
  get(i: number): Matches;
74
74
  }
75
- export interface IndexCongif {
75
+ export interface IndexConfig {
76
76
  dimensions: number;
77
77
  metric: MetricKind;
78
78
  quantization: ScalarKind;
@@ -95,7 +95,7 @@ export declare class Index {
95
95
  * @param {boolean} [multi=false] - Optional, default is false.
96
96
  * @throws Will throw an error if any of the parameters are of incorrect type or invalid value.
97
97
  */
98
- constructor(dimensionsOrConfigs: number | bigint | IndexCongif, metric?: MetricKind, quantization?: ScalarKind, connectivity?: number, expansion_add?: number, expansion_search?: number, multi?: boolean);
98
+ constructor(dimensionsOrConfigs: number | bigint | IndexConfig, metric?: MetricKind, quantization?: ScalarKind, connectivity?: number, expansion_add?: number, expansion_search?: number, multi?: boolean);
99
99
  /**
100
100
  * Add vectors to the index.
101
101
  *
@@ -203,7 +203,7 @@ function normalizeVectors(
203
203
  return flattenedVectors;
204
204
  }
205
205
 
206
- export interface IndexCongif {
206
+ export interface IndexConfig {
207
207
  dimensions: number;
208
208
  metric: MetricKind;
209
209
  quantization: ScalarKind;
@@ -227,7 +227,7 @@ export class Index {
227
227
  * @throws Will throw an error if any of the parameters are of incorrect type or invalid value.
228
228
  */
229
229
  constructor(
230
- dimensionsOrConfigs: number | bigint | IndexCongif,
230
+ dimensionsOrConfigs: number | bigint | IndexConfig,
231
231
  metric: MetricKind = MetricKind.Cos,
232
232
  quantization: ScalarKind = ScalarKind.F32,
233
233
  connectivity: number = 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.16.3",
3
+ "version": "2.16.4",
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",
Binary file
Binary file
@@ -101,7 +101,7 @@ You can also benchmark against other libraries, filter the numeric types, and di
101
101
  $ python scripts/bench_vectors.py --help
102
102
  > usage: bench.py [-h] [--ndim NDIM] [-n COUNT]
103
103
  > [--metric {all,dot,spatial,binary,probability,sparse}]
104
- > [--dtype {all,bits,int8,uint16,uint32,float16,float32,float64,bfloat16,complex32,complex64,complex128}]
104
+ > [--dtype {all,bin8,int8,uint16,uint32,float16,float32,float64,bfloat16,complex32,complex64,complex128}]
105
105
  > [--scipy] [--scikit] [--torch] [--tf] [--jax]
106
106
  >
107
107
  > Benchmark SimSIMD vs. other libraries
@@ -119,7 +119,7 @@ $ python scripts/bench_vectors.py --help
119
119
  > `cdist`.
120
120
  > --metric {all,dot,spatial,binary,probability,sparse}
121
121
  > Distance metric to use, profiles everything by default
122
- > --dtype {all,bits,int8,uint16,uint32,float16,float32,float64,bfloat16,complex32,complex64,complex128}
122
+ > --dtype {all,bin8,int8,uint16,uint32,float16,float32,float64,bfloat16,complex32,complex64,complex128}
123
123
  > Defines numeric types to benchmark, profiles everything by default
124
124
  > --scipy Profile SciPy, must be installed
125
125
  > --scikit Profile scikit-learn, must be installed
@@ -203,6 +203,35 @@ bun test
203
203
  swift build && swift test -v
204
204
  ```
205
205
 
206
+ Running Swift on Linux requires a couple of extra steps, as the Swift compiler is not available in the default repositories.
207
+ Please get the most recent Swift tarball from the [official website](https://www.swift.org/install/).
208
+ At the time of writing, for 64-bit Arm CPU running Ubuntu 22.04, the following commands would work:
209
+
210
+ ```bash
211
+ wget https://download.swift.org/swift-5.9.2-release/ubuntu2204-aarch64/swift-5.9.2-RELEASE/swift-5.9.2-RELEASE-ubuntu22.04-aarch64.tar.gz
212
+ tar xzf swift-5.9.2-RELEASE-ubuntu22.04-aarch64.tar.gz
213
+ sudo mv swift-5.9.2-RELEASE-ubuntu22.04-aarch64 /usr/share/swift
214
+ echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bashrc
215
+ source ~/.bashrc
216
+ ```
217
+
218
+ You can check the available images on [`swift.org/download` page](https://www.swift.org/download/#releases).
219
+ For x86 CPUs, the following commands would work:
220
+
221
+ ```bash
222
+ wget https://download.swift.org/swift-5.9.2-release/ubuntu2204/swift-5.9.2-RELEASE/swift-5.9.2-RELEASE-ubuntu22.04.tar.gz
223
+ tar xzf swift-5.9.2-RELEASE-ubuntu22.04.tar.gz
224
+ sudo mv swift-5.9.2-RELEASE-ubuntu22.04 /usr/share/swift
225
+ echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bashrc
226
+ source ~/.bashrc
227
+ ```
228
+
229
+ Alternatively, on Linux, the official Swift Docker image can be used for builds and tests:
230
+
231
+ ```bash
232
+ sudo docker run --rm -v "$PWD:/workspace" -w /workspace swift:5.9 /bin/bash -cl "swift build -c release --static-swift-stdlib && swift test -c release --enable-test-discovery"
233
+ ```
234
+
206
235
  ## GoLang
207
236
 
208
237
  ```sh
@@ -496,7 +496,7 @@ dependencies = [
496
496
 
497
497
  [[package]]
498
498
  name = "simsimd"
499
- version = "5.9.6"
499
+ version = "6.0.5"
500
500
  dependencies = [
501
501
  "cc",
502
502
  "criterion",
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "simsimd"
3
3
  description = "Portable mixed-precision BLAS-like vector math library for x86 and ARM"
4
- version = "5.9.6"
4
+ version = "6.0.5"
5
5
  edition = "2021"
6
6
  license = "Apache-2.0"
7
7
  authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
package/simsimd/README.md CHANGED
@@ -69,9 +69,9 @@ Implemented distance functions include:
69
69
 
70
70
  Moreover, SimSIMD...
71
71
 
72
- - handles `f64`, `f32`, `f16`, and `bf16` real & complex vectors.
73
- - handles `i8` integral, `i4` sub-byte, and `b8` binary vectors.
74
- - handles sparse `u32` and `u16` sets, and weighted sparse vectors.
72
+ - handles `float64`, `float32`, `float16`, and `bfloat16` real & complex vectors.
73
+ - handles `int8` integral, `int4` sub-byte, and `b8` binary vectors.
74
+ - handles sparse `uint32` and `uint16` sets, and weighted sparse vectors.
75
75
  - is a zero-dependency [header-only C 99](#using-simsimd-in-c) library.
76
76
  - has [Python](#using-simsimd-in-python), [Rust](#using-simsimd-in-rust), [JS](#using-simsimd-in-javascript), and [Swift](#using-simsimd-in-swift) bindings.
77
77
  - has Arm backends for NEON, Scalable Vector Extensions (SVE), and SVE2.
@@ -95,14 +95,14 @@ You can learn more about the technical implementation details in the following b
95
95
  For reference, we use 1536-dimensional vectors, like the embeddings produced by the OpenAI Ada API.
96
96
  Comparing the serial code throughput produced by GCC 12 to hand-optimized kernels in SimSIMD, we see the following single-core improvements for the two most common vector-vector similarity metrics - the Cosine similarity and the Euclidean distance:
97
97
 
98
- | Type | Apple M2 Pro | Intel Sapphire Rapids | AWS Graviton 4 |
99
- | :----- | ----------------------------: | -------------------------------: | ------------------------------: |
100
- | `f64` | 18.5 → 28.8 GB/s <br/> + 56 % | 21.9 → 41.4 GB/s <br/> + 89 % | 20.7 → 41.3 GB/s <br/> + 99 % |
101
- | `f32` | 9.2 → 29.6 GB/s <br/> + 221 % | 10.9 → 95.8 GB/s <br/> + 779 % | 4.9 → 41.9 GB/s <br/> + 755 % |
102
- | `f16` | 4.6 → 14.6 GB/s <br/> + 217 % | 3.1 → 108.4 GB/s <br/> + 3,397 % | 5.4 → 39.3 GB/s <br/> + 627 % |
103
- | `bf16` | 4.6 → 26.3 GB/s <br/> + 472 % | 0.8 → 59.5 GB/s <br/> +7,437 % | 2.5 → 29.9 GB/s <br/> + 1,096 % |
104
- | `i8` | 25.8 → 47.1 GB/s <br/> + 83 % | 33.1 → 65.3 GB/s <br/> + 97 % | 35.2 → 43.5 GB/s <br/> + 24 % |
105
- | `u8` | | 32.5 → 66.5 GB/s <br/> + 105 % | |
98
+ | Type | Apple M2 Pro | Intel Sapphire Rapids | AWS Graviton 4 |
99
+ | :--------- | ----------------------------: | -------------------------------: | ------------------------------: |
100
+ | `float64` | 18.5 → 28.8 GB/s <br/> + 56 % | 21.9 → 41.4 GB/s <br/> + 89 % | 20.7 → 41.3 GB/s <br/> + 99 % |
101
+ | `float32` | 9.2 → 29.6 GB/s <br/> + 221 % | 10.9 → 95.8 GB/s <br/> + 779 % | 4.9 → 41.9 GB/s <br/> + 755 % |
102
+ | `float16` | 4.6 → 14.6 GB/s <br/> + 217 % | 3.1 → 108.4 GB/s <br/> + 3,397 % | 5.4 → 39.3 GB/s <br/> + 627 % |
103
+ | `bfloat16` | 4.6 → 26.3 GB/s <br/> + 472 % | 0.8 → 59.5 GB/s <br/> +7,437 % | 2.5 → 29.9 GB/s <br/> + 1,096 % |
104
+ | `int8` | 25.8 → 47.1 GB/s <br/> + 83 % | 33.1 → 65.3 GB/s <br/> + 97 % | 35.2 → 43.5 GB/s <br/> + 24 % |
105
+ | `uint8` | | 32.5 → 66.5 GB/s <br/> + 105 % | |
106
106
 
107
107
  Similar speedups are often observed even when compared to BLAS and LAPACK libraries underlying most numerical computing libraries, including NumPy and SciPy in Python.
108
108
  Broader benchmarking results:
@@ -115,8 +115,8 @@ Broader benchmarking results:
115
115
 
116
116
  The package is intended to replace the usage of `numpy.inner`, `numpy.dot`, and `scipy.spatial.distance`.
117
117
  Aside from drastic performance improvements, SimSIMD significantly improves accuracy in mixed precision setups.
118
- NumPy and SciPy, processing `i8`, `u8` or `f16` vectors, will use the same types for accumulators, while SimSIMD can combine `i8` enumeration, `i16` multiplication, and `i32` accumulation to avoid overflows entirely.
119
- The same applies to processing `f16` and `bf16` values with `f32` precision.
118
+ NumPy and SciPy, processing `int8`, `uint8` or `float16` vectors, will use the same types for accumulators, while SimSIMD can combine `int8` enumeration, `int16` multiplication, and `int32` accumulation to avoid overflows entirely.
119
+ The same applies to processing `float16` and `bfloat16` values with `float32` precision.
120
120
 
121
121
  ### Installation
122
122
 
@@ -155,14 +155,33 @@ dist = simsimd.vdot(vec1.astype(np.complex64), vec2.astype(np.complex64)) # conj
155
155
  ```
156
156
 
157
157
  Unlike SciPy, SimSIMD allows explicitly stating the precision of the input vectors, which is especially useful for mixed-precision setups.
158
+ The `dtype` argument can be passed both by name and as a positional argument:
158
159
 
159
160
  ```py
160
- dist = simsimd.cosine(vec1, vec2, "i8")
161
- dist = simsimd.cosine(vec1, vec2, "f16")
162
- dist = simsimd.cosine(vec1, vec2, "f32")
163
- dist = simsimd.cosine(vec1, vec2, "f64")
164
- dist = simsimd.hamming(vec1, vec2, "bits")
165
- dist = simsimd.jaccard(vec1, vec2, "bits")
161
+ dist = simsimd.cosine(vec1, vec2, "int8")
162
+ dist = simsimd.cosine(vec1, vec2, "float16")
163
+ dist = simsimd.cosine(vec1, vec2, "float32")
164
+ dist = simsimd.cosine(vec1, vec2, "float64")
165
+ dist = simsimd.hamming(vec1, vec2, "bit8")
166
+ ```
167
+
168
+ With other frameworks, like PyTorch, one can get a richer type-system than NumPy, but the lack of good CPython interoperability makes it hard to pass data without copies.
169
+
170
+ ```py
171
+ import numpy as np
172
+ buf1 = np.empty(8, dtype=np.uint16)
173
+ buf2 = np.empty(8, dtype=np.uint16)
174
+
175
+ # View the same memory region with PyTorch and randomize it
176
+ import torch
177
+ vec1 = torch.asarray(memoryview(buf1), copy=False).view(torch.bfloat16)
178
+ vec2 = torch.asarray(memoryview(buf2), copy=False).view(torch.bfloat16)
179
+ torch.randn(8, out=vec1)
180
+ torch.randn(8, out=vec2)
181
+
182
+ # Both libs will look into the same memory buffers and report the same results
183
+ dist_slow = 1 - torch.nn.functional.cosine_similarity(vec1, vec2, dim=0)
184
+ dist_fast = simsimd.cosine(buf1, buf2, "bf16")
166
185
  ```
167
186
 
168
187
  It also allows using SimSIMD for half-precision complex numbers, which NumPy does not support.
@@ -183,10 +202,10 @@ import numpy as np
183
202
  import simsimd as simd
184
203
 
185
204
  length1, length2 = randint(1, 100), randint(1, 100)
186
- vec1 = np.random.randint(0, 1000, length1).astype(np.uint16)
187
- vec2 = np.random.randint(0, 1000, length2).astype(np.uint16)
205
+ vec1 = np.sort(np.random.randint(0, 1000, length1).astype(np.uint16))
206
+ vec2 = np.sort(np.random.randint(0, 1000, length2).astype(np.uint16))
188
207
 
189
- slow_result = np.intersect1d(vec1, vec2)
208
+ slow_result = len(np.intersect1d(vec1, vec2))
190
209
  fast_result = simd.intersect(vec1, vec2)
191
210
  assert slow_result == fast_result
192
211
  ```
@@ -235,6 +254,48 @@ distances: DistancesTensor = simsimd.cdist(matrix1, matrix2, metric="cosine")
235
254
  distances_array: np.ndarray = np.array(distances, copy=True) # now managed by NumPy
236
255
  ```
237
256
 
257
+ ### Elementwise Kernels
258
+
259
+ SimSIMD also provides mixed-precision elementwise kernels, where the input vectors and the output have the same numeric type, but the intermediate accumulators are of a higher precision.
260
+
261
+ ```py
262
+ import numpy as np
263
+ from simsimd import fma, wsum
264
+
265
+ # Let's take two FullHD video frames
266
+ first_frame = np.random.randn(1920 * 1024).astype(np.uint8)
267
+ second_frame = np.random.randn(1920 * 1024).astype(np.uint8)
268
+ average_frame = np.empty_like(first_frame)
269
+ wsum(first_frame, second_frame, alpha=0.5, beta=0.5, out=average_frame)
270
+
271
+ # Slow analog with NumPy:
272
+ slow_average_frame = (0.5 * first_frame + 0.5 * second_frame).astype(np.uint8)
273
+ ```
274
+
275
+ Similarly, the `fma` takes three arguments and computes the fused multiply-add operation.
276
+ In applications like Machine Learning you may also benefit from using the "brain-float" format not natively supported by NumPy.
277
+ In 3D Graphics, for example, we can use FMA to compute the [Phong shading model](https://en.wikipedia.org/wiki/Phong_shading):
278
+
279
+ ```py
280
+ # Assume a FullHD frame with random values for simplicity
281
+ light_intensity = np.random.rand(1920 * 1080).astype(np.float16) # Intensity of light on each pixel
282
+ diffuse_component = np.random.rand(1920 * 1080).astype(np.float16) # Diffuse reflectance on the surface
283
+ specular_component = np.random.rand(1920 * 1080).astype(np.float16) # Specular reflectance for highlights
284
+ output_color = np.empty_like(light_intensity) # Array to store the resulting color intensity
285
+
286
+ # Define the scaling factors for diffuse and specular contributions
287
+ alpha = 0.7 # Weight for the diffuse component
288
+ beta = 0.3 # Weight for the specular component
289
+
290
+ # Formula: color = alpha * light_intensity * diffuse_component + beta * specular_component
291
+ fma(light_intensity, diffuse_component, specular_component,
292
+ dtype="float16", # Optional, unless it can't be inferred from the input
293
+ alpha=alpha, beta=beta, out=output_color)
294
+
295
+ # Slow analog with NumPy for comparison
296
+ slow_output_color = (alpha * light_intensity * diffuse_component + beta * specular_component).astype(np.float16)
297
+ ```
298
+
238
299
  ### Multithreading and Memory Usage
239
300
 
240
301
  By default, computations use a single CPU core.
@@ -248,15 +309,15 @@ matrix1 = np.packbits(np.random.randint(2, size=(10_000, ndim)).astype(np.uint8)
248
309
  matrix2 = np.packbits(np.random.randint(2, size=(1_000, ndim)).astype(np.uint8))
249
310
 
250
311
  distances = simsimd.cdist(matrix1, matrix2,
251
- metric="hamming", # Unlike SciPy, SimSIMD doesn't divide by the number of dimensions
252
- out_dtype="u8", # so we can use `u8` instead of `f64` to save memory.
253
- threads=0, # Use all CPU cores with OpenMP.
254
- dtype="b8", # Override input argument type to `b8` eight-bit words.
312
+ metric="hamming", # Unlike SciPy, SimSIMD doesn't divide by the number of dimensions
313
+ out_dtype="uint8", # so we can use `uint8` instead of `float64` to save memory.
314
+ threads=0, # Use all CPU cores with OpenMP.
315
+ dtype="bin8", # Override input argument type to `bin8` eight-bit words.
255
316
  )
256
317
  ```
257
318
 
258
- By default, the output distances will be stored in double-precision `f64` floating-point numbers.
259
- That behavior may not be space-efficient, especially if you are computing the hamming distance between short binary vectors, that will generally fit into 8x smaller `u8` or `u16` types.
319
+ By default, the output distances will be stored in double-precision `float64` floating-point numbers.
320
+ That behavior may not be space-efficient, especially if you are computing the hamming distance between short binary vectors, that will generally fit into 8x smaller `uint8` or `uint16` types.
260
321
  To override this behavior, use the `dtype` argument.
261
322
 
262
323
  ### Helper Functions
@@ -575,7 +636,7 @@ Simplest of all, you can include the headers, and the compiler will automaticall
575
636
  int main() {
576
637
  simsimd_f32_t vector_a[1536];
577
638
  simsimd_f32_t vector_b[1536];
578
- simsimd_metric_punned_t distance_function = simsimd_metric_punned(
639
+ simsimd_kernel_punned_t distance_function = simsimd_metric_punned(
579
640
  simsimd_metric_cos_k, // Metric kind, like the angular cosine distance
580
641
  simsimd_datatype_f32_k, // Data type, like: f16, f32, f64, i8, b8, and complex variants
581
642
  simsimd_cap_any_k); // Which CPU capabilities are we allowed to use
@@ -663,7 +724,6 @@ int main() {
663
724
  simsimd_vdot_f16c(f16s, f16s, 1536, &distance);
664
725
  simsimd_vdot_f32c(f32s, f32s, 1536, &distance);
665
726
  simsimd_vdot_f64c(f64s, f64s, 1536, &distance);
666
-
667
727
  return 0;
668
728
  }
669
729
  ```
@@ -676,13 +736,8 @@ int main() {
676
736
  int main() {
677
737
  simsimd_b8_t b8s[1536 / 8]; // 8 bits per word
678
738
  simsimd_distance_t distance;
679
-
680
- // Hamming distance between two vectors
681
739
  simsimd_hamming_b8(b8s, b8s, 1536 / 8, &distance);
682
-
683
- // Jaccard distance between two vectors
684
740
  simsimd_jaccard_b8(b8s, b8s, 1536 / 8, &distance);
685
-
686
741
  return 0;
687
742
  }
688
743
  ```
@@ -707,7 +762,6 @@ int main() {
707
762
  simsimd_kl_f16(f16s, f16s, 1536, &distance);
708
763
  simsimd_kl_f32(f32s, f32s, 1536, &distance);
709
764
  simsimd_kl_f64(f64s, f64s, 1536, &distance);
710
-
711
765
  return 0;
712
766
  }
713
767
  ```
@@ -949,10 +1003,10 @@ In NumPy terms, the implementation may look like:
949
1003
 
950
1004
  ```py
951
1005
  import numpy as np
952
- def wsum(A: np.ndarray, B: np.ndarray, Alpha: float, Beta: float) -> np.ndarray:
1006
+ def wsum(A: np.ndarray, B: np.ndarray, /, Alpha: float, Beta: float) -> np.ndarray:
953
1007
  assert A.dtype == B.dtype, "Input types must match and affect the output style"
954
1008
  return (Alpha * A + Beta * B).astype(A.dtype)
955
- def fma(A: np.ndarray, B: np.ndarray, C: np.ndarray, Alpha: float, Beta: float) -> np.ndarray:
1009
+ def fma(A: np.ndarray, B: np.ndarray, C: np.ndarray, /, Alpha: float, Beta: float) -> np.ndarray:
956
1010
  assert A.dtype == B.dtype and A.dtype == C.dtype, "Input types must match and affect the output style"
957
1011
  return (Alpha * A * B + Beta * C).astype(A.dtype)
958
1012
  ```
@@ -1095,7 +1149,7 @@ All of the function names follow the same pattern: `simsimd_{function}_{type}_{b
1095
1149
  - The type can be `f64`, `f32`, `f16`, `bf16`, `f64c`, `f32c`, `f16c`, `bf16c`, `i8`, or `b8`.
1096
1150
  - The function can be `dot`, `vdot`, `cos`, `l2sq`, `hamming`, `jaccard`, `kl`, `js`, or `intersect`.
1097
1151
 
1098
- To avoid hard-coding the backend, you can use the `simsimd_metric_punned_t` to pun the function pointer and the `simsimd_capabilities` function to get the available backends at runtime.
1152
+ To avoid hard-coding the backend, you can use the `simsimd_kernel_punned_t` to pun the function pointer and the `simsimd_capabilities` function to get the available backends at runtime.
1099
1153
  To match all the function names, consider a RegEx:
1100
1154
 
1101
1155
  ```regex
package/simsimd/c/lib.c CHANGED
@@ -7,6 +7,12 @@
7
7
  #define SIMSIMD_NATIVE_F16 0
8
8
  #define SIMSIMD_NATIVE_BF16 0
9
9
 
10
+ /* Override the primary serial operations to avoid the LibC dependency.
11
+ */
12
+ #define SIMSIMD_SQRT(x) simsimd_approximate_square_root(x)
13
+ #define SIMSIMD_RSQRT(x) simsimd_approximate_inverse_square_root(x)
14
+ #define SIMSIMD_LOG(x) simsimd_approximate_log(x)
15
+
10
16
  /* Depending on the Operating System, the following intrinsics are available
11
17
  * on recent compiler toolchains:
12
18
  *
@@ -55,20 +61,21 @@ extern "C" {
55
61
  // If no metric is found, it returns NaN. We can obtain NaN by dividing 0.0 by 0.0, but that annoys
56
62
  // the MSVC compiler. Instead we can directly write-in the signaling NaN (0x7FF0000000000001)
57
63
  // or the qNaN (0x7FF8000000000000).
58
- #define SIMSIMD_DECLARATION_DENSE(name, extension, type) \
59
- SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##type##_t const *a, simsimd_##type##_t const *b, \
60
- simsimd_size_t n, simsimd_distance_t *results) { \
61
- static simsimd_metric_punned_t metric = 0; \
62
- if (metric == 0) { \
63
- simsimd_capability_t used_capability; \
64
- simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
65
- simsimd_capabilities(), simsimd_cap_any_k, &metric, &used_capability); \
66
- if (!metric) { \
67
- *(simsimd_u64_t *)results = 0x7FF0000000000001ull; \
68
- return; \
69
- } \
70
- } \
71
- metric(a, b, n, results); \
64
+ #define SIMSIMD_DECLARATION_DENSE(name, extension, type) \
65
+ SIMSIMD_DYNAMIC void simsimd_##name##_##extension(simsimd_##type##_t const *a, simsimd_##type##_t const *b, \
66
+ simsimd_size_t n, simsimd_distance_t *results) { \
67
+ static simsimd_metric_dense_punned_t metric = 0; \
68
+ if (metric == 0) { \
69
+ simsimd_capability_t used_capability; \
70
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
71
+ simsimd_capabilities(), simsimd_cap_any_k, (simsimd_kernel_punned_t *)&metric, \
72
+ &used_capability); \
73
+ if (!metric) { \
74
+ *(simsimd_u64_t *)results = 0x7FF0000000000001ull; \
75
+ return; \
76
+ } \
77
+ } \
78
+ metric(a, b, n, results); \
72
79
  }
73
80
 
74
81
  #define SIMSIMD_DECLARATION_SPARSE(name, extension, type) \
@@ -78,9 +85,9 @@ extern "C" {
78
85
  static simsimd_metric_sparse_punned_t metric = 0; \
79
86
  if (metric == 0) { \
80
87
  simsimd_capability_t used_capability; \
81
- simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
88
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
82
89
  simsimd_capabilities(), simsimd_cap_any_k, \
83
- (simsimd_metric_punned_t *)(&metric), &used_capability); \
90
+ (simsimd_kernel_punned_t *)(&metric), &used_capability); \
84
91
  if (!metric) { \
85
92
  *(simsimd_u64_t *)result = 0x7FF0000000000001ull; \
86
93
  return; \
@@ -96,9 +103,9 @@ extern "C" {
96
103
  static simsimd_metric_curved_punned_t metric = 0; \
97
104
  if (metric == 0) { \
98
105
  simsimd_capability_t used_capability; \
99
- simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
106
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
100
107
  simsimd_capabilities(), simsimd_cap_any_k, \
101
- (simsimd_metric_punned_t *)(&metric), &used_capability); \
108
+ (simsimd_kernel_punned_t *)(&metric), &used_capability); \
102
109
  if (!metric) { \
103
110
  *(simsimd_u64_t *)result = 0x7FF0000000000001ull; \
104
111
  return; \
@@ -114,9 +121,9 @@ extern "C" {
114
121
  static simsimd_kernel_fma_punned_t metric = 0; \
115
122
  if (metric == 0) { \
116
123
  simsimd_capability_t used_capability; \
117
- simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
124
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
118
125
  simsimd_capabilities(), simsimd_cap_any_k, \
119
- (simsimd_metric_punned_t *)(&metric), &used_capability); \
126
+ (simsimd_kernel_punned_t *)(&metric), &used_capability); \
120
127
  } \
121
128
  metric(a, b, c, n, alpha, beta, result); \
122
129
  }
@@ -128,9 +135,9 @@ extern "C" {
128
135
  static simsimd_kernel_wsum_punned_t metric = 0; \
129
136
  if (metric == 0) { \
130
137
  simsimd_capability_t used_capability; \
131
- simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
138
+ simsimd_find_kernel_punned(simsimd_metric_##name##_k, simsimd_datatype_##extension##_k, \
132
139
  simsimd_capabilities(), simsimd_cap_any_k, \
133
- (simsimd_metric_punned_t *)(&metric), &used_capability); \
140
+ (simsimd_kernel_punned_t *)(&metric), &used_capability); \
134
141
  } \
135
142
  metric(a, b, n, alpha, beta, result); \
136
143
  }
@@ -326,14 +333,14 @@ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void) {
326
333
  return static_capabilities;
327
334
  }
328
335
 
329
- SIMSIMD_DYNAMIC void simsimd_find_metric_punned( //
336
+ SIMSIMD_DYNAMIC void simsimd_find_kernel_punned( //
330
337
  simsimd_metric_kind_t kind, //
331
338
  simsimd_datatype_t datatype, //
332
339
  simsimd_capability_t supported, //
333
340
  simsimd_capability_t allowed, //
334
- simsimd_metric_punned_t *metric_output, //
341
+ simsimd_kernel_punned_t *kernel_output, //
335
342
  simsimd_capability_t *capability_output) {
336
- _simsimd_find_metric_punned_implementation(kind, datatype, supported, allowed, metric_output, capability_output);
343
+ _simsimd_find_kernel_punned_implementation(kind, datatype, supported, allowed, kernel_output, capability_output);
337
344
  }
338
345
 
339
346
  #ifdef __cplusplus