usearch 2.20.4 → 2.20.5

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.
@@ -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 = "6.4.10"
4
+ version = "6.5.1"
5
5
  edition = "2021"
6
6
  license = "Apache-2.0"
7
7
  authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
@@ -43,7 +43,11 @@ lto = true # Enables Link Time Optimization for further optimizations
43
43
  codegen-units = 1 # May increase compilation time but optimizes further
44
44
  rpath = false # On some systems, setting this to false can help with optimizations
45
45
 
46
+ [features]
47
+ default = []
48
+ std = []
49
+
46
50
  [dev-dependencies]
47
- criterion = { version = "0.5.1" }
48
- rand = { version = "0.8.5" }
49
- half = { version = "2.4.0" }
51
+ criterion = { version = "0.6.0" }
52
+ rand = { version = "0.9.1" }
53
+ half = { version = "2.6.0" }
package/simsimd/README.md CHANGED
@@ -454,7 +454,7 @@ import numpy as np
454
454
  from simsimd import fma, wsum
455
455
 
456
456
  # Let's take two FullHD video frames
457
- first_frame = np.random.randn(1920 * 1024).astype(np.uint8)
457
+ first_frame = np.random.randn(1920 * 1024).astype(np.uint8)
458
458
  second_frame = np.random.randn(1920 * 1024).astype(np.uint8)
459
459
  average_frame = np.empty_like(first_frame)
460
460
  wsum(first_frame, second_frame, alpha=0.5, beta=0.5, out=average_frame)
@@ -479,7 +479,7 @@ alpha = 0.7 # Weight for the diffuse component
479
479
  beta = 0.3 # Weight for the specular component
480
480
 
481
481
  # Formula: color = alpha * light_intensity * diffuse_component + beta * specular_component
