usearch 2.16.8 → 2.16.9

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,12 +1,12 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "5.9.2",
3
+ "version": "6.0.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "simsimd",
9
- "version": "5.9.2",
9
+ "version": "6.0.5",
10
10
  "hasInstallScript": true,
11
11
  "license": "Apache 2.0",
12
12
  "dependencies": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/bindings": "^1.5.5",
19
- "@types/node": "^20.17.1",
19
+ "@types/node": "^20.17.6",
20
20
  "node-gyp": "^10.0.1",
21
21
  "prebuildify": "^6.0.0",
22
22
  "typescript": "^5.3.3"
@@ -107,9 +107,9 @@
107
107
  }
108
108
  },
109
109
  "node_modules/@types/node": {
110
- "version": "20.17.1",
111
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.1.tgz",
112
- "integrity": "sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==",
110
+ "version": "20.17.6",
111
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz",
112
+ "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==",
113
113
  "devOptional": true,
114
114
  "license": "MIT",
115
115
  "dependencies": {
@@ -335,10 +335,11 @@
335
335
  }
336
336
  },
337
337
  "node_modules/cross-spawn": {
338
- "version": "7.0.3",
339
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
340
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
338
+ "version": "7.0.6",
339
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
340
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
341
341
  "dev": true,
342
+ "license": "MIT",
342
343
  "dependencies": {
343
344
  "path-key": "^3.1.0",
344
345
  "shebang-command": "^2.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "6.0.5",
3
+ "version": "6.2.3",
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",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/bindings": "^1.5.5",
51
- "@types/node": "^20.17.1",
51
+ "@types/node": "^20.17.6",
52
52
  "node-gyp": "^10.0.1",
53
53
  "prebuildify": "^6.0.0",
54
54
  "typescript": "^5.3.3"
@@ -80,6 +80,12 @@ extern "C" {
80
80
  fn simsimd_l2sq_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
81
81
  fn simsimd_l2sq_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
82
82
 
83
+ fn simsimd_l2_i8(a: *const i8, b: *const i8, c: usize, d: *mut Distance);
84
+ fn simsimd_l2_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
85
+ fn simsimd_l2_bf16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
86
+ fn simsimd_l2_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
87
+ fn simsimd_l2_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
88
+
83
89
  fn simsimd_hamming_b8(a: *const u8, b: *const u8, c: usize, d: *mut Distance);
84
90
  fn simsimd_jaccard_b8(a: *const u8, b: *const u8, c: usize, d: *mut Distance);
85
91
 
@@ -93,6 +99,9 @@ extern "C" {
93
99
  fn simsimd_kl_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
94
100
  fn simsimd_kl_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
95
101
 
102
+ fn simsimd_intersect_u16(a: *const u16, b: *const u16, a_length: usize, b_length: usize, d: *mut Distance);
103
+ fn simsimd_intersect_u32(a: *const u32, b: *const u32, a_length: usize, b_length: usize, d: *mut Distance);
104
+
96
105
  fn simsimd_uses_neon() -> i32;
97
106
  fn simsimd_uses_neon_f16() -> i32;
98
107
  fn simsimd_uses_neon_bf16() -> i32;
@@ -215,6 +224,12 @@ where
215
224
  /// between corresponding elements of the two slices.
216
225
  fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance>;
217
226
 
227
+ /// Computes the Euclidean distance between two slices.
228
+ /// The Euclidean distance is the square root of
229
+ // sum of the squared differences between corresponding
230
+ /// elements of the two slices.
231
+ fn l2(a: &[Self], b: &[Self]) -> Option<Distance>;
232
+
218
233
  /// Computes the squared Euclidean distance between two slices.
219
234
  /// The squared Euclidean distance is the sum of the squared differences
220
235
  /// between corresponding elements of the two slices.
@@ -222,6 +237,14 @@ where
222
237
  SpatialSimilarity::l2sq(a, b)
223
238
  }
224
239
 
240
+ /// Computes the Euclidean distance between two slices.
241
+ /// The Euclidean distance is the square root of the
242
+ /// sum of the squared differences between corresponding
243
+ /// elements of the two slices.
244
+ fn euclidean(a: &[Self], b: &[Self]) -> Option<Distance> {
245
+ SpatialSimilarity::l2(a, b)
246
+ }
247
+
225
248
  /// Computes the squared Euclidean distance between two slices.
226
249
  /// The squared Euclidean distance is the sum of the squared differences
227
250
  /// between corresponding elements of the two slices.
@@ -295,6 +318,16 @@ where
295
318
  fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct>;
296
319
  }
297
320
 
321
+ /// `Sparse` provides trait methods for sparse vectors.
322
+ pub trait Sparse
323
+ where
324
+ Self: Sized,
325
+ {
326
+ /// Computes the number of common elements between two sparse vectors.
327
+ /// both vectors must be sorted in ascending order.
328
+ fn intersect(a: &[Self], b: &[Self]) -> Option<Distance>;
329
+ }
330
+
298
331
  impl BinarySimilarity for u8 {
299
332
  fn hamming(a: &[Self], b: &[Self]) -> Option<Distance> {
300
333
  if a.len() != b.len() {
@@ -347,6 +380,38 @@ impl SpatialSimilarity for i8 {
347
380
  unsafe { simsimd_l2sq_i8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
348
381
  Some(distance_value)
349
382
  }
383
+
384
+ fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
385
+ if a.len() != b.len() {
386
+ return None;
387
+ }
388
+ let mut distance_value: Distance = 0.0;
389
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
390
+ unsafe { simsimd_l2_i8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
391
+ Some(distance_value)
392
+ }
393
+ }
394
+
395
+ impl Sparse for u16 {
396
+
397
+ fn intersect(a: &[Self], b: &[Self]) -> Option<Distance> {
398
+ let mut distance_value: Distance = 0.0;
399
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
400
+ unsafe { simsimd_intersect_u16(a.as_ptr(), b.as_ptr(), a.len(), b.len(), distance_ptr) };
401
+ Some(distance_value)
402
+ }
403
+
404
+ }
405
+
406
+ impl Sparse for u32 {
407
+
408
+ fn intersect(a: &[Self], b: &[Self]) -> Option<Distance> {
409
+ let mut distance_value: Distance = 0.0;
410
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
411
+ unsafe { simsimd_intersect_u32(a.as_ptr(), b.as_ptr(), a.len(), b.len(), distance_ptr) };
412
+ Some(distance_value)
413
+ }
414
+
350
415
  }
351
416
 
352
417
  impl SpatialSimilarity for f16 {
@@ -391,6 +456,20 @@ impl SpatialSimilarity for f16 {
391
456
  unsafe { simsimd_l2sq_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
392
457
  Some(distance_value)
393
458
  }
459
+
460
+ fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
461
+
462
+ if a.len() != b.len() {
463
+ return None;
464
+ }
465
+ // Explicitly cast `*const f16` to `*const u16`
466
+ let a_ptr = a.as_ptr() as *const u16;
467
+ let b_ptr = b.as_ptr() as *const u16;
468
+ let mut distance_value: Distance = 0.0;
469
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
470
+ unsafe { simsimd_l2_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
471
+ Some(distance_value)
472
+ }
394
473
  }
395
474
 
396
475
  impl SpatialSimilarity for bf16 {
@@ -435,6 +514,19 @@ impl SpatialSimilarity for bf16 {
435
514
  unsafe { simsimd_l2sq_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
436
515
  Some(distance_value)
437
516
  }
517
+
518
+ fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
519
+ if a.len() != b.len() {
520
+ return None;
521
+ }
522
+ // Explicitly cast `*const bf16` to `*const u16`
523
+ let a_ptr = a.as_ptr() as *const u16;
524
+ let b_ptr = b.as_ptr() as *const u16;
525
+ let mut distance_value: Distance = 0.0;
526
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
527
+ unsafe { simsimd_l2sq_bf16(a_ptr, b_ptr, a.len(), distance_ptr) };
528
+ Some(distance_value)
529
+ }
438
530
  }
439
531
 
440
532
  impl SpatialSimilarity for f32 {
@@ -467,6 +559,16 @@ impl SpatialSimilarity for f32 {
467
559
  unsafe { simsimd_l2sq_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
468
560
  Some(distance_value)
469
561
  }
562
+
563
+ fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
564
+ if a.len() != b.len() {
565
+ return None;
566
+ }
567
+ let mut distance_value: Distance = 0.0;
568
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
569
+ unsafe { simsimd_l2_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
570
+ Some(distance_value)
571
+ }
470
572
  }
471
573
 
472
574
  impl SpatialSimilarity for f64 {
@@ -499,6 +601,16 @@ impl SpatialSimilarity for f64 {
499
601
  unsafe { simsimd_l2sq_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
500
602
  Some(distance_value)
501
603
  }
604
+
605
+ fn l2(a: &[Self], b: &[Self]) -> Option<Distance> {
606
+ if a.len() != b.len() {
607
+ return None;
608
+ }
609
+ let mut distance_value: Distance = 0.0;
610
+ let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
611
+ unsafe { simsimd_l2_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
612
+ Some(distance_value)
613
+ }
502
614
  }
503
615
 
504
616
  impl ProbabilitySimilarity for f16 {
@@ -850,6 +962,60 @@ mod tests {
850
962
  }
851
963
  }
852
964
 
965
+ #[test]
966
+ fn test_l2_f32() {
967
+ let a: &[f32; 3] = &[1.0, 2.0, 3.0];
968
+ let b: &[f32; 3] = &[4.0, 5.0, 6.0];
969
+ if let Some(result) = SpatialSimilarity::euclidean(a, b) {
970
+ println!("The result of l2_f32 is {:.8}", result);
971
+ assert_almost_equal(5.2, result, 0.01);
972
+ }
973
+ }
974
+
975
+ #[test]
976
+ fn test_l2_f64() {
977
+ let a: &[f64; 3] = &[1.0, 2.0, 3.0];
978
+ let b: &[f64; 3] = &[4.0, 5.0, 6.0];
979
+ if let Some(result) = SpatialSimilarity::euclidean(a, b) {
980
+ println!("The result of l2_f64 is {:.8}", result);
981
+ assert_almost_equal(5.2, result, 0.01);
982
+ }
983
+ }
984
+
985
+ #[test]
986
+ fn test_l2_f16() {
987
+ let a_half: Vec<HalfF16> = vec![1.0, 2.0, 3.0]
988
+ .iter()
989
+ .map(|&x| HalfF16::from_f32(x))
990
+ .collect();
991
+ let b_half: Vec<HalfF16> = vec![4.0, 5.0, 6.0]
992
+ .iter()
993
+ .map(|&x| HalfF16::from_f32(x))
994
+ .collect();
995
+
996
+
997
+ let a_simsimd: &[f16] =
998
+ unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const f16, a_half.len()) };
999
+ let b_simsimd: &[f16] =
1000
+ unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const f16, b_half.len()) };
1001
+
1002
+ if let Some(result) = SpatialSimilarity::euclidean(&a_simsimd, &b_simsimd) {
1003
+ println!("The result of l2_f16 is {:.8}", result);
1004
+ assert_almost_equal(5.2, result, 0.01);
1005
+ }
1006
+
1007
+ }
1008
+
1009
+ #[test]
1010
+ fn test_l2_i8() {
1011
+ let a = &[1, 2, 3];
1012
+ let b = &[4, 5, 6];
1013
+
1014
+ if let Some(result) = SpatialSimilarity::euclidean(a, b) {
1015
+ println!("The result of l2_i8 is {:.8}", result);
1016
+ assert_almost_equal(5.2, result, 0.01);
1017
+ }
1018
+ }
853
1019
  // Adding new tests for bit-level distances
854
1020
  #[test]
855
1021
  fn test_hamming_u8() {
@@ -877,19 +1043,19 @@ mod tests {
877
1043
  // Adding new tests for probability similarities
878
1044
  #[test]
879
1045
  fn test_js_f32() {
880
- let a = &[0.1, 0.9, 0.0];
881
- let b = &[0.2, 0.8, 0.0];
1046
+ let a: &[f32; 3] = &[0.1, 0.9, 0.0];
1047
+ let b: &[f32; 3] = &[0.2, 0.8, 0.0];
882
1048
 
883
1049
  if let Some(result) = ProbabilitySimilarity::jensenshannon(a, b) {
884
1050
  println!("The result of js_f32 is {:.8}", result);
885
- assert_almost_equal(0.01, result, 0.01); // Example value
1051
+ assert_almost_equal(0.099, result, 0.01); // Example value
886
1052
  }
887
1053
  }
888
1054
 
889
1055
  #[test]
890
1056
  fn test_kl_f32() {
891
- let a = &[0.1, 0.9, 0.0];
892
- let b = &[0.2, 0.8, 0.0];
1057
+ let a: &[f32; 3] = &[0.1, 0.9, 0.0];
1058
+ let b: &[f32; 3] = &[0.2, 0.8, 0.0];
893
1059
 
894
1060
  if let Some(result) = ProbabilitySimilarity::kullbackleibler(a, b) {
895
1061
  println!("The result of kl_f32 is {:.8}", result);
@@ -990,4 +1156,51 @@ mod tests {
990
1156
  assert_almost_equal(0.025, result, 0.01);
991
1157
  }
992
1158
  }
1159
+
1160
+ #[test]
1161
+ fn test_intersect_u16() {
1162
+ {
1163
+ let a_u16: &[u16] = &[153, 16384, 17408];
1164
+ let b_u16: &[u16] = &[15360, 16384, 7408];
1165
+
1166
+ if let Some(result) = Sparse::intersect(a_u16, b_u16) {
1167
+ println!("The result of intersect_u16 is {:.8}", result);
1168
+ assert_almost_equal(1.0, result, 0.0001);
1169
+ }
1170
+ }
1171
+
1172
+ {
1173
+ let a_u16: &[u16] = &[153, 11638, 08];
1174
+ let b_u16: &[u16] = &[15360, 16384, 7408];
1175
+
1176
+ if let Some(result) = Sparse::intersect(a_u16, b_u16) {
1177
+ println!("The result of intersect_u16 is {:.8}", result);
1178
+ assert_almost_equal(0.0, result, 0.0001);
1179
+ }
1180
+ }
1181
+ }
1182
+
1183
+ #[test]
1184
+ fn test_intersect_u32() {
1185
+ {
1186
+ let a_u32: &[u32] = &[11, 153];
1187
+ let b_u32: &[u32] = &[11, 153, 7408, 16384];
1188
+
1189
+ if let Some(result) = Sparse::intersect(a_u32, b_u32) {
1190
+ println!("The result of intersect_u32 is {:.8}", result);
1191
+ assert_almost_equal(2.0, result, 0.0001);
1192
+ }
1193
+ }
1194
+
1195
+ {
1196
+ let a_u32: &[u32] = &[153, 7408, 11638];
1197
+ let b_u32: &[u32] = &[153, 7408, 11638];
1198
+
1199
+ if let Some(result) = Sparse::intersect(a_u32, b_u32) {
1200
+ println!("The result of intersect_u32 is {:.8}", result);
1201
+ assert_almost_equal(3.0, result, 0.0001);
1202
+ }
1203
+ }
1204
+
1205
+ }
993
1206
  }