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.
- package/README.md +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/Cargo.lock +201 -197
- package/simsimd/Cargo.toml +8 -4
- package/simsimd/README.md +114 -48
- package/simsimd/c/lib.c +16 -0
- package/simsimd/include/simsimd/simsimd.h +2 -2
- package/simsimd/include/simsimd/types.h +42 -4
- package/simsimd/package-lock.json +2 -2
- package/simsimd/package.json +1 -1
- package/simsimd/rust/lib.rs +439 -16
package/simsimd/Cargo.toml
CHANGED
|
@@ -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
|
+
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.
|
|
48
|
-
rand = { version = "0.
|
|
49
|
-
half = { version = "2.
|
|
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::
|
|
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
|
|
683
|
+
let jensen_shannon = f32::jensenshannon(&vector_a, &vector_b)
|
|
654
684
|
.expect("Vectors must be of the same length");
|
|
655
685
|
|
|
656
|
-
println!("
|
|
686
|
+
println!("Jensen-Shannon Divergence: {}", jensen_shannon);
|
|
657
687
|
|
|
658
|
-
let
|
|
688
|
+
let kullback_leibler = f32::kullbackleibler(&vector_a, &vector_b)
|
|
659
689
|
.expect("Vectors must be of the same length");
|
|
660
690
|
|
|
661
|
-
println!("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
709
|
-
let
|
|
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
|
-
|
|
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
|
-
|
|
779
|
+
SimSIMD provides a `bf16` type with built-in conversion methods and direct bit access.
|
|
725
780
|
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
import simsimd as simd
|
|
781
|
+
```rust
|
|
782
|
+
use simsimd::{SpatialSimilarity, bf16};
|
|
729
783
|
|
|
730
|
-
|
|
731
|
-
|
|
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
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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(
|
|
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(
|
|
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 =>
|
|
802
|
-
const quantizedVectorB = new Int8Array(vectorB.map(v =>
|
|
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(
|
|
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
|
|
92
|
-
#define SIMSIMD_VERSION_PATCH
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
+
"version": "6.4.10",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "Apache 2.0",
|
|
12
12
|
"dependencies": {
|
package/simsimd/package.json
CHANGED