482
- fma(light_intensity, diffuse_component, specular_component,
482
+ fma(light_intensity, diffuse_component, specular_component,
483
483
  dtype="float16", # Optional, unless it can't be inferred from the input
484
484
  alpha=alpha, beta=beta, out=output_color)
485
485
 
@@ -499,7 +499,7 @@ ndim = 1536 # OpenAI Ada embeddings
499
499
  matrix1 = np.packbits(np.random.randint(2, size=(10_000, ndim)).astype(np.uint8))
500
500
  matrix2 = np.packbits(np.random.randint(2, size=(1_000, ndim)).astype(np.uint8))
501
501
 
502
- distances = simsimd.cdist(matrix1, matrix2,
502
+ distances = simsimd.cdist(matrix1, matrix2,
503
503
  metric="hamming", # Unlike SciPy, SimSIMD doesn't divide by the number of dimensions
504
504
  out_dtype="uint8", # so we can use `uint8` instead of `float64` to save memory.
505
505
  threads=0, # Use all CPU cores with OpenMP.
@@ -541,8 +541,38 @@ with ThreadPoolExecutor(max_workers=num_threads) as executor:
541
541
  futures.append(executor.submit(compute_batch, start_idx, end_idx))
542
542
 
543
543
  # Collect results from all threads
544
- results = [future.result() for future in futures]
545
- ```
544
+ results = [future.result() for future in futures]
545
+ ```
546
+
547
+ ### Half-Precision Brain-Float Numbers
548
+
549
+ The "brain-float-16" is a popular machine learning format.
550
+ It's broadly supported in hardware and is very machine-friendly, but software support is still lagging behind.
551
+ [Unlike NumPy](https://github.com/numpy/numpy/issues/19808), you can already use `bf16` datatype in SimSIMD.
552
+ Luckily, to downcast `f32` to `bf16` you only have to drop the last 16 bits:
553
+
554
+ ```py
555
+ import numpy as np
556
+ import simsimd as simd
557
+
558
+ a = np.random.randn(ndim).astype(np.float32)
559
+ b = np.random.randn(ndim).astype(np.float32)
560
+
561
+ # NumPy doesn't natively support brain-float, so we need a trick!
562
+ # Luckily, it's very easy to reduce the representation accuracy
563
+ # by simply masking the low 16-bits of our 32-bit single-precision
564
+ # numbers. We can also add `0x8000` to round the numbers.
565
+ a_f32rounded = ((a.view(np.uint32) + 0x8000) & 0xFFFF0000).view(np.float32)
566
+ b_f32rounded = ((b.view(np.uint32) + 0x8000) & 0xFFFF0000).view(np.float32)
567
+
568
+ # To represent them as brain-floats, we need to drop the second half
569
+ a_bf16 = np.right_shift(a_f32rounded.view(np.uint32), 16).astype(np.uint16)
570
+ b_bf16 = np.right_shift(b_f32rounded.view(np.uint32), 16).astype(np.uint16)
571
+
572
+ # Now we can compare the results
573
+ expected = np.inner(a_f32rounded, b_f32rounded)
574
+ result = simd.inner(a_bf16, b_bf16, "bf16")
575
+ ```
546
576
 
547
577
  ### Helper Functions
548
578
 
@@ -644,21 +674,21 @@ Complex inner products are available for `f64`, `f32`, and `f16` types.
644
674
  ### Probability Distributions: Jensen-Shannon and Kullback-Leibler Divergences
645
675
 
646
676
  ```rust
647
- use simsimd::SpatialSimilarity;
677
+ use simsimd::ProbabilitySimilarity;
648
678
 
649
679
  fn main() {
650
680
  let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
651
681
  let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
652
682
 
653
- let cosine_similarity = f32::jensenshannon(&vector_a, &vector_b)
683
+ let jensen_shannon = f32::jensenshannon(&vector_a, &vector_b)
654
684
  .expect("Vectors must be of the same length");
655
685
 
656
- println!("Cosine Similarity: {}", cosine_similarity);
686
+ println!("Jensen-Shannon Divergence: {}", jensen_shannon);
657
687
 
658
- let sq_euclidean_distance = f32::kullbackleibler(&vector_a, &vector_b)
688
+ let kullback_leibler = f32::kullbackleibler(&vector_a, &vector_b)
659
689
  .expect("Vectors must be of the same length");
660
690
 
661
- println!("Squared Euclidean Distance: {}", sq_euclidean_distance);
691
+ println!("Kullback-Leibler Divergence: {}", kullback_leibler);
662
692
  }
663
693
  ```
664
694
 
@@ -693,23 +723,48 @@ Binary similarity functions are available only for `u8` types.
693
723
 
694
724
  ### Half-Precision Floating-Point Numbers
695
725
 
696
- Rust has no native support for half-precision floating-point numbers, but SimSIMD provides a `f16` type.
697
- It has no functionality - it is a `transparent` wrapper around `u16` and can be used with `half` or any other half-precision library.
726
+ Rust has no native support for half-precision floating-point numbers, but SimSIMD provides a `f16` type with built-in conversion methods.
727
+ The underlying `u16` representation is publicly accessible for direct bit manipulation.
698
728
 
699
729
  ```rust
700
- use simsimd::SpatialSimilarity;
701
- use simsimd::f16 as SimF16;
730
+ use simsimd::{SpatialSimilarity, f16};
731
+
732
+ fn main() {
733
+ // Create f16 vectors using built-in conversion methods
734
+ let vector_a: Vec<f16> = vec![1.0, 2.0, 3.0].iter().map(|&x| f16::from_f32(x)).collect();
735
+ let vector_b: Vec<f16> = vec![4.0, 5.0, 6.0].iter().map(|&x| f16::from_f32(x)).collect();
736
+
737
+ // Compute the cosine similarity
738
+ let cosine_similarity = f16::cosine(&vector_a, &vector_b)
739
+ .expect("Vectors must be of the same length");
740
+
741
+ println!("Cosine Similarity: {}", cosine_similarity);
742
+
743
+ // Direct bit manipulation
744
+ let half = f16::from_f32(3.14159);
745
+ let bits = half.0; // Access raw u16 representation
746
+ let reconstructed = f16(bits);
747
+
748
+ // Convert back to f32
749
+ let float_value = half.to_f32();
750
+ }
751
+ ```
752
+
753
+ For interoperability with the `half` crate:
754
+
755
+ ```rust
756
+ use simsimd::{SpatialSimilarity, f16 as SimF16};
702
757
  use half::f16 as HalfF16;
703
758
 
704
759
  fn main() {
705
- let vector_a: Vec<HalfF16> = ...
706
- let vector_b: Vec<HalfF16> = ...
760
+ let vector_a: Vec<HalfF16> = vec![1.0, 2.0, 3.0].iter().map(|&x| HalfF16::from_f32(x)).collect();
761
+ let vector_b: Vec<HalfF16> = vec![4.0, 5.0, 6.0].iter().map(|&x| HalfF16::from_f32(x)).collect();
707
762
 
708
- let buffer_a: &[SimF16] = unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const SimF16, a_half.len()) };
709
- let buffer_b: &[SimF16] = unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const SimF16, b_half.len()) };
763
+ // Safe reinterpret cast due to identical memory layout
764
+ let buffer_a: &[SimF16] = unsafe { std::slice::from_raw_parts(vector_a.as_ptr() as *const SimF16, vector_a.len()) };
765
+ let buffer_b: &[SimF16] = unsafe { std::slice::from_raw_parts(vector_b.as_ptr() as *const SimF16, vector_b.len()) };
710
766
 
711
- // Compute the cosine similarity between vector_a and vector_b
712
- let cosine_similarity = SimF16::cosine(&vector_a, &vector_b)
767
+ let cosine_similarity = SimF16::cosine(buffer_a, buffer_b)
713
768
  .expect("Vectors must be of the same length");
714
769
 
715
770
  println!("Cosine Similarity: {}", cosine_similarity);
@@ -719,31 +774,41 @@ fn main() {
719
774
  ### Half-Precision Brain-Float Numbers
720
775
 
721
776
  The "brain-float-16" is a popular machine learning format.
722
- It's broadly supported in hardware and is very machine-friendly, but software support is still lagging behind.
777
+ It's broadly supported in hardware and is very machine-friendly, but software support is still lagging behind.
723
778
  [Unlike NumPy](https://github.com/numpy/numpy/issues/19808), you can already use `bf16` datatype in SimSIMD.
724
- Luckily, to downcast `f32` to `bf16` you only have to drop the last 16 bits:
779
+ SimSIMD provides a `bf16` type with built-in conversion methods and direct bit access.
725
780
 
726
- ```py
727
- import numpy as np
728
- import simsimd as simd
781
+ ```rust
782
+ use simsimd::{SpatialSimilarity, bf16};
729
783
 
730
- a = np.random.randn(ndim).astype(np.float32)
731
- b = np.random.randn(ndim).astype(np.float32)
784
+ fn main() {
785
+ // Create bf16 vectors using built-in conversion methods
786
+ let vector_a: Vec<bf16> = vec![1.0, 2.0, 3.0].iter().map(|&x| bf16::from_f32(x)).collect();
787
+ let vector_b: Vec<bf16> = vec![4.0, 5.0, 6.0].iter().map(|&x| bf16::from_f32(x)).collect();
732
788
 
733
- # NumPy doesn't natively support brain-float, so we need a trick!
734
- # Luckily, it's very easy to reduce the representation accuracy
735
- # by simply masking the low 16-bits of our 32-bit single-precision
736
- # numbers. We can also add `0x8000` to round the numbers.
737
- a_f32rounded = ((a.view(np.uint32) + 0x8000) & 0xFFFF0000).view(np.float32)
738
- b_f32rounded = ((b.view(np.uint32) + 0x8000) & 0xFFFF0000).view(np.float32)
789
+ // Compute the cosine similarity
790
+ let cosine_similarity = bf16::cosine(&vector_a, &vector_b)
791
+ .expect("Vectors must be of the same length");
792
+
793
+ println!("Cosine Similarity: {}", cosine_similarity);
739
794
 
740
- # To represent them as brain-floats, we need to drop the second half
741
- a_bf16 = np.right_shift(a_f32rounded.view(np.uint32), 16).astype(np.uint16)
742
- b_bf16 = np.right_shift(b_f32rounded.view(np.uint32), 16).astype(np.uint16)
795
+ // Direct bit manipulation
796
+ let brain_half = bf16::from_f32(3.14159);
797
+ let bits = brain_half.0; // Access raw u16 representation
798
+ let reconstructed = bf16(bits);
799
+
800
+ // Convert back to f32
801
+ let float_value = brain_half.to_f32();
743
802
 
744
- # Now we can compare the results
745
- expected = np.inner(a_f32rounded, b_f32rounded)
746
- result = simd.inner(a_bf16, b_bf16, "bf16")
803
+ // Compare precision differences
804
+ let original = 3.14159_f32;
805
+ let f16_roundtrip = f16::from_f32(original).to_f32();
806
+ let bf16_roundtrip = bf16::from_f32(original).to_f32();
807
+
808
+ println!("Original: {}", original);
809
+ println!("f16 roundtrip: {}", f16_roundtrip);
810
+ println!("bf16 roundtrip: {}", bf16_roundtrip);
811
+ }
747
812
  ```
748
813
 
749
814
  ### Dynamic Dispatch in Rust
@@ -760,6 +825,7 @@ println!("uses ice: {}", capabilities::uses_ice());
760
825
  println!("uses genoa: {}", capabilities::uses_genoa());
761
826
  println!("uses sapphire: {}", capabilities::uses_sapphire());
762
827
  println!("uses turin: {}", capabilities::uses_turin());
828
+ println!("uses sierra: {}", capabilities::uses_sierra());
763
829
  ```
764
830
 
765
831
  ## Using SimSIMD in JavaScript
@@ -776,13 +842,13 @@ This will automatically happen unless you install the package with the `--ignore
776
842
  After you install it, you will be able to call the SimSIMD functions on various `TypedArray` variants:
777
843
 
778
844
  ```js
779
- const { sqeuclidean, cosine, inner, hamming, jaccard } = require('simsimd');
845
+ const { sqeuclidean, cosine, inner, hamming, jaccard } = require("simsimd");
780
846
 
781
847
  const vectorA = new Float32Array([1.0, 2.0, 3.0]);
782
848
  const vectorB = new Float32Array([4.0, 5.0, 6.0]);
783
849
 
784
850
  const distance = sqeuclidean(vectorA, vectorB);
785
- console.log('Squared Euclidean Distance:', distance);
851
+ console.log("Squared Euclidean Distance:", distance);
786
852
  ```
787
853
 
788
854
  Other numeric types and precision levels are supported as well.
@@ -798,8 +864,8 @@ When doing machine learning and vector search with high-dimensional vectors you
798
864
  You may want to project values from the $[-1, 1]$ range to the $[-127, 127]$ range and then cast them to `Int8Array`:
799
865
 
800
866
  ```js
801
- const quantizedVectorA = new Int8Array(vectorA.map(v => (v * 127)));
802
- const quantizedVectorB = new Int8Array(vectorB.map(v => (v * 127)));
867
+ const quantizedVectorA = new Int8Array(vectorA.map((v) => v * 127));
868
+ const quantizedVectorB = new Int8Array(vectorB.map((v) => v * 127));
803
869
  const distance = cosine(quantizedVectorA, quantizedVectorB);
804
870
  ```
805
871
 
@@ -808,7 +874,7 @@ You can map all positive values to `1` and all negative values and zero to `0`,
808
874
  After that, Hamming and Jaccard distances can be computed.
809
875
 
810
876
  ```js
811
- const { toBinary, hamming } = require('simsimd');
877
+ const { toBinary, hamming } = require("simsimd");
812
878
 
813
879
  const binaryVectorA = toBinary(vectorA);
814
880
  const binaryVectorB = toBinary(vectorB);
@@ -919,7 +985,7 @@ int main() {
919
985
  simsimd_cos_f32(f32s, f32s, 1536, &distance);
920
986
  simsimd_cos_f64(f64s, f64s, 1536, &distance);
921
987
  simsimd_cos_bf16(bf16s, bf16s, 1536, &distance);
922
-
988
+
923
989
  // Euclidean distance between two vectors
924
990
  simsimd_l2sq_i8(i8s, i8s, 1536, &distance);
925
991
  simsimd_l2sq_u8(u8s, u8s, 1536, &distance);
@@ -1036,7 +1102,7 @@ To explicitly disable half-precision support, define the following macro before
1036
1102
  > This flag does just that and is used to produce the `simsimd.so` shared library, as well as the Python and other bindings.
1037
1103
 
1038
1104
  For Arm: `SIMSIMD_TARGET_NEON`, `SIMSIMD_TARGET_SVE`, `SIMSIMD_TARGET_SVE2`, `SIMSIMD_TARGET_NEON_F16`, `SIMSIMD_TARGET_SVE_F16`, `SIMSIMD_TARGET_NEON_BF16`, `SIMSIMD_TARGET_SVE_BF16`.
1039
- For x86: (`SIMSIMD_TARGET_HASWELL`, `SIMSIMD_TARGET_SKYLAKE`, `SIMSIMD_TARGET_ICE`, `SIMSIMD_TARGET_GENOA`, `SIMSIMD_TARGET_SAPPHIRE`, `SIMSIMD_TARGET_TURIN`, `SIMSIMD_TARGET_SIERRA`.
1105
+ For x86: (`SIMSIMD_TARGET_HASWELL`, `SIMSIMD_TARGET_SKYLAKE`, `SIMSIMD_TARGET_ICE`, `SIMSIMD_TARGET_GENOA`, `SIMSIMD_TARGET_SAPPHIRE`, `SIMSIMD_TARGET_TURIN`, `SIMSIMD_TARGET_SIERRA`.
1040
1106
 
1041
1107
  > By default, SimSIMD automatically infers the target architecture and pre-compiles as many kernels as possible.
1042
1108
  > In some cases, you may want to explicitly disable some of the kernels.
@@ -1064,7 +1130,7 @@ In general there are a few principles that SimSIMD follows:
1064
1130
 
1065
1131
  Possibly, in the future:
1066
1132
 
1067
- - Best effort computation silencing `NaN` components in low-precision inputs.
1133
+ - Best effort computation silencing `NaN` components in low-precision inputs.
1068
1134
  - Detect overflows and report the distance with a "signaling" `NaN`.
1069
1135
 
1070
1136
  Last, but not the least - don't build unless there is a demand for it.
@@ -1199,7 +1265,7 @@ SimSIMD defines `dot` and `vdot` kernels as:
1199
1265
 
1200
1266
  Where $\bar{b_i}$ is the complex conjugate of $b_i$.
1201
1267
  Putting that into Python code for scalar arrays:
1202
-
1268
+
1203
1269
  ```python
1204
1270
  def dot(a: List[number], b: List[number]) -> number:
1205
1271
  a_real, a_imaginary = a[0::2], a[1::2]
package/simsimd/c/lib.c CHANGED
@@ -243,6 +243,22 @@ SIMSIMD_DYNAMIC int simsimd_uses_sierra(void) { return (simsimd_capabilities() &
243
243
  SIMSIMD_DYNAMIC int simsimd_uses_dynamic_dispatch(void) { return 1; }
244
244
  SIMSIMD_DYNAMIC int simsimd_flush_denormals(void) { return _simsimd_flush_denormals(); }
245
245
 
246
+ SIMSIMD_DYNAMIC simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr) {
247
+ return simsimd_f16_to_f32_implementation(x_ptr);
248
+ }
249
+
250
+ SIMSIMD_DYNAMIC void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_ptr) {
251
+ simsimd_f32_to_f16_implementation(x, result_ptr);
252
+ }
253
+
254
+ SIMSIMD_DYNAMIC simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr) {
255
+ return simsimd_bf16_to_f32_implementation(x_ptr);
256
+ }
257
+
258
+ SIMSIMD_DYNAMIC void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t *result_ptr) {
259
+ simsimd_f32_to_bf16_implementation(x, result_ptr);
260
+ }
261
+
246
262
  SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void) {
247
263
  //! The latency of the CPUID instruction can be over 100 cycles, so we cache the result.
248
264
  static simsimd_capability_t static_capabilities = simsimd_cap_any_k;
@@ -88,8 +88,8 @@
88
88
  #define SIMSIMD_H
89
89
 
90
90
  #define SIMSIMD_VERSION_MAJOR 6
91
- #define SIMSIMD_VERSION_MINOR 4
92
- #define SIMSIMD_VERSION_PATCH 10
91
+ #define SIMSIMD_VERSION_MINOR 5
92
+ #define SIMSIMD_VERSION_PATCH 1
93
93
 
94
94
  /**
95
95
  * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
@@ -547,7 +547,7 @@ SIMSIMD_INTERNAL simsimd_f32_t simsimd_approximate_log(simsimd_f32_t number) {
547
547
  * https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
548
548
  * https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
549
549
  */
550
- SIMSIMD_INTERNAL simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr) {
550
+ SIMSIMD_INTERNAL simsimd_f32_t simsimd_f16_to_f32_implementation(simsimd_f16_t const *x_ptr) {
551
551
  unsigned short x;
552
552
  SIMSIMD_COPY16(&x, x_ptr);
553
553
  unsigned int exponent = (x & 0x7C00) >> 10;
@@ -570,7 +570,7 @@ SIMSIMD_INTERNAL simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr) {
570
570
  * https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233
571
571
  * https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834
572
572
  */
573
- SIMSIMD_INTERNAL void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_ptr) {
573
+ SIMSIMD_INTERNAL void simsimd_f32_to_f16_implementation(simsimd_f32_t x, simsimd_f16_t *result_ptr) {
574
574
  simsimd_f32i32_t conv;
575
575
  conv.f = x;
576
576
  unsigned int b = conv.i + 0x00001000;
@@ -589,7 +589,7 @@ SIMSIMD_INTERNAL void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_
589
589
  * https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307
590
590
  * https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus
591
591
  */
592
- SIMSIMD_INTERNAL simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr) {
592
+ SIMSIMD_INTERNAL simsimd_f32_t simsimd_bf16_to_f32_implementation(simsimd_bf16_t const *x_ptr) {
593
593
  unsigned short x;
594
594
  SIMSIMD_COPY16(&x, x_ptr);
595
595
  simsimd_f32i32_t conv;
@@ -603,7 +603,7 @@ SIMSIMD_INTERNAL simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr)
603
603
  * https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307
604
604
  * https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus
605
605
  */
606
- SIMSIMD_INTERNAL void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t *result_ptr) {
606
+ SIMSIMD_INTERNAL void simsimd_f32_to_bf16_implementation(simsimd_f32_t x, simsimd_bf16_t *result_ptr) {
607
607
  simsimd_f32i32_t conv;
608
608
  conv.f = x;
609
609
  conv.i += 0x8000; // Rounding is optional
@@ -620,6 +620,44 @@ SIMSIMD_INTERNAL simsimd_u32_t simsimd_u32_ror(simsimd_u32_t x, int n) { return
620
620
  SIMSIMD_INTERNAL simsimd_u16_t simsimd_u16_ror(simsimd_u16_t x, int n) { return (x >> n) | (x << (16 - n)); }
621
621
  SIMSIMD_INTERNAL simsimd_u8_t simsimd_u8_ror(simsimd_u8_t x, int n) { return (x >> n) | (x << (8 - n)); }
622
622
 
623
+ #if SIMSIMD_DYNAMIC_DISPATCH
624
+
625
+ /** @copydoc simsimd_f16_to_f32_implementation */
626
+ SIMSIMD_DYNAMIC simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr);
627
+
628
+ /** @copydoc simsimd_f32_to_f16_implementation */
629
+ SIMSIMD_DYNAMIC void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_ptr);
630
+
631
+ /** @copydoc simsimd_bf16_to_f32_implementation */
632
+ SIMSIMD_DYNAMIC simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr);
633
+
634
+ /** @copydoc simsimd_f32_to_bf16_implementation */
635
+ SIMSIMD_DYNAMIC void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t *result_ptr);
636
+
637
+ #else // SIMSIMD_DYNAMIC_DISPATCH
638
+
639
+ /** @copydoc simsimd_f16_to_f32_implementation */
640
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_f16_to_f32(simsimd_f16_t const *x_ptr) {
641
+ return simsimd_f16_to_f32_implementation(x_ptr);
642
+ }
643
+
644
+ /** @copydoc simsimd_f32_to_f16_implementation */
645
+ SIMSIMD_PUBLIC void simsimd_f32_to_f16(simsimd_f32_t x, simsimd_f16_t *result_ptr) {
646
+ simsimd_f32_to_f16_implementation(x, result_ptr);
647
+ }
648
+
649
+ /** @copydoc simsimd_bf16_to_f32_implementation */
650
+ SIMSIMD_PUBLIC simsimd_f32_t simsimd_bf16_to_f32(simsimd_bf16_t const *x_ptr) {
651
+ return simsimd_bf16_to_f32_implementation(x_ptr);
652
+ }
653
+
654
+ /** @copydoc simsimd_f32_to_bf16_implementation */
655
+ SIMSIMD_PUBLIC void simsimd_f32_to_bf16(simsimd_f32_t x, simsimd_bf16_t *result_ptr) {
656
+ simsimd_f32_to_bf16_implementation(x, result_ptr);
657
+ }
658
+
659
+ #endif // SIMSIMD_DYNAMIC_DISPATCH
660
+
623
661
  #ifdef __cplusplus
624
662
  } // extern "C"
625
663
  #endif
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "6.4.9",
3
+ "version": "6.4.10",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "simsimd",
9
- "version": "6.4.9",
9
+ "version": "6.4.10",
10
10
  "hasInstallScript": true,
11
11
  "license": "Apache 2.0",
12
12
  "dependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "6.4.10",
3
+ "version": "6.5.1",
4
4
  "description": "Portable mixed-precision BLAS-like vector math library for x86 and ARM",
5
5
  "homepage": "https://github.com/ashvardanian/simsimd",
6
6
  "author": "Ash Vardanian",