1use crate::array::Array;
2use crate::error::Result;
3use crate::sealed::Sealed;
4
5use crate::utils::guard::Guarded;
6use crate::utils::{IntoOption, ScalarOrArray, VectorArray};
7use crate::Stream;
8use mlx_internal_macros::generate_macro;
9use smallvec::SmallVec;
10
11impl Array {
12 pub fn diff(&self, n: i32, axis: i32) -> Result<Array> {
24 let stream = Stream::thread_local_or_default();
25 Array::try_from_op(|res| unsafe {
26 mlx_sys::mlx_diff(res, self.as_ptr(), n, axis, stream.as_ref().as_ptr())
27 })
28 }
29
30 pub fn trunc(&self) -> Result<Array> {
41 let stream = Stream::thread_local_or_default();
42 Array::try_from_op(|res| unsafe {
43 mlx_sys::mlx_trunc(res, self.as_ptr(), stream.as_ref().as_ptr())
44 })
45 }
46
47 pub fn abs(&self) -> Result<Array> {
60 let stream = Stream::thread_local_or_default();
61 Array::try_from_op(|res| unsafe {
62 mlx_sys::mlx_abs(res, self.as_ptr(), stream.as_ref().as_ptr())
63 })
64 }
65
66 #[deprecated(
68 since = "0.26.0",
69 note = "use `with_stream` or `with_device` around `abs`"
70 )]
71 pub fn abs_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
72 crate::with_stream(stream.as_ref(), || self.abs())
73 }
74
75 pub fn add(&self, other: impl AsRef<Array>) -> Result<Array> {
95 let stream = Stream::thread_local_or_default();
96 Array::try_from_op(|res| unsafe {
97 mlx_sys::mlx_add(
98 res,
99 self.as_ptr(),
100 other.as_ref().as_ptr(),
101 stream.as_ref().as_ptr(),
102 )
103 })
104 }
105
106 #[deprecated(
108 since = "0.26.0",
109 note = "use `with_stream` or `with_device` around `add`"
110 )]
111 pub fn add_device(
112 &self,
113 other: impl AsRef<Array>,
114 stream: impl AsRef<Stream>,
115 ) -> Result<Array> {
116 crate::with_stream(stream.as_ref(), || self.add(other))
117 }
118
119 pub fn subtract(&self, other: impl AsRef<Array>) -> Result<Array> {
139 let stream = Stream::thread_local_or_default();
140 Array::try_from_op(|res| unsafe {
141 mlx_sys::mlx_subtract(
142 res,
143 self.as_ptr(),
144 other.as_ref().as_ptr(),
145 stream.as_ref().as_ptr(),
146 )
147 })
148 }
149
150 #[deprecated(
152 since = "0.26.0",
153 note = "use `with_stream` or `with_device` around `subtract`"
154 )]
155 pub fn subtract_device(
156 &self,
157 other: impl AsRef<Array>,
158 stream: impl AsRef<Stream>,
159 ) -> Result<Array> {
160 crate::with_stream(stream.as_ref(), || self.subtract(other))
161 }
162
163 pub fn negative(&self) -> Result<Array> {
178 let stream = Stream::thread_local_or_default();
179 Array::try_from_op(|res| unsafe {
180 mlx_sys::mlx_negative(res, self.as_ptr(), stream.as_ref().as_ptr())
181 })
182 }
183
184 #[deprecated(
186 since = "0.26.0",
187 note = "use `with_stream` or `with_device` around `negative`"
188 )]
189 pub fn negative_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
190 crate::with_stream(stream.as_ref(), || self.negative())
191 }
192
193 pub fn multiply(&self, other: impl AsRef<Array>) -> Result<Array> {
209 let stream = Stream::thread_local_or_default();
210 Array::try_from_op(|res| unsafe {
211 mlx_sys::mlx_multiply(
212 res,
213 self.as_ptr(),
214 other.as_ref().as_ptr(),
215 stream.as_ref().as_ptr(),
216 )
217 })
218 }
219
220 #[deprecated(
222 since = "0.26.0",
223 note = "use `with_stream` or `with_device` around `multiply`"
224 )]
225 pub fn multiply_device(
226 &self,
227 other: impl AsRef<Array>,
228 stream: impl AsRef<Stream>,
229 ) -> Result<Array> {
230 crate::with_stream(stream.as_ref(), || self.multiply(other))
231 }
232
233 pub fn nan_to_num(
243 &self,
244 nan: impl IntoOption<f32>,
245 pos_inf: impl IntoOption<f32>,
246 neg_inf: impl IntoOption<f32>,
247 ) -> Result<Array> {
248 let stream = Stream::thread_local_or_default();
249 let pos_inf = pos_inf.into_option();
250 let neg_inf = neg_inf.into_option();
251
252 let pos_inf = mlx_sys::mlx_optional_float {
253 value: pos_inf.unwrap_or(0.0),
254 has_value: pos_inf.is_some(),
255 };
256 let neg_inf = mlx_sys::mlx_optional_float {
257 value: neg_inf.unwrap_or(0.0),
258 has_value: neg_inf.is_some(),
259 };
260
261 Array::try_from_op(|res| unsafe {
262 mlx_sys::mlx_nan_to_num(
263 res,
264 self.as_ptr(),
265 nan.into_option().unwrap_or(0.),
266 pos_inf,
267 neg_inf,
268 stream.as_ref().as_ptr(),
269 )
270 })
271 }
272
273 #[deprecated(
275 since = "0.26.0",
276 note = "use `with_stream` or `with_device` around `nan_to_num`"
277 )]
278 pub fn nan_to_num_device(
279 &self,
280 nan: impl IntoOption<f32>,
281 pos_inf: impl IntoOption<f32>,
282 neg_inf: impl IntoOption<f32>,
283 stream: impl AsRef<Stream>,
284 ) -> Result<Array> {
285 crate::with_stream(stream.as_ref(), || self.nan_to_num(nan, pos_inf, neg_inf))
286 }
287
288 pub fn divide(&self, other: impl AsRef<Array>) -> Result<Array> {
308 let stream = Stream::thread_local_or_default();
309 Array::try_from_op(|res| unsafe {
310 mlx_sys::mlx_divide(
311 res,
312 self.as_ptr(),
313 other.as_ref().as_ptr(),
314 stream.as_ref().as_ptr(),
315 )
316 })
317 }
318
319 #[deprecated(
321 since = "0.26.0",
322 note = "use `with_stream` or `with_device` around `divide`"
323 )]
324 pub fn divide_device(
325 &self,
326 other: impl AsRef<Array>,
327 stream: impl AsRef<Stream>,
328 ) -> Result<Array> {
329 crate::with_stream(stream.as_ref(), || self.divide(other))
330 }
331
332 pub fn power(&self, other: impl AsRef<Array>) -> Result<Array> {
352 let stream = Stream::thread_local_or_default();
353 Array::try_from_op(|res| unsafe {
354 mlx_sys::mlx_power(
355 res,
356 self.as_ptr(),
357 other.as_ref().as_ptr(),
358 stream.as_ref().as_ptr(),
359 )
360 })
361 }
362
363 #[deprecated(
365 since = "0.26.0",
366 note = "use `with_stream` or `with_device` around `power`"
367 )]
368 pub fn power_device(
369 &self,
370 other: impl AsRef<Array>,
371 stream: impl AsRef<Stream>,
372 ) -> Result<Array> {
373 crate::with_stream(stream.as_ref(), || self.power(other))
374 }
375
376 pub fn remainder(&self, other: impl AsRef<Array>) -> Result<Array> {
396 let stream = Stream::thread_local_or_default();
397 Array::try_from_op(|res| unsafe {
398 mlx_sys::mlx_remainder(
399 res,
400 self.as_ptr(),
401 other.as_ref().as_ptr(),
402 stream.as_ref().as_ptr(),
403 )
404 })
405 }
406
407 #[deprecated(
409 since = "0.26.0",
410 note = "use `with_stream` or `with_device` around `remainder`"
411 )]
412 pub fn remainder_device(
413 &self,
414 other: impl AsRef<Array>,
415 stream: impl AsRef<Stream>,
416 ) -> Result<Array> {
417 crate::with_stream(stream.as_ref(), || self.remainder(other))
418 }
419
420 pub fn sqrt(&self) -> Result<Array> {
433 let stream = Stream::thread_local_or_default();
434 Array::try_from_op(|res| unsafe {
435 mlx_sys::mlx_sqrt(res, self.as_ptr(), stream.as_ref().as_ptr())
436 })
437 }
438
439 #[deprecated(
441 since = "0.26.0",
442 note = "use `with_stream` or `with_device` around `sqrt`"
443 )]
444 pub fn sqrt_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
445 crate::with_stream(stream.as_ref(), || self.sqrt())
446 }
447
448 pub fn cos(&self) -> Result<Array> {
461 let stream = Stream::thread_local_or_default();
462 Array::try_from_op(|res| unsafe {
463 mlx_sys::mlx_cos(res, self.as_ptr(), stream.as_ref().as_ptr())
464 })
465 }
466
467 #[deprecated(
469 since = "0.26.0",
470 note = "use `with_stream` or `with_device` around `cos`"
471 )]
472 pub fn cos_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
473 crate::with_stream(stream.as_ref(), || self.cos())
474 }
475
476 pub fn exp(&self) -> Result<Array> {
491 let stream = Stream::thread_local_or_default();
492 Array::try_from_op(|res| unsafe {
493 mlx_sys::mlx_exp(res, self.as_ptr(), stream.as_ref().as_ptr())
494 })
495 }
496
497 #[deprecated(
499 since = "0.26.0",
500 note = "use `with_stream` or `with_device` around `exp`"
501 )]
502 pub fn exp_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
503 crate::with_stream(stream.as_ref(), || self.exp())
504 }
505
506 pub fn floor(&self) -> Result<Array> {
519 let stream = Stream::thread_local_or_default();
520 Array::try_from_op(|res| unsafe {
521 mlx_sys::mlx_floor(res, self.as_ptr(), stream.as_ref().as_ptr())
522 })
523 }
524
525 #[deprecated(
527 since = "0.26.0",
528 note = "use `with_stream` or `with_device` around `floor`"
529 )]
530 pub fn floor_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
531 crate::with_stream(stream.as_ref(), || self.floor())
532 }
533
534 pub fn floor_divide(&self, other: impl AsRef<Array>) -> Result<Array> {
558 let stream = Stream::thread_local_or_default();
559 Array::try_from_op(|res| unsafe {
560 mlx_sys::mlx_floor_divide(
561 res,
562 self.as_ptr(),
563 other.as_ref().as_ptr(),
564 stream.as_ref().as_ptr(),
565 )
566 })
567 }
568
569 #[deprecated(
571 since = "0.26.0",
572 note = "use `with_stream` or `with_device` around `floor_divide`"
573 )]
574 pub fn floor_divide_device(
575 &self,
576 other: impl AsRef<Array>,
577 stream: impl AsRef<Stream>,
578 ) -> Result<Array> {
579 crate::with_stream(stream.as_ref(), || self.floor_divide(other))
580 }
581
582 pub fn is_nan(&self) -> Result<Array> {
587 let stream = Stream::thread_local_or_default();
588 Array::try_from_op(|res| unsafe {
589 mlx_sys::mlx_isnan(res, self.as_ptr(), stream.as_ref().as_ptr())
590 })
591 }
592
593 #[deprecated(
595 since = "0.26.0",
596 note = "use `with_stream` or `with_device` around `is_nan`"
597 )]
598 pub fn is_nan_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
599 crate::with_stream(stream.as_ref(), || self.is_nan())
600 }
601
602 pub fn is_inf(&self) -> Result<Array> {
607 let stream = Stream::thread_local_or_default();
608 Array::try_from_op(|res| unsafe {
609 mlx_sys::mlx_isinf(res, self.as_ptr(), stream.as_ref().as_ptr())
610 })
611 }
612
613 #[deprecated(
615 since = "0.26.0",
616 note = "use `with_stream` or `with_device` around `is_inf`"
617 )]
618 pub fn is_inf_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
619 crate::with_stream(stream.as_ref(), || self.is_inf())
620 }
621
622 pub fn is_finite(&self) -> Result<Array> {
627 let stream = Stream::thread_local_or_default();
628 Array::try_from_op(|res| unsafe {
629 mlx_sys::mlx_isfinite(res, self.as_ptr(), stream.as_ref().as_ptr())
630 })
631 }
632
633 #[deprecated(
635 since = "0.26.0",
636 note = "use `with_stream` or `with_device` around `is_finite`"
637 )]
638 pub fn is_finite_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
639 crate::with_stream(stream.as_ref(), || self.is_finite())
640 }
641
642 pub fn is_neg_inf(&self) -> Result<Array> {
647 let stream = Stream::thread_local_or_default();
648 Array::try_from_op(|res| unsafe {
649 mlx_sys::mlx_isneginf(res, self.as_ptr(), stream.as_ref().as_ptr())
650 })
651 }
652
653 #[deprecated(
655 since = "0.26.0",
656 note = "use `with_stream` or `with_device` around `is_neg_inf`"
657 )]
658 pub fn is_neg_inf_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
659 crate::with_stream(stream.as_ref(), || self.is_neg_inf())
660 }
661
662 pub fn is_pos_inf(&self) -> Result<Array> {
667 let stream = Stream::thread_local_or_default();
668 Array::try_from_op(|res| unsafe {
669 mlx_sys::mlx_isposinf(res, self.as_ptr(), stream.as_ref().as_ptr())
670 })
671 }
672
673 #[deprecated(
675 since = "0.26.0",
676 note = "use `with_stream` or `with_device` around `is_pos_inf`"
677 )]
678 pub fn is_pos_inf_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
679 crate::with_stream(stream.as_ref(), || self.is_pos_inf())
680 }
681
682 pub fn log(&self) -> Result<Array> {
695 let stream = Stream::thread_local_or_default();
696 Array::try_from_op(|res| unsafe {
697 mlx_sys::mlx_log(res, self.as_ptr(), stream.as_ref().as_ptr())
698 })
699 }
700
701 #[deprecated(
703 since = "0.26.0",
704 note = "use `with_stream` or `with_device` around `log`"
705 )]
706 pub fn log_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
707 crate::with_stream(stream.as_ref(), || self.log())
708 }
709
710 pub fn log2(&self) -> Result<Array> {
723 let stream = Stream::thread_local_or_default();
724 Array::try_from_op(|res| unsafe {
725 mlx_sys::mlx_log2(res, self.as_ptr(), stream.as_ref().as_ptr())
726 })
727 }
728
729 #[deprecated(
731 since = "0.26.0",
732 note = "use `with_stream` or `with_device` around `log2`"
733 )]
734 pub fn log2_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
735 crate::with_stream(stream.as_ref(), || self.log2())
736 }
737
738 pub fn log10(&self) -> Result<Array> {
751 let stream = Stream::thread_local_or_default();
752 Array::try_from_op(|res| unsafe {
753 mlx_sys::mlx_log10(res, self.as_ptr(), stream.as_ref().as_ptr())
754 })
755 }
756
757 #[deprecated(
759 since = "0.26.0",
760 note = "use `with_stream` or `with_device` around `log10`"
761 )]
762 pub fn log10_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
763 crate::with_stream(stream.as_ref(), || self.log10())
764 }
765
766 pub fn log1p(&self) -> Result<Array> {
779 let stream = Stream::thread_local_or_default();
780 Array::try_from_op(|res| unsafe {
781 mlx_sys::mlx_log1p(res, self.as_ptr(), stream.as_ref().as_ptr())
782 })
783 }
784
785 #[deprecated(
787 since = "0.26.0",
788 note = "use `with_stream` or `with_device` around `log1p`"
789 )]
790 pub fn log1p_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
791 crate::with_stream(stream.as_ref(), || self.log1p())
792 }
793
794 pub fn matmul(&self, other: impl AsRef<Array>) -> Result<Array> {
824 let stream = Stream::thread_local_or_default();
825 Array::try_from_op(|res| unsafe {
826 mlx_sys::mlx_matmul(
827 res,
828 self.as_ptr(),
829 other.as_ref().as_ptr(),
830 stream.as_ref().as_ptr(),
831 )
832 })
833 }
834
835 #[deprecated(
837 since = "0.26.0",
838 note = "use `with_stream` or `with_device` around `matmul`"
839 )]
840 pub fn matmul_device(
841 &self,
842 other: impl AsRef<Array>,
843 stream: impl AsRef<Stream>,
844 ) -> Result<Array> {
845 crate::with_stream(stream.as_ref(), || self.matmul(other))
846 }
847
848 pub fn reciprocal(&self) -> Result<Array> {
861 let stream = Stream::thread_local_or_default();
862 Array::try_from_op(|res| unsafe {
863 mlx_sys::mlx_reciprocal(res, self.as_ptr(), stream.as_ref().as_ptr())
864 })
865 }
866
867 #[deprecated(
869 since = "0.26.0",
870 note = "use `with_stream` or `with_device` around `reciprocal`"
871 )]
872 pub fn reciprocal_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
873 crate::with_stream(stream.as_ref(), || self.reciprocal())
874 }
875
876 pub fn round(&self, decimals: impl Into<Option<i32>>) -> Result<Array> {
882 let stream = Stream::thread_local_or_default();
883 Array::try_from_op(|res| unsafe {
884 mlx_sys::mlx_round(
885 res,
886 self.as_ptr(),
887 decimals.into().unwrap_or(0),
888 stream.as_ref().as_ptr(),
889 )
890 })
891 }
892
893 #[deprecated(
895 since = "0.26.0",
896 note = "use `with_stream` or `with_device` around `round`"
897 )]
898 pub fn round_device(
899 &self,
900 decimals: impl Into<Option<i32>>,
901 stream: impl AsRef<Stream>,
902 ) -> Result<Array> {
903 crate::with_stream(stream.as_ref(), || self.round(decimals))
904 }
905
906 pub fn rsqrt(&self) -> Result<Array> {
908 let stream = Stream::thread_local_or_default();
909 Array::try_from_op(|res| unsafe {
910 mlx_sys::mlx_rsqrt(res, self.as_ptr(), stream.as_ref().as_ptr())
911 })
912 }
913
914 #[deprecated(
916 since = "0.26.0",
917 note = "use `with_stream` or `with_device` around `rsqrt`"
918 )]
919 pub fn rsqrt_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
920 crate::with_stream(stream.as_ref(), || self.rsqrt())
921 }
922
923 pub fn sin(&self) -> Result<Array> {
925 let stream = Stream::thread_local_or_default();
926 Array::try_from_op(|res| unsafe {
927 mlx_sys::mlx_sin(res, self.as_ptr(), stream.as_ref().as_ptr())
928 })
929 }
930
931 #[deprecated(
933 since = "0.26.0",
934 note = "use `with_stream` or `with_device` around `sin`"
935 )]
936 pub fn sin_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
937 crate::with_stream(stream.as_ref(), || self.sin())
938 }
939
940 pub fn square(&self) -> Result<Array> {
942 let stream = Stream::thread_local_or_default();
943 Array::try_from_op(|res| unsafe {
944 mlx_sys::mlx_square(res, self.as_ptr(), stream.as_ref().as_ptr())
945 })
946 }
947
948 #[deprecated(
950 since = "0.26.0",
951 note = "use `with_stream` or `with_device` around `square`"
952 )]
953 pub fn square_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
954 crate::with_stream(stream.as_ref(), || self.square())
955 }
956
957 pub fn real(&self) -> Result<Array> {
959 let stream = Stream::thread_local_or_default();
960 Array::try_from_op(|res| unsafe {
961 mlx_sys::mlx_real(res, self.as_ptr(), stream.as_ref().as_ptr())
962 })
963 }
964
965 #[deprecated(
967 since = "0.26.0",
968 note = "use `with_stream` or `with_device` around `real`"
969 )]
970 pub fn real_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
971 crate::with_stream(stream.as_ref(), || self.real())
972 }
973
974 pub fn imag(&self) -> Result<Array> {
976 let stream = Stream::thread_local_or_default();
977 Array::try_from_op(|res| unsafe {
978 mlx_sys::mlx_imag(res, self.as_ptr(), stream.as_ref().as_ptr())
979 })
980 }
981
982 #[deprecated(
984 since = "0.26.0",
985 note = "use `with_stream` or `with_device` around `imag`"
986 )]
987 pub fn imag_device(&self, stream: impl AsRef<Stream>) -> Result<Array> {
988 crate::with_stream(stream.as_ref(), || self.imag())
989 }
990}
991
992pub fn abs(a: impl AsRef<Array>) -> Result<Array> {
1003 a.as_ref().abs()
1004}
1005
1006#[generate_macro(customize(forwarding_shim = true))]
1008#[deprecated(
1009 since = "0.26.0",
1010 note = "use `with_stream` or `with_device` around `abs`"
1011)]
1012pub fn abs_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1013 crate::with_stream(stream.as_ref(), || abs(a))
1014}
1015
1016pub fn acos(a: impl AsRef<Array>) -> Result<Array> {
1018 let stream = Stream::thread_local_or_default();
1019 Array::try_from_op(|res| unsafe {
1020 mlx_sys::mlx_arccos(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1021 })
1022}
1023
1024#[generate_macro(customize(forwarding_shim = true))]
1026#[deprecated(
1027 since = "0.26.0",
1028 note = "use `with_stream` or `with_device` around `acos`"
1029)]
1030pub fn acos_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1031 crate::with_stream(stream.as_ref(), || acos(a))
1032}
1033
1034pub fn acosh(a: impl AsRef<Array>) -> Result<Array> {
1036 let stream = Stream::thread_local_or_default();
1037 Array::try_from_op(|res| unsafe {
1038 mlx_sys::mlx_arccosh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1039 })
1040}
1041
1042#[generate_macro(customize(forwarding_shim = true))]
1044#[deprecated(
1045 since = "0.26.0",
1046 note = "use `with_stream` or `with_device` around `acosh`"
1047)]
1048pub fn acosh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1049 crate::with_stream(stream.as_ref(), || acosh(a))
1050}
1051
1052pub fn vecdot(lhs: impl AsRef<Array>, rhs: impl AsRef<Array>, axis: i32) -> Result<Array> {
1064 let stream = Stream::thread_local_or_default();
1065 Array::try_from_op(|res| unsafe {
1066 mlx_sys::mlx_vecdot(
1067 res,
1068 lhs.as_ref().as_ptr(),
1069 rhs.as_ref().as_ptr(),
1070 axis,
1071 stream.as_ref().as_ptr(),
1072 )
1073 })
1074}
1075
1076pub fn add(lhs: impl AsRef<Array>, rhs: impl AsRef<Array>) -> Result<Array> {
1078 lhs.as_ref().add(rhs)
1079}
1080
1081#[generate_macro(customize(forwarding_shim = true))]
1083#[deprecated(
1084 since = "0.26.0",
1085 note = "use `with_stream` or `with_device` around `add`"
1086)]
1087pub fn add_device(
1088 lhs: impl AsRef<Array>,
1089 rhs: impl AsRef<Array>,
1090 #[optional] stream: impl AsRef<Stream>,
1091) -> Result<Array> {
1092 crate::with_stream(stream.as_ref(), || add(lhs, rhs))
1093}
1094
1095pub fn asin(a: impl AsRef<Array>) -> Result<Array> {
1097 let stream = Stream::thread_local_or_default();
1098 Array::try_from_op(|res| unsafe {
1099 mlx_sys::mlx_arcsin(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1100 })
1101}
1102
1103#[generate_macro(customize(forwarding_shim = true))]
1105#[deprecated(
1106 since = "0.26.0",
1107 note = "use `with_stream` or `with_device` around `asin`"
1108)]
1109pub fn asin_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1110 crate::with_stream(stream.as_ref(), || asin(a))
1111}
1112
1113pub fn asinh(a: impl AsRef<Array>) -> Result<Array> {
1115 let stream = Stream::thread_local_or_default();
1116 Array::try_from_op(|res| unsafe {
1117 mlx_sys::mlx_arcsinh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1118 })
1119}
1120
1121#[generate_macro(customize(forwarding_shim = true))]
1123#[deprecated(
1124 since = "0.26.0",
1125 note = "use `with_stream` or `with_device` around `asinh`"
1126)]
1127pub fn asinh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1128 crate::with_stream(stream.as_ref(), || asinh(a))
1129}
1130
1131pub fn atan(a: impl AsRef<Array>) -> Result<Array> {
1133 let stream = Stream::thread_local_or_default();
1134 Array::try_from_op(|res| unsafe {
1135 mlx_sys::mlx_arctan(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1136 })
1137}
1138
1139#[generate_macro(customize(forwarding_shim = true))]
1141#[deprecated(
1142 since = "0.26.0",
1143 note = "use `with_stream` or `with_device` around `atan`"
1144)]
1145pub fn atan_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1146 crate::with_stream(stream.as_ref(), || atan(a))
1147}
1148
1149pub fn atan2(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1151 let stream = Stream::thread_local_or_default();
1152 let a = a.as_ref();
1153 let b = b.as_ref();
1154
1155 Array::try_from_op(|res| unsafe {
1156 mlx_sys::mlx_arctan2(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
1157 })
1158}
1159
1160#[generate_macro(customize(forwarding_shim = true))]
1162#[deprecated(
1163 since = "0.26.0",
1164 note = "use `with_stream` or `with_device` around `atan2`"
1165)]
1166pub fn atan2_device(
1167 a: impl AsRef<Array>,
1168 b: impl AsRef<Array>,
1169 #[optional] stream: impl AsRef<Stream>,
1170) -> Result<Array> {
1171 crate::with_stream(stream.as_ref(), || atan2(a, b))
1172}
1173
1174pub fn atanh(a: impl AsRef<Array>) -> Result<Array> {
1176 let stream = Stream::thread_local_or_default();
1177 Array::try_from_op(|res| unsafe {
1178 mlx_sys::mlx_arctanh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1179 })
1180}
1181
1182#[generate_macro(customize(forwarding_shim = true))]
1184#[deprecated(
1185 since = "0.26.0",
1186 note = "use `with_stream` or `with_device` around `atanh`"
1187)]
1188pub fn atanh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1189 crate::with_stream(stream.as_ref(), || atanh(a))
1190}
1191
1192pub fn ceil(a: impl AsRef<Array>) -> Result<Array> {
1194 let stream = Stream::thread_local_or_default();
1195 Array::try_from_op(|res| unsafe {
1196 mlx_sys::mlx_ceil(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1197 })
1198}
1199
1200#[generate_macro(customize(forwarding_shim = true))]
1202#[deprecated(
1203 since = "0.26.0",
1204 note = "use `with_stream` or `with_device` around `ceil`"
1205)]
1206pub fn ceil_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1207 crate::with_stream(stream.as_ref(), || ceil(a))
1208}
1209
1210pub trait ClipBound<'min, 'max>: Sealed {
1215 fn into_min_max(
1217 self,
1218 ) -> (
1219 Option<impl ScalarOrArray<'min>>,
1220 Option<impl ScalarOrArray<'max>>,
1221 );
1222}
1223
1224impl<'min, Min> ClipBound<'min, 'min> for (Min, ())
1225where
1226 Min: ScalarOrArray<'min> + Sealed,
1227{
1228 fn into_min_max(
1229 self,
1230 ) -> (
1231 Option<impl ScalarOrArray<'min>>,
1232 Option<impl ScalarOrArray<'min>>,
1233 ) {
1234 (Some(self.0), Option::<Min>::None)
1235 }
1236}
1237
1238impl<'max, Max> ClipBound<'max, 'max> for ((), Max)
1239where
1240 Max: ScalarOrArray<'max> + Sealed,
1241{
1242 fn into_min_max(
1243 self,
1244 ) -> (
1245 Option<impl ScalarOrArray<'max>>,
1246 Option<impl ScalarOrArray<'max>>,
1247 ) {
1248 (Option::<Max>::None, Some(self.1))
1249 }
1250}
1251
1252impl<'min, 'max, Min, Max> ClipBound<'min, 'max> for (Min, Max)
1253where
1254 Min: ScalarOrArray<'min> + Sealed,
1255 Max: ScalarOrArray<'max> + Sealed,
1256{
1257 fn into_min_max(
1258 self,
1259 ) -> (
1260 Option<impl ScalarOrArray<'min>>,
1261 Option<impl ScalarOrArray<'max>>,
1262 ) {
1263 (Some(self.0), Some(self.1))
1264 }
1265}
1266
1267pub fn clip<'min, 'max>(a: impl AsRef<Array>, bound: impl ClipBound<'min, 'max>) -> Result<Array> {
1289 let stream = Stream::thread_local_or_default();
1290 let (a_min, a_max) = bound.into_min_max();
1291
1292 let a_min = a_min.map(|min| min.into_owned_or_ref_array());
1294 let a_max = a_max.map(|max| max.into_owned_or_ref_array());
1295
1296 unsafe {
1297 let min_ptr = match &a_min {
1298 Some(a_min) => a_min.as_ref().as_ptr(),
1299 None => mlx_sys::mlx_array_new(),
1300 };
1301 let max_ptr = match &a_max {
1302 Some(a_max) => a_max.as_ref().as_ptr(),
1303 None => mlx_sys::mlx_array_new(),
1304 };
1305
1306 Array::try_from_op(|res| {
1307 mlx_sys::mlx_clip(
1308 res,
1309 a.as_ref().as_ptr(),
1310 min_ptr,
1311 max_ptr,
1312 stream.as_ref().as_ptr(),
1313 )
1314 })
1315 }
1316}
1317
1318#[generate_macro(customize(forwarding_shim = true))]
1320#[deprecated(
1321 since = "0.26.0",
1322 note = "use `with_stream` or `with_device` around `clip`"
1323)]
1324pub fn clip_device<'min, 'max>(
1325 a: impl AsRef<Array>,
1326 bound: impl ClipBound<'min, 'max>,
1327 #[optional] stream: impl AsRef<Stream>,
1328) -> Result<Array> {
1329 crate::with_stream(stream.as_ref(), || clip(a, bound))
1330}
1331
1332pub fn cos(a: impl AsRef<Array>) -> Result<Array> {
1334 a.as_ref().cos()
1335}
1336
1337#[generate_macro(customize(forwarding_shim = true))]
1339#[deprecated(
1340 since = "0.26.0",
1341 note = "use `with_stream` or `with_device` around `cos`"
1342)]
1343pub fn cos_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1344 crate::with_stream(stream.as_ref(), || cos(a))
1345}
1346
1347pub fn cosh(a: impl AsRef<Array>) -> Result<Array> {
1349 let stream = Stream::thread_local_or_default();
1350 Array::try_from_op(|res| unsafe {
1351 mlx_sys::mlx_cosh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1352 })
1353}
1354
1355#[generate_macro(customize(forwarding_shim = true))]
1357#[deprecated(
1358 since = "0.26.0",
1359 note = "use `with_stream` or `with_device` around `cosh`"
1360)]
1361pub fn cosh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1362 crate::with_stream(stream.as_ref(), || cosh(a))
1363}
1364
1365pub fn degrees(a: impl AsRef<Array>) -> Result<Array> {
1367 let stream = Stream::thread_local_or_default();
1368 Array::try_from_op(|res| unsafe {
1369 mlx_sys::mlx_degrees(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1370 })
1371}
1372
1373#[generate_macro(customize(forwarding_shim = true))]
1375#[deprecated(
1376 since = "0.26.0",
1377 note = "use `with_stream` or `with_device` around `degrees`"
1378)]
1379pub fn degrees_device(
1380 a: impl AsRef<Array>,
1381 #[optional] stream: impl AsRef<Stream>,
1382) -> Result<Array> {
1383 crate::with_stream(stream.as_ref(), || degrees(a))
1384}
1385
1386pub fn divide(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1388 a.as_ref().divide(b)
1389}
1390
1391#[generate_macro(customize(forwarding_shim = true))]
1393#[deprecated(
1394 since = "0.26.0",
1395 note = "use `with_stream` or `with_device` around `divide`"
1396)]
1397pub fn divide_device(
1398 a: impl AsRef<Array>,
1399 b: impl AsRef<Array>,
1400 #[optional] stream: impl AsRef<Stream>,
1401) -> Result<Array> {
1402 crate::with_stream(stream.as_ref(), || divide(a, b))
1403}
1404
1405pub fn divmod(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<(Array, Array)> {
1412 let stream = Stream::thread_local_or_default();
1413 let a_ptr = a.as_ref().as_ptr();
1414 let b_ptr = b.as_ref().as_ptr();
1415
1416 let vec = VectorArray::try_from_op(|res| unsafe {
1417 mlx_sys::mlx_divmod(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
1418 })?;
1419
1420 let vals: SmallVec<[_; 2]> = vec.try_into_values()?;
1421 let mut iter = vals.into_iter();
1422 let quotient = iter.next().unwrap();
1423 let remainder = iter.next().unwrap();
1424
1425 Ok((quotient, remainder))
1426}
1427
1428#[generate_macro(customize(forwarding_shim = true))]
1430#[deprecated(
1431 since = "0.26.0",
1432 note = "use `with_stream` or `with_device` around `divmod`"
1433)]
1434pub fn divmod_device(
1435 a: impl AsRef<Array>,
1436 b: impl AsRef<Array>,
1437 #[optional] stream: impl AsRef<Stream>,
1438) -> Result<(Array, Array)> {
1439 crate::with_stream(stream.as_ref(), || divmod(a, b))
1440}
1441
1442pub fn erf(a: impl AsRef<Array>) -> Result<Array> {
1444 let stream = Stream::thread_local_or_default();
1445 Array::try_from_op(|res| unsafe {
1446 mlx_sys::mlx_erf(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1447 })
1448}
1449
1450#[generate_macro(customize(forwarding_shim = true))]
1452#[deprecated(
1453 since = "0.26.0",
1454 note = "use `with_stream` or `with_device` around `erf`"
1455)]
1456pub fn erf_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1457 crate::with_stream(stream.as_ref(), || erf(a))
1458}
1459
1460pub fn erfinv(a: impl AsRef<Array>) -> Result<Array> {
1462 let stream = Stream::thread_local_or_default();
1463 Array::try_from_op(|res| unsafe {
1464 mlx_sys::mlx_erfinv(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1465 })
1466}
1467
1468#[generate_macro(customize(forwarding_shim = true))]
1470#[deprecated(
1471 since = "0.26.0",
1472 note = "use `with_stream` or `with_device` around `erfinv`"
1473)]
1474pub fn erfinv_device(
1475 a: impl AsRef<Array>,
1476 #[optional] stream: impl AsRef<Stream>,
1477) -> Result<Array> {
1478 crate::with_stream(stream.as_ref(), || erfinv(a))
1479}
1480
1481pub fn exp(a: impl AsRef<Array>) -> Result<Array> {
1483 a.as_ref().exp()
1484}
1485
1486#[generate_macro(customize(forwarding_shim = true))]
1488#[deprecated(
1489 since = "0.26.0",
1490 note = "use `with_stream` or `with_device` around `exp`"
1491)]
1492pub fn exp_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1493 crate::with_stream(stream.as_ref(), || exp(a))
1494}
1495
1496pub fn expm1(a: impl AsRef<Array>) -> Result<Array> {
1498 let stream = Stream::thread_local_or_default();
1499 Array::try_from_op(|res| unsafe {
1500 mlx_sys::mlx_expm1(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1501 })
1502}
1503
1504#[generate_macro(customize(forwarding_shim = true))]
1506#[deprecated(
1507 since = "0.26.0",
1508 note = "use `with_stream` or `with_device` around `expm1`"
1509)]
1510pub fn expm1_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1511 crate::with_stream(stream.as_ref(), || expm1(a))
1512}
1513
1514pub fn floor(a: impl AsRef<Array>) -> Result<Array> {
1516 a.as_ref().floor()
1517}
1518
1519#[generate_macro(customize(forwarding_shim = true))]
1521#[deprecated(
1522 since = "0.26.0",
1523 note = "use `with_stream` or `with_device` around `floor`"
1524)]
1525pub fn floor_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1526 crate::with_stream(stream.as_ref(), || floor(a))
1527}
1528
1529pub fn floor_divide(a: impl AsRef<Array>, other: impl AsRef<Array>) -> Result<Array> {
1531 a.as_ref().floor_divide(other)
1532}
1533
1534#[generate_macro(customize(forwarding_shim = true))]
1536#[deprecated(
1537 since = "0.26.0",
1538 note = "use `with_stream` or `with_device` around `floor_divide`"
1539)]
1540pub fn floor_divide_device(
1541 a: impl AsRef<Array>,
1542 other: impl AsRef<Array>,
1543 #[optional] stream: impl AsRef<Stream>,
1544) -> Result<Array> {
1545 crate::with_stream(stream.as_ref(), || floor_divide(a, other))
1546}
1547
1548pub fn log(a: impl AsRef<Array>) -> Result<Array> {
1550 a.as_ref().log()
1551}
1552
1553#[generate_macro(customize(forwarding_shim = true))]
1555#[deprecated(
1556 since = "0.26.0",
1557 note = "use `with_stream` or `with_device` around `log`"
1558)]
1559pub fn log_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1560 crate::with_stream(stream.as_ref(), || log(a))
1561}
1562
1563pub fn log10(a: impl AsRef<Array>) -> Result<Array> {
1565 a.as_ref().log10()
1566}
1567
1568#[generate_macro(customize(forwarding_shim = true))]
1570#[deprecated(
1571 since = "0.26.0",
1572 note = "use `with_stream` or `with_device` around `log10`"
1573)]
1574pub fn log10_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1575 crate::with_stream(stream.as_ref(), || log10(a))
1576}
1577
1578pub fn log1p(a: impl AsRef<Array>) -> Result<Array> {
1580 a.as_ref().log1p()
1581}
1582
1583#[generate_macro(customize(forwarding_shim = true))]
1585#[deprecated(
1586 since = "0.26.0",
1587 note = "use `with_stream` or `with_device` around `log1p`"
1588)]
1589pub fn log1p_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1590 crate::with_stream(stream.as_ref(), || log1p(a))
1591}
1592
1593pub fn log2(a: impl AsRef<Array>) -> Result<Array> {
1595 a.as_ref().log2()
1596}
1597
1598#[generate_macro(customize(forwarding_shim = true))]
1600#[deprecated(
1601 since = "0.26.0",
1602 note = "use `with_stream` or `with_device` around `log2`"
1603)]
1604pub fn log2_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1605 crate::with_stream(stream.as_ref(), || log2(a))
1606}
1607
1608pub fn logaddexp(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1615 let stream = Stream::thread_local_or_default();
1616 let a_ptr = a.as_ref().as_ptr();
1617 let b_ptr = b.as_ref().as_ptr();
1618
1619 Array::try_from_op(|res| unsafe {
1620 mlx_sys::mlx_logaddexp(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
1621 })
1622}
1623
1624#[generate_macro(customize(forwarding_shim = true))]
1626#[deprecated(
1627 since = "0.26.0",
1628 note = "use `with_stream` or `with_device` around `logaddexp`"
1629)]
1630pub fn logaddexp_device(
1631 a: impl AsRef<Array>,
1632 b: impl AsRef<Array>,
1633 #[optional] stream: impl AsRef<Stream>,
1634) -> Result<Array> {
1635 crate::with_stream(stream.as_ref(), || logaddexp(a, b))
1636}
1637
1638pub fn matmul(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1640 a.as_ref().matmul(b)
1641}
1642
1643#[generate_macro(customize(forwarding_shim = true))]
1645#[deprecated(
1646 since = "0.26.0",
1647 note = "use `with_stream` or `with_device` around `matmul`"
1648)]
1649pub fn matmul_device(
1650 a: impl AsRef<Array>,
1651 b: impl AsRef<Array>,
1652 #[optional] stream: impl AsRef<Stream>,
1653) -> Result<Array> {
1654 crate::with_stream(stream.as_ref(), || matmul(a, b))
1655}
1656
1657pub fn segmented_mm(
1687 a: impl AsRef<Array>,
1688 b: impl AsRef<Array>,
1689 segments: impl AsRef<Array>,
1690) -> Result<Array> {
1691 let stream = Stream::thread_local_or_default();
1692 Array::try_from_op(|res| unsafe {
1693 mlx_sys::mlx_segmented_mm(
1694 res,
1695 a.as_ref().as_ptr(),
1696 b.as_ref().as_ptr(),
1697 segments.as_ref().as_ptr(),
1698 stream.as_ref().as_ptr(),
1699 )
1700 })
1701}
1702
1703#[generate_macro(customize(forwarding_shim = true))]
1705#[deprecated(
1706 since = "0.26.0",
1707 note = "use `with_stream` or `with_device` around `segmented_mm`"
1708)]
1709pub fn segmented_mm_device(
1710 a: impl AsRef<Array>,
1711 b: impl AsRef<Array>,
1712 segments: impl AsRef<Array>,
1713 #[optional] stream: impl AsRef<Stream>,
1714) -> Result<Array> {
1715 crate::with_stream(stream.as_ref(), || segmented_mm(a, b, segments))
1716}
1717
1718pub fn maximum(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1723 let stream = Stream::thread_local_or_default();
1724 let a_ptr = a.as_ref().as_ptr();
1725 let b_ptr = b.as_ref().as_ptr();
1726
1727 Array::try_from_op(|res| unsafe {
1728 mlx_sys::mlx_maximum(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
1729 })
1730}
1731
1732#[generate_macro(customize(forwarding_shim = true))]
1734#[deprecated(
1735 since = "0.26.0",
1736 note = "use `with_stream` or `with_device` around `maximum`"
1737)]
1738pub fn maximum_device(
1739 a: impl AsRef<Array>,
1740 b: impl AsRef<Array>,
1741 #[optional] stream: impl AsRef<Stream>,
1742) -> Result<Array> {
1743 crate::with_stream(stream.as_ref(), || maximum(a, b))
1744}
1745
1746pub fn minimum(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1751 let stream = Stream::thread_local_or_default();
1752 let a_ptr = a.as_ref().as_ptr();
1753 let b_ptr = b.as_ref().as_ptr();
1754
1755 Array::try_from_op(|res| unsafe {
1756 mlx_sys::mlx_minimum(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
1757 })
1758}
1759
1760#[generate_macro(customize(forwarding_shim = true))]
1762#[deprecated(
1763 since = "0.26.0",
1764 note = "use `with_stream` or `with_device` around `minimum`"
1765)]
1766pub fn minimum_device(
1767 a: impl AsRef<Array>,
1768 b: impl AsRef<Array>,
1769 #[optional] stream: impl AsRef<Stream>,
1770) -> Result<Array> {
1771 crate::with_stream(stream.as_ref(), || minimum(a, b))
1772}
1773
1774pub fn multiply(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1776 a.as_ref().multiply(b)
1777}
1778
1779#[generate_macro(customize(forwarding_shim = true))]
1781#[deprecated(
1782 since = "0.26.0",
1783 note = "use `with_stream` or `with_device` around `multiply`"
1784)]
1785pub fn multiply_device(
1786 a: impl AsRef<Array>,
1787 b: impl AsRef<Array>,
1788 #[optional] stream: impl AsRef<Stream>,
1789) -> Result<Array> {
1790 crate::with_stream(stream.as_ref(), || multiply(a, b))
1791}
1792
1793pub fn negative(a: impl AsRef<Array>) -> Result<Array> {
1795 a.as_ref().negative()
1796}
1797
1798#[generate_macro(customize(forwarding_shim = true))]
1800#[deprecated(
1801 since = "0.26.0",
1802 note = "use `with_stream` or `with_device` around `negative`"
1803)]
1804pub fn negative_device(
1805 a: impl AsRef<Array>,
1806 #[optional] stream: impl AsRef<Stream>,
1807) -> Result<Array> {
1808 crate::with_stream(stream.as_ref(), || negative(a))
1809}
1810
1811pub fn power(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1813 a.as_ref().power(b)
1814}
1815
1816#[generate_macro(customize(forwarding_shim = true))]
1818#[deprecated(
1819 since = "0.26.0",
1820 note = "use `with_stream` or `with_device` around `power`"
1821)]
1822pub fn power_device(
1823 a: impl AsRef<Array>,
1824 b: impl AsRef<Array>,
1825 #[optional] stream: impl AsRef<Stream>,
1826) -> Result<Array> {
1827 crate::with_stream(stream.as_ref(), || power(a, b))
1828}
1829
1830pub fn radians(a: impl AsRef<Array>) -> Result<Array> {
1832 let stream = Stream::thread_local_or_default();
1833 Array::try_from_op(|res| unsafe {
1834 mlx_sys::mlx_radians(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1835 })
1836}
1837
1838#[generate_macro(customize(forwarding_shim = true))]
1840#[deprecated(
1841 since = "0.26.0",
1842 note = "use `with_stream` or `with_device` around `radians`"
1843)]
1844pub fn radians_device(
1845 a: impl AsRef<Array>,
1846 #[optional] stream: impl AsRef<Stream>,
1847) -> Result<Array> {
1848 crate::with_stream(stream.as_ref(), || radians(a))
1849}
1850
1851pub fn reciprocal(a: impl AsRef<Array>) -> Result<Array> {
1853 a.as_ref().reciprocal()
1854}
1855
1856#[generate_macro(customize(forwarding_shim = true))]
1858#[deprecated(
1859 since = "0.26.0",
1860 note = "use `with_stream` or `with_device` around `reciprocal`"
1861)]
1862pub fn reciprocal_device(
1863 a: impl AsRef<Array>,
1864 #[optional] stream: impl AsRef<Stream>,
1865) -> Result<Array> {
1866 crate::with_stream(stream.as_ref(), || reciprocal(a))
1867}
1868
1869pub fn remainder(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1871 a.as_ref().remainder(b)
1872}
1873
1874#[generate_macro(customize(forwarding_shim = true))]
1876#[deprecated(
1877 since = "0.26.0",
1878 note = "use `with_stream` or `with_device` around `remainder`"
1879)]
1880pub fn remainder_device(
1881 a: impl AsRef<Array>,
1882 b: impl AsRef<Array>,
1883 #[optional] stream: impl AsRef<Stream>,
1884) -> Result<Array> {
1885 crate::with_stream(stream.as_ref(), || remainder(a, b))
1886}
1887
1888pub fn round(a: impl AsRef<Array>, decimals: impl Into<Option<i32>>) -> Result<Array> {
1890 a.as_ref().round(decimals)
1891}
1892
1893#[generate_macro(customize(forwarding_shim = true))]
1895#[deprecated(
1896 since = "0.26.0",
1897 note = "use `with_stream` or `with_device` around `round`"
1898)]
1899pub fn round_device(
1900 a: impl AsRef<Array>,
1901 decimals: impl Into<Option<i32>>,
1902 #[optional] stream: impl AsRef<Stream>,
1903) -> Result<Array> {
1904 crate::with_stream(stream.as_ref(), || round(a, decimals))
1905}
1906
1907pub fn rsqrt(a: impl AsRef<Array>) -> Result<Array> {
1909 a.as_ref().rsqrt()
1910}
1911
1912#[generate_macro(customize(forwarding_shim = true))]
1914#[deprecated(
1915 since = "0.26.0",
1916 note = "use `with_stream` or `with_device` around `rsqrt`"
1917)]
1918pub fn rsqrt_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1919 crate::with_stream(stream.as_ref(), || rsqrt(a))
1920}
1921
1922pub fn sigmoid(a: impl AsRef<Array>) -> Result<Array> {
1928 let stream = Stream::thread_local_or_default();
1929 Array::try_from_op(|res| unsafe {
1930 mlx_sys::mlx_sigmoid(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1931 })
1932}
1933
1934#[generate_macro(customize(forwarding_shim = true))]
1936#[deprecated(
1937 since = "0.26.0",
1938 note = "use `with_stream` or `with_device` around `sigmoid`"
1939)]
1940pub fn sigmoid_device(
1941 a: impl AsRef<Array>,
1942 #[optional] stream: impl AsRef<Stream>,
1943) -> Result<Array> {
1944 crate::with_stream(stream.as_ref(), || sigmoid(a))
1945}
1946
1947pub fn sign(a: impl AsRef<Array>) -> Result<Array> {
1949 let stream = Stream::thread_local_or_default();
1950 Array::try_from_op(|res| unsafe {
1951 mlx_sys::mlx_sign(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1952 })
1953}
1954
1955#[generate_macro(customize(forwarding_shim = true))]
1957#[deprecated(
1958 since = "0.26.0",
1959 note = "use `with_stream` or `with_device` around `sign`"
1960)]
1961pub fn sign_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1962 crate::with_stream(stream.as_ref(), || sign(a))
1963}
1964
1965pub fn sin(a: impl AsRef<Array>) -> Result<Array> {
1967 a.as_ref().sin()
1968}
1969
1970#[generate_macro(customize(forwarding_shim = true))]
1972#[deprecated(
1973 since = "0.26.0",
1974 note = "use `with_stream` or `with_device` around `sin`"
1975)]
1976pub fn sin_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1977 crate::with_stream(stream.as_ref(), || sin(a))
1978}
1979
1980pub fn sinh(a: impl AsRef<Array>) -> Result<Array> {
1982 let stream = Stream::thread_local_or_default();
1983 Array::try_from_op(|res| unsafe {
1984 mlx_sys::mlx_sinh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1985 })
1986}
1987
1988#[generate_macro(customize(forwarding_shim = true))]
1990#[deprecated(
1991 since = "0.26.0",
1992 note = "use `with_stream` or `with_device` around `sinh`"
1993)]
1994pub fn sinh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1995 crate::with_stream(stream.as_ref(), || sinh(a))
1996}
1997
1998pub fn softmax_axes(
2004 a: impl AsRef<Array>,
2005 axes: &[i32],
2006 precise: impl Into<Option<bool>>,
2007) -> Result<Array> {
2008 let stream = Stream::thread_local_or_default();
2009 let precise = precise.into().unwrap_or(false);
2010 let s = stream.as_ref().as_ptr();
2011
2012 Array::try_from_op(|res| unsafe {
2013 mlx_sys::mlx_softmax_axes(
2014 res,
2015 a.as_ref().as_ptr(),
2016 axes.as_ptr(),
2017 axes.len(),
2018 precise,
2019 s,
2020 )
2021 })
2022}
2023
2024#[generate_macro(customize(forwarding_shim = true))]
2026#[deprecated(
2027 since = "0.26.0",
2028 note = "use `with_stream` or `with_device` around `softmax_axes`"
2029)]
2030pub fn softmax_axes_device(
2031 a: impl AsRef<Array>,
2032 axes: &[i32],
2033 precise: impl Into<Option<bool>>,
2034 #[optional] stream: impl AsRef<Stream>,
2035) -> Result<Array> {
2036 crate::with_stream(stream.as_ref(), || softmax_axes(a, axes, precise))
2037}
2038
2039pub fn softmax_axis(
2041 a: impl AsRef<Array>,
2042 axis: i32,
2043 precise: impl Into<Option<bool>>,
2044) -> Result<Array> {
2045 let stream = Stream::thread_local_or_default();
2046 let precise = precise.into().unwrap_or(false);
2047 let s = stream.as_ref().as_ptr();
2048
2049 Array::try_from_op(|res| unsafe {
2050 mlx_sys::mlx_softmax_axis(res, a.as_ref().as_ptr(), axis, precise, s)
2051 })
2052}
2053
2054#[generate_macro(customize(forwarding_shim = true))]
2056#[deprecated(
2057 since = "0.26.0",
2058 note = "use `with_stream` or `with_device` around `softmax_axis`"
2059)]
2060pub fn softmax_axis_device(
2061 a: impl AsRef<Array>,
2062 axis: i32,
2063 precise: impl Into<Option<bool>>,
2064 #[optional] stream: impl AsRef<Stream>,
2065) -> Result<Array> {
2066 crate::with_stream(stream.as_ref(), || softmax_axis(a, axis, precise))
2067}
2068
2069pub fn softmax(a: impl AsRef<Array>, precise: impl Into<Option<bool>>) -> Result<Array> {
2071 let stream = Stream::thread_local_or_default();
2072 let precise = precise.into().unwrap_or(false);
2073 let s = stream.as_ref().as_ptr();
2074
2075 Array::try_from_op(|res| unsafe { mlx_sys::mlx_softmax(res, a.as_ref().as_ptr(), precise, s) })
2076}
2077
2078#[generate_macro(customize(forwarding_shim = true))]
2080#[deprecated(
2081 since = "0.26.0",
2082 note = "use `with_stream` or `with_device` around `softmax`"
2083)]
2084pub fn softmax_device(
2085 a: impl AsRef<Array>,
2086 precise: impl Into<Option<bool>>,
2087 #[optional] stream: impl AsRef<Stream>,
2088) -> Result<Array> {
2089 crate::with_stream(stream.as_ref(), || softmax(a, precise))
2090}
2091
2092pub fn sqrt(a: impl AsRef<Array>) -> Result<Array> {
2094 a.as_ref().sqrt()
2095}
2096
2097#[generate_macro(customize(forwarding_shim = true))]
2099#[deprecated(
2100 since = "0.26.0",
2101 note = "use `with_stream` or `with_device` around `sqrt`"
2102)]
2103pub fn sqrt_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2104 crate::with_stream(stream.as_ref(), || sqrt(a))
2105}
2106
2107pub fn square(a: impl AsRef<Array>) -> Result<Array> {
2109 a.as_ref().square()
2110}
2111
2112#[generate_macro(customize(forwarding_shim = true))]
2114#[deprecated(
2115 since = "0.26.0",
2116 note = "use `with_stream` or `with_device` around `square`"
2117)]
2118pub fn square_device(
2119 a: impl AsRef<Array>,
2120 #[optional] stream: impl AsRef<Stream>,
2121) -> Result<Array> {
2122 crate::with_stream(stream.as_ref(), || square(a))
2123}
2124
2125pub fn subtract(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
2127 a.as_ref().subtract(b)
2128}
2129
2130#[generate_macro(customize(forwarding_shim = true))]
2132#[deprecated(
2133 since = "0.26.0",
2134 note = "use `with_stream` or `with_device` around `subtract`"
2135)]
2136pub fn subtract_device(
2137 a: impl AsRef<Array>,
2138 b: impl AsRef<Array>,
2139 #[optional] stream: impl AsRef<Stream>,
2140) -> Result<Array> {
2141 crate::with_stream(stream.as_ref(), || subtract(a, b))
2142}
2143
2144pub fn tan(a: impl AsRef<Array>) -> Result<Array> {
2146 let stream = Stream::thread_local_or_default();
2147 Array::try_from_op(|res| unsafe {
2148 mlx_sys::mlx_tan(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2149 })
2150}
2151
2152#[generate_macro(customize(forwarding_shim = true))]
2154#[deprecated(
2155 since = "0.26.0",
2156 note = "use `with_stream` or `with_device` around `tan`"
2157)]
2158pub fn tan_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2159 crate::with_stream(stream.as_ref(), || tan(a))
2160}
2161
2162pub fn tanh(a: impl AsRef<Array>) -> Result<Array> {
2164 let stream = Stream::thread_local_or_default();
2165 Array::try_from_op(|res| unsafe {
2166 mlx_sys::mlx_tanh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2167 })
2168}
2169
2170#[generate_macro(customize(forwarding_shim = true))]
2172#[deprecated(
2173 since = "0.26.0",
2174 note = "use `with_stream` or `with_device` around `tanh`"
2175)]
2176pub fn tanh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2177 crate::with_stream(stream.as_ref(), || tanh(a))
2178}
2179
2180pub fn real(a: impl AsRef<Array>) -> Result<Array> {
2182 let stream = Stream::thread_local_or_default();
2183 Array::try_from_op(|res| unsafe {
2184 mlx_sys::mlx_real(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2185 })
2186}
2187
2188#[generate_macro(customize(forwarding_shim = true))]
2190#[deprecated(
2191 since = "0.26.0",
2192 note = "use `with_stream` or `with_device` around `real`"
2193)]
2194pub fn real_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2195 crate::with_stream(stream.as_ref(), || real(a))
2196}
2197
2198pub fn imag(a: impl AsRef<Array>) -> Result<Array> {
2200 let stream = Stream::thread_local_or_default();
2201 Array::try_from_op(|res| unsafe {
2202 mlx_sys::mlx_imag(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2203 })
2204}
2205
2206#[generate_macro(customize(forwarding_shim = true))]
2208#[deprecated(
2209 since = "0.26.0",
2210 note = "use `with_stream` or `with_device` around `imag`"
2211)]
2212pub fn imag_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2213 crate::with_stream(stream.as_ref(), || imag(a))
2214}
2215
2216pub fn block_masked_mm<'mo, 'lhs, 'rhs>(
2222 a: impl AsRef<Array>,
2223 b: impl AsRef<Array>,
2224 block_size: impl Into<Option<i32>>,
2225 mask_out: impl Into<Option<&'mo Array>>,
2226 mask_lhs: impl Into<Option<&'lhs Array>>,
2227 mask_rhs: impl Into<Option<&'rhs Array>>,
2228) -> Result<Array> {
2229 let stream = Stream::thread_local_or_default();
2230 let a_ptr = a.as_ref().as_ptr();
2231 let b_ptr = b.as_ref().as_ptr();
2232 unsafe {
2233 let mask_out_ptr = mask_out
2234 .into()
2235 .map(|m| m.as_ptr())
2236 .unwrap_or(mlx_sys::mlx_array_new());
2237 let mask_lhs_ptr = mask_lhs
2238 .into()
2239 .map(|m| m.as_ptr())
2240 .unwrap_or(mlx_sys::mlx_array_new());
2241 let mask_rhs_ptr = mask_rhs
2242 .into()
2243 .map(|m| m.as_ptr())
2244 .unwrap_or(mlx_sys::mlx_array_new());
2245
2246 Array::try_from_op(|res| {
2247 mlx_sys::mlx_block_masked_mm(
2248 res,
2249 a_ptr,
2250 b_ptr,
2251 block_size.into().unwrap_or(32),
2252 mask_out_ptr,
2253 mask_lhs_ptr,
2254 mask_rhs_ptr,
2255 stream.as_ref().as_ptr(),
2256 )
2257 })
2258 }
2259}
2260
2261#[generate_macro(customize(forwarding_shim = true))]
2263#[deprecated(
2264 since = "0.26.0",
2265 note = "use `with_stream` or `with_device` around `block_masked_mm`"
2266)]
2267pub fn block_masked_mm_device<'mo, 'lhs, 'rhs>(
2268 a: impl AsRef<Array>,
2269 b: impl AsRef<Array>,
2270 #[optional] block_size: impl Into<Option<i32>>,
2271 #[optional] mask_out: impl Into<Option<&'mo Array>>,
2272 #[optional] mask_lhs: impl Into<Option<&'lhs Array>>,
2273 #[optional] mask_rhs: impl Into<Option<&'rhs Array>>,
2274 #[optional] stream: impl AsRef<Stream>,
2275) -> Result<Array> {
2276 crate::with_stream(stream.as_ref(), || {
2277 block_masked_mm(a, b, block_size, mask_out, mask_lhs, mask_rhs)
2278 })
2279}
2280
2281pub fn addmm(
2294 c: impl AsRef<Array>,
2295 a: impl AsRef<Array>,
2296 b: impl AsRef<Array>,
2297 alpha: impl Into<Option<f32>>,
2298 beta: impl Into<Option<f32>>,
2299) -> Result<Array> {
2300 let stream = Stream::thread_local_or_default();
2301 let c_ptr = c.as_ref().as_ptr();
2302 let a_ptr = a.as_ref().as_ptr();
2303 let b_ptr = b.as_ref().as_ptr();
2304 let alpha = alpha.into().unwrap_or(1.0);
2305 let beta = beta.into().unwrap_or(1.0);
2306
2307 Array::try_from_op(|res| unsafe {
2308 mlx_sys::mlx_addmm(
2309 res,
2310 c_ptr,
2311 a_ptr,
2312 b_ptr,
2313 alpha,
2314 beta,
2315 stream.as_ref().as_ptr(),
2316 )
2317 })
2318}
2319
2320#[generate_macro(customize(forwarding_shim = true))]
2322#[deprecated(
2323 since = "0.26.0",
2324 note = "use `with_stream` or `with_device` around `addmm`"
2325)]
2326pub fn addmm_device(
2327 c: impl AsRef<Array>,
2328 a: impl AsRef<Array>,
2329 b: impl AsRef<Array>,
2330 #[optional] alpha: impl Into<Option<f32>>,
2331 #[optional] beta: impl Into<Option<f32>>,
2332 #[optional] stream: impl AsRef<Stream>,
2333) -> Result<Array> {
2334 crate::with_stream(stream.as_ref(), || addmm(c, a, b, alpha, beta))
2335}
2336
2337pub fn inner(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
2340 let stream = Stream::thread_local_or_default();
2341 let a = a.as_ref();
2342 let b = b.as_ref();
2343 Array::try_from_op(|res| unsafe {
2344 mlx_sys::mlx_inner(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
2345 })
2346}
2347
2348#[generate_macro(customize(forwarding_shim = true))]
2350#[deprecated(
2351 since = "0.26.0",
2352 note = "use `with_stream` or `with_device` around `inner`"
2353)]
2354pub fn inner_device(
2355 a: impl AsRef<Array>,
2356 b: impl AsRef<Array>,
2357 #[optional] stream: impl AsRef<Stream>,
2358) -> Result<Array> {
2359 crate::with_stream(stream.as_ref(), || inner(a, b))
2360}
2361
2362pub fn outer(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
2365 let stream = Stream::thread_local_or_default();
2366 let a = a.as_ref();
2367 let b = b.as_ref();
2368 Array::try_from_op(|res| unsafe {
2369 mlx_sys::mlx_outer(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
2370 })
2371}
2372
2373#[generate_macro(customize(forwarding_shim = true))]
2375#[deprecated(
2376 since = "0.26.0",
2377 note = "use `with_stream` or `with_device` around `outer`"
2378)]
2379pub fn outer_device(
2380 a: impl AsRef<Array>,
2381 b: impl AsRef<Array>,
2382 #[optional] stream: impl AsRef<Stream>,
2383) -> Result<Array> {
2384 crate::with_stream(stream.as_ref(), || outer(a, b))
2385}
2386
2387pub fn tensordot_axes(
2389 a: impl AsRef<Array>,
2390 b: impl AsRef<Array>,
2391 axes_a: &[i32],
2392 axes_b: &[i32],
2393) -> Result<Array> {
2394 let stream = Stream::thread_local_or_default();
2395 let a = a.as_ref();
2396 let b = b.as_ref();
2397 Array::try_from_op(|res| unsafe {
2398 mlx_sys::mlx_tensordot(
2399 res,
2400 a.as_ptr(),
2401 b.as_ptr(),
2402 axes_a.as_ptr(),
2403 axes_a.len(),
2404 axes_b.as_ptr(),
2405 axes_b.len(),
2406 stream.as_ref().as_ptr(),
2407 )
2408 })
2409}
2410
2411#[generate_macro(customize(forwarding_shim = true))]
2413#[deprecated(
2414 since = "0.26.0",
2415 note = "use `with_stream` or `with_device` around `tensordot_axes`"
2416)]
2417pub fn tensordot_axes_device(
2418 a: impl AsRef<Array>,
2419 b: impl AsRef<Array>,
2420 axes_a: &[i32],
2421 axes_b: &[i32],
2422 #[optional] stream: impl AsRef<Stream>,
2423) -> Result<Array> {
2424 crate::with_stream(stream.as_ref(), || tensordot_axes(a, b, axes_a, axes_b))
2425}
2426
2427pub fn tensordot_axis(a: impl AsRef<Array>, b: impl AsRef<Array>, axis: i32) -> Result<Array> {
2429 let stream = Stream::thread_local_or_default();
2430 let a = a.as_ref();
2431 let b = b.as_ref();
2432 Array::try_from_op(|res| unsafe {
2433 mlx_sys::mlx_tensordot_axis(res, a.as_ptr(), b.as_ptr(), axis, stream.as_ref().as_ptr())
2434 })
2435}
2436
2437#[generate_macro(customize(forwarding_shim = true))]
2439#[deprecated(
2440 since = "0.26.0",
2441 note = "use `with_stream` or `with_device` around `tensordot_axis`"
2442)]
2443pub fn tensordot_axis_device(
2444 a: impl AsRef<Array>,
2445 b: impl AsRef<Array>,
2446 axis: i32,
2447 #[optional] stream: impl AsRef<Stream>,
2448) -> Result<Array> {
2449 crate::with_stream(stream.as_ref(), || tensordot_axis(a, b, axis))
2450}
2451
2452pub fn gather_mm<'lhs, 'rhs>(
2480 a: impl AsRef<Array>,
2481 b: impl AsRef<Array>,
2482 lhs_indices: impl Into<Option<&'lhs Array>>,
2483 rhs_indices: impl Into<Option<&'rhs Array>>,
2484 sorted_indices: impl Into<Option<bool>>,
2485) -> Result<Array> {
2486 let stream = Stream::thread_local_or_default();
2487 let a_ptr = a.as_ref().as_ptr();
2488 let b_ptr = b.as_ref().as_ptr();
2489 let sorted = sorted_indices.into().unwrap_or(false);
2490
2491 unsafe {
2492 let lhs_ptr = lhs_indices
2493 .into()
2494 .map(|i| i.as_ptr())
2495 .unwrap_or(mlx_sys::mlx_array_new());
2496 let rhs_ptr = rhs_indices
2497 .into()
2498 .map(|i| i.as_ptr())
2499 .unwrap_or(mlx_sys::mlx_array_new());
2500
2501 Array::try_from_op(|res| {
2502 mlx_sys::mlx_gather_mm(
2503 res,
2504 a_ptr,
2505 b_ptr,
2506 lhs_ptr,
2507 rhs_ptr,
2508 sorted,
2509 stream.as_ref().as_ptr(),
2510 )
2511 })
2512 }
2513}
2514
2515#[generate_macro(customize(forwarding_shim = true))]
2517#[deprecated(
2518 since = "0.26.0",
2519 note = "use `with_stream` or `with_device` around `gather_mm`"
2520)]
2521pub fn gather_mm_device<'lhs, 'rhs>(
2522 a: impl AsRef<Array>,
2523 b: impl AsRef<Array>,
2524 #[optional] lhs_indices: impl Into<Option<&'lhs Array>>,
2525 #[optional] rhs_indices: impl Into<Option<&'rhs Array>>,
2526 #[optional] sorted_indices: impl Into<Option<bool>>,
2527 #[optional] stream: impl AsRef<Stream>,
2528) -> Result<Array> {
2529 crate::with_stream(stream.as_ref(), || {
2530 gather_mm(a, b, lhs_indices, rhs_indices, sorted_indices)
2531 })
2532}
2533
2534#[cfg(test)]
2535mod tests {
2536 use std::f32::consts::PI;
2537
2538 use super::*;
2539 use crate::{
2540 array, complex64,
2541 ops::{all_close, arange, broadcast_to, eye, full, linspace, ones, reshape, split_equal},
2542 test_utils::{assert_array_eq, assert_array_eq_with_context, tolerances},
2543 transforms::eval,
2544 Dtype,
2545 };
2546 use float_eq::assert_float_eq;
2547 use pretty_assertions::assert_eq;
2548
2549 #[test]
2550 fn test_abs() {
2551 let data = [1i32, 2, -3, -4, -5];
2552 let array = Array::from_slice(&data, &[5]);
2553 let result = array.abs().unwrap();
2554
2555 let data: &[i32] = result.as_slice();
2556 assert_eq!(data, [1, 2, 3, 4, 5]);
2557
2558 let data: &[i32] = array.as_slice();
2560 assert_eq!(data, [1, 2, -3, -4, -5]);
2561 }
2562
2563 #[test]
2564 fn test_add() {
2565 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2566 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2567
2568 let c = &a + &b;
2569
2570 let c_data: &[f32] = c.as_slice();
2571 assert_eq!(c_data, &[5.0, 7.0, 9.0]);
2572
2573 let a_data: &[f32] = a.as_slice();
2575 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2576
2577 let b_data: &[f32] = b.as_slice();
2578 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2579 }
2580
2581 #[test]
2582 fn test_add_invalid_broadcast() {
2583 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2584 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2585
2586 let c = a.add(&b);
2587 assert!(c.is_err());
2588 }
2589
2590 #[test]
2591 fn test_sub() {
2592 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2593 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2594
2595 let c = &a - &b;
2596
2597 let c_data: &[f32] = c.as_slice();
2598 assert_eq!(c_data, &[-3.0, -3.0, -3.0]);
2599
2600 let a_data: &[f32] = a.as_slice();
2602 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2603
2604 let b_data: &[f32] = b.as_slice();
2605 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2606 }
2607
2608 #[test]
2609 fn test_sub_invalid_broadcast() {
2610 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2611 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2612 let c = a.subtract(&b);
2613 assert!(c.is_err());
2614 }
2615
2616 #[test]
2617 fn test_neg() {
2618 let a = Array::from_slice::<f32>(&[1.0, 2.0, 3.0], &[3]);
2619 let b = a.negative().unwrap();
2620
2621 let b_data: &[f32] = b.as_slice();
2622 assert_eq!(b_data, &[-1.0, -2.0, -3.0]);
2623
2624 let a_data: &[f32] = a.as_slice();
2626 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2627 }
2628
2629 #[test]
2630 fn test_neg_bool() {
2631 let a = Array::from_slice(&[true, false, true], &[3]);
2632 let b = a.negative();
2633 assert!(b.is_err());
2634 }
2635
2636 #[test]
2637 fn test_logical_not() {
2638 let a: Array = false.into();
2639 let b = a.logical_not().unwrap();
2640
2641 let b_data: &[bool] = b.as_slice();
2642 assert_eq!(b_data, [true]);
2643 }
2644
2645 #[test]
2646 fn test_mul() {
2647 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2648 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2649
2650 let c = &a * &b;
2651
2652 let c_data: &[f32] = c.as_slice();
2653 assert_eq!(c_data, &[4.0, 10.0, 18.0]);
2654
2655 let a_data: &[f32] = a.as_slice();
2657 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2658
2659 let b_data: &[f32] = b.as_slice();
2660 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2661 }
2662
2663 #[test]
2664 fn test_mul_invalid_broadcast() {
2665 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2666 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2667 let c = a.multiply(&b);
2668 assert!(c.is_err());
2669 }
2670
2671 #[test]
2672 fn test_nan_to_num() {
2673 let a = array!([1.0, 2.0, f32::NAN, 4.0, 5.0]);
2674 let b = a.nan_to_num(0.0, 1.0, 0.0).unwrap();
2675
2676 let b_data: &[f32] = b.as_slice();
2677 assert_eq!(b_data, &[1.0, 2.0, 0.0, 4.0, 5.0]);
2678 }
2679
2680 #[test]
2681 fn test_div() {
2682 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2683 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2684
2685 let c = &a / &b;
2686
2687 let c_data: &[f32] = c.as_slice();
2688 assert_eq!(c_data, &[0.25, 0.4, 0.5]);
2689
2690 let a_data: &[f32] = a.as_slice();
2692 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2693
2694 let b_data: &[f32] = b.as_slice();
2695 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2696 }
2697
2698 #[test]
2699 fn test_div_invalid_broadcast() {
2700 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2701 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2702 let c = a.divide(&b);
2703 assert!(c.is_err());
2704 }
2705
2706 #[test]
2707 fn test_pow() {
2708 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2709 let b = Array::from_slice(&[2.0, 3.0, 4.0], &[3]);
2710
2711 let c = a.power(&b).unwrap();
2712
2713 let c_data: &[f32] = c.as_slice();
2714 assert_eq!(c_data, &[1.0, 8.0, 81.0]);
2715
2716 let a_data: &[f32] = a.as_slice();
2718 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2719
2720 let b_data: &[f32] = b.as_slice();
2721 assert_eq!(b_data, &[2.0, 3.0, 4.0]);
2722 }
2723
2724 #[test]
2725 fn test_pow_invalid_broadcast() {
2726 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2727 let b = Array::from_slice(&[2.0, 3.0], &[2]);
2728 let c = a.power(&b);
2729 assert!(c.is_err());
2730 }
2731
2732 #[test]
2733 fn test_rem() {
2734 let a = Array::from_slice(&[10.0, 11.0, 12.0], &[3]);
2735 let b = Array::from_slice(&[3.0, 4.0, 5.0], &[3]);
2736
2737 let c = &a % &b;
2738
2739 let c_data: &[f32] = c.as_slice();
2740 assert_eq!(c_data, &[1.0, 3.0, 2.0]);
2741
2742 let a_data: &[f32] = a.as_slice();
2744 assert_eq!(a_data, &[10.0, 11.0, 12.0]);
2745
2746 let b_data: &[f32] = b.as_slice();
2747 assert_eq!(b_data, &[3.0, 4.0, 5.0]);
2748 }
2749
2750 #[test]
2751 fn test_rem_invalid_broadcast() {
2752 let a = Array::from_slice(&[10.0, 11.0, 12.0], &[3]);
2753 let b = Array::from_slice(&[3.0, 4.0], &[2]);
2754 let c = a.remainder(&b);
2755 assert!(c.is_err());
2756 }
2757
2758 #[test]
2759 fn test_sqrt() {
2760 let a = Array::from_slice(&[1.0, 4.0, 9.0], &[3]);
2761 let b = a.sqrt().unwrap();
2762
2763 let b_data: &[f32] = b.as_slice();
2764 assert_eq!(b_data, &[1.0, 2.0, 3.0]);
2765
2766 let a_data: &[f32] = a.as_slice();
2768 assert_eq!(a_data, &[1.0, 4.0, 9.0]);
2769 }
2770
2771 #[test]
2772 fn test_cos() {
2773 let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
2774 let b = a.cos().unwrap();
2775
2776 let b_expected = array!([1.0, 0.54030234, -0.41614687]);
2777 assert_array_all_close!(b, b_expected);
2778
2779 let a_expected = array!([0.0, 1.0, 2.0]);
2781 assert_array_all_close!(a, a_expected);
2782 }
2783
2784 #[test]
2785 fn test_exp() {
2786 let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
2787 let b = a.exp().unwrap();
2788
2789 let b_expected = array!([1.0, 2.7182817, 7.389056]);
2790 assert_array_all_close!(b, b_expected);
2791
2792 let a_expected = array!([0.0, 1.0, 2.0]);
2794 assert_array_all_close!(a, a_expected);
2795 }
2796
2797 #[test]
2798 fn test_floor() {
2799 let a = Array::from_slice(&[0.1, 1.9, 2.5], &[3]);
2800 let b = a.floor().unwrap();
2801
2802 let b_data: &[f32] = b.as_slice();
2803 assert_eq!(b_data, &[0.0, 1.0, 2.0]);
2804
2805 let a_data: &[f32] = a.as_slice();
2807 assert_eq!(a_data, &[0.1, 1.9, 2.5]);
2808 }
2809
2810 #[test]
2811 fn test_floor_complex64() {
2812 let val = complex64::new(1.0, 2.0);
2813 let a = Array::from_complex(val);
2814 let b = a.floor();
2815 assert!(b.is_err());
2816 }
2817
2818 #[test]
2819 fn test_floor_divide() {
2820 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2821 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2822
2823 let c = a.floor_divide(&b).unwrap();
2824
2825 let c_data: &[f32] = c.as_slice();
2826 assert_eq!(c_data, &[0.0, 0.0, 0.0]);
2827
2828 let a_data: &[f32] = a.as_slice();
2830 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2831
2832 let b_data: &[f32] = b.as_slice();
2833 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2834 }
2835
2836 #[test]
2837 fn test_floor_divide_complex64() {
2838 let val = complex64::new(1.0, 2.0);
2839 let a = Array::from_complex(val);
2840 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2841 let c = a.floor_divide(&b);
2842 assert!(c.is_err());
2843 }
2844
2845 #[test]
2846 fn test_floor_divide_invalid_broadcast() {
2847 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2848 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2849 let c = a.floor_divide(&b);
2850 assert!(c.is_err());
2851 }
2852
2853 #[test]
2854 fn test_is_nan() {
2855 let a = Array::from_slice(&[1.0, f32::NAN, 3.0], &[3]);
2856 let b = a.is_nan().unwrap();
2857
2858 let b_data: &[bool] = b.as_slice();
2859 assert_eq!(b_data, &[false, true, false]);
2860 }
2861
2862 #[test]
2863 fn test_is_inf() {
2864 let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
2865 let b = a.is_inf().unwrap();
2866
2867 let b_data: &[bool] = b.as_slice();
2868 assert_eq!(b_data, &[false, true, false]);
2869 }
2870
2871 #[test]
2872 fn test_is_finite() {
2873 let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
2874 let b = a.is_finite().unwrap();
2875
2876 let b_data: &[bool] = b.as_slice();
2877 assert_eq!(b_data, &[true, false, true]);
2878 }
2879
2880 #[test]
2881 fn test_is_neg_inf() {
2882 let a = Array::from_slice(&[1.0, f32::NEG_INFINITY, 3.0], &[3]);
2883 let b = a.is_neg_inf().unwrap();
2884
2885 let b_data: &[bool] = b.as_slice();
2886 assert_eq!(b_data, &[false, true, false]);
2887 }
2888
2889 #[test]
2890 fn test_is_pos_inf() {
2891 let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
2892 let b = a.is_pos_inf().unwrap();
2893
2894 let b_data: &[bool] = b.as_slice();
2895 assert_eq!(b_data, &[false, true, false]);
2896 }
2897
2898 #[test]
2899 fn test_log() {
2900 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2901 let b = a.log().unwrap();
2902
2903 let b_data: &[f32] = b.as_slice();
2904 assert_eq!(b_data, &[0.0, 0.6931472, 1.0986123]);
2905
2906 let a_data: &[f32] = a.as_slice();
2908 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2909 }
2910
2911 #[test]
2912 fn test_log2() {
2913 let a = Array::from_slice(&[1.0, 2.0, 4.0, 8.0], &[4]);
2914 let b = a.log2().unwrap();
2915
2916 let b_data: &[f32] = b.as_slice();
2917 assert_eq!(b_data, &[0.0, 1.0, 2.0, 3.0]);
2918
2919 let a_data: &[f32] = a.as_slice();
2921 assert_eq!(a_data, &[1.0, 2.0, 4.0, 8.0]);
2922 }
2923
2924 #[test]
2925 fn test_log10() {
2926 let a = Array::from_slice(&[1.0, 10.0, 100.0], &[3]);
2927 let b = a.log10().unwrap();
2928
2929 let b_data: &[f32] = b.as_slice();
2930 assert_eq!(b_data, &[0.0, 1.0, 2.0]);
2931
2932 let a_data: &[f32] = a.as_slice();
2934 assert_eq!(a_data, &[1.0, 10.0, 100.0]);
2935 }
2936
2937 #[test]
2938 fn test_log1p() {
2939 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2940 let b = a.log1p().unwrap();
2941
2942 let b_data: &[f32] = b.as_slice();
2943 assert_eq!(b_data, &[0.6931472, 1.0986123, 1.3862944]);
2944
2945 let a_data: &[f32] = a.as_slice();
2947 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2948 }
2949
2950 #[test]
2951 fn test_matmul() {
2952 let a = Array::from_slice(&[1, 2, 3, 4], &[2, 2]);
2953 let b = Array::from_slice(&[-5.0, 37.5, 4., 7., 1., 0.], &[2, 3]);
2954
2955 let c = a.matmul(&b).unwrap();
2956
2957 assert_eq!(c.shape(), &[2, 3]);
2958 let c_data: &[f32] = c.as_slice();
2959 assert_eq!(c_data, &[9.0, 39.5, 4.0, 13.0, 116.5, 12.0]);
2960
2961 let a_data: &[i32] = a.as_slice();
2963 assert_eq!(a_data, &[1, 2, 3, 4]);
2964
2965 let b_data: &[f32] = b.as_slice();
2966 assert_eq!(b_data, &[-5.0, 37.5, 4., 7., 1., 0.]);
2967 }
2968
2969 #[test]
2970 fn test_matmul_ndim_zero() {
2971 let a: Array = 1.0.into();
2972 let b = Array::from_slice::<i32>(&[1], &[1]);
2973 let c = a.matmul(&b);
2974 assert!(c.is_err());
2975 }
2976
2977 #[test]
2978 fn test_matmul_ndim_one() {
2979 let a = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4]);
2980 let b = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4]);
2981 let c = a.matmul(&b);
2982 assert!(c.is_ok());
2983 }
2984
2985 #[test]
2986 fn test_matmul_dim_mismatch() {
2987 let a = Array::from_slice(&[1, 2, 3, 4, 5, 6], &[2, 3]);
2988 let b = Array::from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], &[2, 5]);
2989 let c = a.matmul(&b);
2990 assert!(c.is_err());
2991 }
2992
2993 #[test]
2994 fn test_matmul_non_float_output_type() {
2995 let a = Array::from_slice(&[1, 2, 3, 4], &[2, 2]);
2996 let b = Array::from_slice(&[5, 37, 4, 7, 1, 0], &[2, 3]);
2997
2998 let c = a.matmul(&b);
2999 assert!(c.is_err());
3000 }
3001
3002 #[test]
3003 fn test_reciprocal() {
3004 let a = Array::from_slice(&[1.0, 2.0, 4.0], &[3]);
3005 let b = a.reciprocal().unwrap();
3006
3007 let b_data: &[f32] = b.as_slice();
3008 assert_eq!(b_data, &[1.0, 0.5, 0.25]);
3009
3010 let a_data: &[f32] = a.as_slice();
3012 assert_eq!(a_data, &[1.0, 2.0, 4.0]);
3013 }
3014
3015 #[test]
3016 fn test_round() {
3017 let a = Array::from_slice(&[1.1, 2.9, 3.5], &[3]);
3018 let b = a.round(None).unwrap();
3019
3020 let b_data: &[f32] = b.as_slice();
3021 assert_eq!(b_data, &[1.0, 3.0, 4.0]);
3022
3023 let a_data: &[f32] = a.as_slice();
3025 assert_eq!(a_data, &[1.1, 2.9, 3.5]);
3026 }
3027
3028 #[test]
3029 fn test_rsqrt() {
3030 let a = Array::from_slice(&[1.0, 2.0, 4.0], &[3]);
3031 let b = a.rsqrt().unwrap();
3032
3033 let b_data: &[f32] = b.as_slice();
3034 assert_eq!(b_data, &[1.0, 0.70710677, 0.5]);
3035
3036 let a_data: &[f32] = a.as_slice();
3038 assert_eq!(a_data, &[1.0, 2.0, 4.0]);
3039 }
3040
3041 #[test]
3042 fn test_sin() {
3043 let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
3044 let b = a.sin().unwrap();
3045
3046 let b_data: &[f32] = b.as_slice();
3047 assert_eq!(b_data, &[0.0, 0.841471, 0.9092974]);
3048
3049 let a_data: &[f32] = a.as_slice();
3051 assert_eq!(a_data, &[0.0, 1.0, 2.0]);
3052 }
3053
3054 #[test]
3055 fn test_square() {
3056 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
3057 let b = a.square().unwrap();
3058
3059 let b_data: &[f32] = b.as_slice();
3060 assert_eq!(b_data, &[1.0, 4.0, 9.0]);
3061
3062 let a_data: &[f32] = a.as_slice();
3064 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
3065 }
3066
3067 #[test]
3070 fn test_unary_neg() {
3071 let x = array!(1.0);
3072 assert_eq!(negative(&x).unwrap().item_exact::<f32>(), -1.0);
3073 assert_eq!((-x).item_exact::<f32>(), -1.0);
3074
3075 assert_array_eq(
3077 -array!(),
3078 array!(),
3079 tolerances::EXACT.rtol,
3080 tolerances::EXACT.atol,
3081 );
3082
3083 let x = array!(true);
3085 assert!(negative(&x).is_err());
3086 }
3087
3088 #[test]
3089 fn test_unary_abs() {
3090 let x = array!([-1.0, 0.0, 1.0]);
3091 assert_array_eq(
3092 abs(&x).unwrap(),
3093 array!([1.0, 0.0, 1.0]),
3094 tolerances::EXACT.rtol,
3095 tolerances::EXACT.atol,
3096 );
3097
3098 assert_array_eq(
3100 abs(array!()).unwrap(),
3101 array!(),
3102 tolerances::EXACT.rtol,
3103 tolerances::EXACT.atol,
3104 );
3105
3106 let x = array!([-1, 0, 1]);
3108 assert_array_eq(
3109 abs(&x).unwrap(),
3110 array!([1, 0, 1]),
3111 tolerances::EXACT.rtol,
3112 tolerances::EXACT.atol,
3113 );
3114
3115 let x = array!([1u32, 0, 1]);
3117 assert_array_eq(
3118 abs(&x).unwrap(),
3119 array!([1u32, 0, 1]),
3120 tolerances::EXACT.rtol,
3121 tolerances::EXACT.atol,
3122 );
3123
3124 let x = array!([false, true]);
3126 assert_array_eq(
3127 abs(&x).unwrap(),
3128 array!([false, true]),
3129 tolerances::EXACT.rtol,
3130 tolerances::EXACT.atol,
3131 );
3132 }
3133
3134 #[test]
3135 fn test_unary_sign() {
3136 let x = array!([-1.0, 0.0, 1.0]);
3137 assert_array_eq(
3138 sign(&x).unwrap(),
3139 x,
3140 tolerances::EXACT.rtol,
3141 tolerances::EXACT.atol,
3142 );
3143
3144 assert_array_eq(
3146 sign(array!()).unwrap(),
3147 array!(),
3148 tolerances::EXACT.rtol,
3149 tolerances::EXACT.atol,
3150 );
3151
3152 let x = array!([-1, 0, 1]);
3154 assert_array_eq(
3155 sign(&x).unwrap(),
3156 x,
3157 tolerances::EXACT.rtol,
3158 tolerances::EXACT.atol,
3159 );
3160
3161 let x = array!([1u32, 0, 1]);
3163 assert_array_eq(
3164 sign(&x).unwrap(),
3165 x,
3166 tolerances::EXACT.rtol,
3167 tolerances::EXACT.atol,
3168 );
3169
3170 let x = array!([false, true]);
3172 assert_array_eq(
3173 sign(&x).unwrap(),
3174 x,
3175 tolerances::EXACT.rtol,
3176 tolerances::EXACT.atol,
3177 );
3178 }
3179
3180 const NEG_INF: f32 = f32::NEG_INFINITY;
3181
3182 #[test]
3183 fn test_unary_floor_ceil() {
3184 let x = array![1.0];
3185 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), 1.0);
3186 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), 1.0);
3187
3188 let x = array![1.5];
3189 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), 1.0);
3190 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), 2.0);
3191
3192 let x = array![-1.5];
3193 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), -2.0);
3194 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), -1.0);
3195
3196 let x = array![NEG_INF];
3197 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), NEG_INF);
3198 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), NEG_INF);
3199
3200 let x = array!([1.0, 1.0]).as_type::<complex64>().unwrap();
3201 assert!(floor(&x).is_err());
3202 assert!(ceil(&x).is_err());
3203 }
3204
3205 #[test]
3206 fn test_unary_round() {
3207 let x = array!([0.5, -0.5, 1.5, -1.5, 2.3, 2.6]);
3208 assert_array_eq_with_context(
3209 round(&x, None).unwrap(),
3210 array!([0.0_f32, 0.0, 2.0, -2.0, 2.0, 3.0]),
3211 tolerances::EXACT.rtol,
3212 tolerances::EXACT.atol,
3213 "float32 half-to-even rounding",
3214 );
3215
3216 let x = array!([11, 222, 32]);
3217 assert_array_eq_with_context(
3218 round(&x, -1).unwrap(),
3219 array!([10, 220, 30]),
3220 tolerances::EXACT.rtol,
3221 tolerances::EXACT.atol,
3222 "int32 negative-decimal rounding",
3223 );
3224 }
3225
3226 #[test]
3227 fn test_unary_exp() {
3228 let x = array![0.0];
3229 assert_eq!(exp(&x).unwrap().item_exact::<f32>(), 1.0);
3230
3231 let x = array![2.0];
3232 assert_float_eq! {
3233 exp(&x).unwrap().item_exact::<f32>(),
3234 2.0f32.exp(),
3235 abs <= 1e-5
3236 };
3237
3238 assert_array_eq(
3239 exp(array!()).unwrap(),
3240 array!(),
3241 tolerances::EXACT.rtol,
3242 tolerances::EXACT.atol,
3243 );
3244
3245 let x = array![NEG_INF];
3246 assert_eq!(exp(&x).unwrap().item_exact::<f32>(), 0.0);
3247
3248 let x = array![2];
3250 assert_eq!(x.dtype(), Dtype::Int32);
3251 assert_float_eq! {
3252 exp(&x).unwrap().item_exact::<f32>(),
3253 2.0f32.exp(),
3254 abs <= 1e-5
3255 };
3256
3257 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3259 let res = exp(&x).unwrap();
3260 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.exp())).unwrap();
3261 assert!(all_close(&res, &expected, None, None, None).unwrap());
3262
3263 let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
3264 let x = split_equal(&data, 2, 1).unwrap();
3265 let expected = Array::from_slice(&[0.0f32.exp(), 2.0f32.exp()], &[2, 1]);
3266 assert!(all_close(exp(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3267 }
3268
3269 #[test]
3270 fn test_unary_expm1() {
3271 let x = array![-1.0];
3272 assert_float_eq! {
3273 expm1(&x).unwrap().item_exact::<f32>(),
3274 (-1.0f32).exp_m1(),
3275 abs <= 1e-5
3276 };
3277
3278 let x = array![1.0];
3279 assert_float_eq! {
3280 expm1(&x).unwrap().item_exact::<f32>(),
3281 1.0f32.exp_m1(),
3282 abs <= 1e-5
3283 };
3284
3285 let x = array![1];
3287 assert_eq!(expm1(&x).unwrap().dtype(), Dtype::Float32);
3288 assert_float_eq! {
3289 expm1(&x).unwrap().item_exact::<f32>(),
3290 1.0f32.exp_m1(),
3291 abs <= 1e-5
3292 };
3293 }
3294
3295 #[test]
3296 fn test_unary_sin() {
3297 let x = array![0.0];
3298 assert_eq!(sin(&x).unwrap().item_exact::<f32>(), 0.0);
3299
3300 let x = array![std::f32::consts::PI / 2.0];
3301 assert_float_eq! {
3302 sin(&x).unwrap().item_exact::<f32>(),
3303 (std::f32::consts::PI / 2.0f32).sin(),
3304 abs <= 1e-5
3305 };
3306
3307 assert_array_eq(
3308 sin(array!()).unwrap(),
3309 array!(),
3310 tolerances::EXACT.rtol,
3311 tolerances::EXACT.atol,
3312 );
3313
3314 let x = array![0];
3316 assert_eq!(x.dtype(), Dtype::Int32);
3317 assert_float_eq! {
3318 sin(&x).unwrap().item_exact::<f32>(),
3319 0.0f32.sin(),
3320 abs <= 1e-5
3321 };
3322
3323 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3325 let res = sin(&x).unwrap();
3326 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.sin())).unwrap();
3327 assert!(all_close(&res, &expected, None, None, None).unwrap());
3328
3329 let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
3330 let x = split_equal(&data, 2, 1).unwrap();
3331 let expected = Array::from_slice(&[0.0f32.sin(), 2.0f32.sin()], &[2, 1]);
3332 assert!(all_close(sin(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3333 }
3334
3335 #[test]
3336 fn test_unary_cos() {
3337 let x = array![0.0];
3338 assert_float_eq! {
3339 cos(&x).unwrap().item_exact::<f32>(),
3340 0.0f32.cos(),
3341 abs <= 1e-5
3342 };
3343
3344 let x = array![std::f32::consts::PI / 2.0];
3345 assert_float_eq! {
3346 cos(&x).unwrap().item_exact::<f32>(),
3347 (std::f32::consts::PI / 2.0f32).cos(),
3348 abs <= 1e-5
3349 };
3350
3351 assert_array_eq(
3352 cos(array!()).unwrap(),
3353 array!(),
3354 tolerances::EXACT.rtol,
3355 tolerances::EXACT.atol,
3356 );
3357
3358 let x = array![0];
3360 assert_eq!(x.dtype(), Dtype::Int32);
3361 assert_float_eq! {
3362 cos(&x).unwrap().item_exact::<f32>(),
3363 0.0f32.cos(),
3364 abs <= 1e-5
3365 };
3366
3367 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3369 let res = cos(&x).unwrap();
3370 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.cos())).unwrap();
3371 assert!(all_close(&res, &expected, None, None, None).unwrap());
3372
3373 let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
3374 let x = split_equal(&data, 2, 1).unwrap();
3375 let expected = Array::from_slice(&[0.0f32.cos(), 2.0f32.cos()], &[2, 1]);
3376 assert!(all_close(cos(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3377 }
3378
3379 #[test]
3380 fn test_unary_degrees() {
3381 let x = array![0.0];
3382 assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 0.0);
3383
3384 let x = array![std::f32::consts::PI / 2.0];
3385 assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 90.0);
3386
3387 assert_array_eq(
3388 degrees(array!()).unwrap(),
3389 array!(),
3390 tolerances::EXACT.rtol,
3391 tolerances::EXACT.atol,
3392 );
3393
3394 let x = array![0];
3396 assert_eq!(x.dtype(), Dtype::Int32);
3397 assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 0.0);
3398
3399 let x = broadcast_to(&array!(std::f32::consts::PI / 2.0), &[2, 2, 2]).unwrap();
3401 let res = degrees(&x).unwrap();
3402 let expected = Array::full::<f32>(&[2, 2, 2], array!(90.0)).unwrap();
3403 assert!(all_close(&res, &expected, None, None, None).unwrap());
3404
3405 let angles = Array::from_slice(&[0.0, PI / 2.0, PI, 1.5 * PI], &[2, 2]);
3406 let x = split_equal(&angles, 2, 1).unwrap();
3407 let expected = Array::from_slice(&[0.0, 180.0], &[2, 1]);
3408 assert!(all_close(degrees(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3409 }
3410
3411 #[test]
3412 fn test_unary_radians() {
3413 let x = array![0.0];
3414 assert_eq!(radians(&x).unwrap().item_exact::<f32>(), 0.0);
3415
3416 let x = array![90.0];
3417 assert_eq!(
3418 radians(&x).unwrap().item_exact::<f32>(),
3419 std::f32::consts::PI / 2.0
3420 );
3421
3422 assert_array_eq(
3423 radians(array!()).unwrap(),
3424 array!(),
3425 tolerances::EXACT.rtol,
3426 tolerances::EXACT.atol,
3427 );
3428
3429 let x = array![90];
3431 assert_eq!(x.dtype(), Dtype::Int32);
3432 assert_eq!(
3433 radians(&x).unwrap().item_exact::<f32>(),
3434 std::f32::consts::PI / 2.0
3435 );
3436
3437 let x = broadcast_to(&array!(90.0), &[2, 2, 2]).unwrap();
3439 let res = radians(&x).unwrap();
3440 let expected = Array::full::<f32>(&[2, 2, 2], array!(std::f32::consts::PI / 2.0)).unwrap();
3441 assert!(all_close(&res, &expected, None, None, None).unwrap());
3442
3443 let angles = Array::from_slice(&[0.0, 90.0, 180.0, 270.0], &[2, 2]);
3444 let x = split_equal(&angles, 2, 1).unwrap();
3445 let expected = Array::from_slice(&[0.0, PI], &[2, 1]);
3446 assert!(all_close(radians(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3447 }
3448
3449 #[test]
3450 fn test_unary_log() {
3451 let x = array![0.0];
3452 assert_eq!(log(&x).unwrap().item_exact::<f32>(), NEG_INF);
3453
3454 let x = array![1.0];
3455 assert_eq!(log(&x).unwrap().item_exact::<f32>(), 0.0);
3456
3457 let x = array![1];
3459 assert_eq!(log(&x).unwrap().dtype(), Dtype::Float32);
3460 assert_eq!(log(&x).unwrap().item_exact::<f32>(), 0.0);
3461
3462 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3464 let res = log(&x).unwrap();
3465 let expected = Array::full::<f32>(&[2, 2, 2], array!(0.0)).unwrap();
3466 assert!(all_close(&res, &expected, None, None, None).unwrap());
3467
3468 let data = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
3469 let x = split_equal(&data, 2, 1).unwrap();
3470 let expected = Array::from_slice(&[1.0f32.ln(), 3.0f32.ln()], &[2, 1]);
3471 assert!(all_close(log(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3472 }
3473
3474 #[test]
3475 fn test_unary_log2() {
3476 let x = array![0.0];
3477 assert_eq!(log2(&x).unwrap().item_exact::<f32>(), NEG_INF);
3478
3479 let x = array![1.0];
3480 assert_eq!(log2(&x).unwrap().item_exact::<f32>(), 0.0);
3481
3482 let x = array![1024.0];
3483 assert_eq!(log2(&x).unwrap().item_exact::<f32>(), 10.0);
3484 }
3485
3486 #[test]
3487 fn test_unary_log10() {
3488 let x = array![0.0];
3489 assert_eq!(log10(&x).unwrap().item_exact::<f32>(), NEG_INF);
3490
3491 let x = array![1.0];
3492 assert_eq!(log10(&x).unwrap().item_exact::<f32>(), 0.0);
3493
3494 let x = array![1000.0];
3495 assert_eq!(log10(&x).unwrap().item_exact::<f32>(), 3.0);
3496 }
3497
3498 #[test]
3499 fn test_unary_log1p() {
3500 let x = array![-1.0];
3501 assert_float_eq! {
3502 log1p(&x).unwrap().item_exact::<f32>(),
3503 (-1.0f32).ln_1p(),
3504 abs <= 1e-5
3505 };
3506
3507 let x = array![1.0];
3508 assert_float_eq! {
3509 log1p(&x).unwrap().item_exact::<f32>(),
3510 1.0f32.ln_1p(),
3511 abs <= 1e-5
3512 };
3513
3514 let x = array![1];
3516 assert_eq!(log1p(&x).unwrap().dtype(), Dtype::Float32);
3517 assert_float_eq! {
3518 log1p(&x).unwrap().item_exact::<f32>(),
3519 1.0f32.ln_1p(),
3520 abs <= 1e-5
3521 };
3522
3523 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3525 let res = log1p(&x).unwrap();
3526 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.ln_1p())).unwrap();
3527 assert!(all_close(&res, &expected, None, None, None).unwrap());
3528
3529 let data = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
3530 let x = split_equal(&data, 2, 1).unwrap();
3531 let expected = Array::from_slice(&[1.0f32.ln_1p(), 3.0f32.ln_1p()], &[2, 1]);
3532 assert!(all_close(log1p(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3533 }
3534
3535 #[test]
3536 fn test_unary_sigmoid() {
3537 let x = array![0.0];
3538 assert_float_eq! {
3539 sigmoid(&x).unwrap().item_exact::<f32>(),
3540 0.5,
3541 abs <= 1e-5
3542 };
3543
3544 let x = array![0];
3546 assert_eq!(sigmoid(&x).unwrap().dtype(), Dtype::Float32);
3547 assert_float_eq! {
3548 sigmoid(&x).unwrap().item_exact::<f32>(),
3549 0.5,
3550 abs <= 1e-5
3551 };
3552
3553 let inf = f32::INFINITY;
3554 let x = array![inf];
3555 assert_eq!(sigmoid(&x).unwrap().item_exact::<f32>(), 1.0);
3556
3557 let x = array![-inf];
3558 assert_eq!(sigmoid(&x).unwrap().item_exact::<f32>(), 0.0);
3559 }
3560
3561 #[test]
3562 fn test_unary_square() {
3563 let x = array![3.0];
3564 assert_eq!(square(&x).unwrap().item_exact::<f32>(), 9.0);
3565
3566 let x = array![2];
3567 assert_eq!(square(&x).unwrap().item_exact::<i32>(), 4);
3568
3569 let x = Array::full::<f32>(&[3, 3], array!(2.0)).unwrap();
3570 assert!(all_close(
3571 square(&x).unwrap(),
3572 Array::full::<f32>(&[3, 3], array!(4.0)).unwrap(),
3573 None,
3574 None,
3575 None
3576 )
3577 .unwrap());
3578 }
3579
3580 #[test]
3581 fn test_unary_sqrt_rsqrt() {
3582 let x = array![4.0];
3583 assert_eq!(sqrt(&x).unwrap().item_exact::<f32>(), 2.0);
3584 assert_eq!(rsqrt(&x).unwrap().item_exact::<f32>(), 0.5);
3585
3586 let x = Array::full::<f32>(&[3, 3], array!(9.0)).unwrap();
3587 assert!(all_close(
3588 sqrt(&x).unwrap(),
3589 Array::full::<f32>(&[3, 3], array!(3.0)).unwrap(),
3590 None,
3591 None,
3592 None
3593 )
3594 .unwrap());
3595
3596 let x = array![4i32];
3597 assert_eq!(sqrt(&x).unwrap().item_exact::<f32>(), 2.0);
3598 assert_eq!(rsqrt(&x).unwrap().item_exact::<f32>(), 0.5);
3599 }
3600
3601 #[test]
3602 fn test_unary_reciprocal() {
3603 let x = array![8.0];
3604 assert_eq!(reciprocal(&x).unwrap().item_exact::<f32>(), 0.125);
3605
3606 let x = array![2];
3607 let out = reciprocal(&x).unwrap();
3608 assert_eq!(out.dtype(), Dtype::Float32);
3609 assert_eq!(out.item_exact::<f32>(), 0.5);
3610
3611 let x = Array::full::<f32>(&[3, 3], array!(2.0)).unwrap();
3612 assert!(all_close(
3613 reciprocal(&x).unwrap(),
3614 Array::full::<f32>(&[3, 3], array!(0.5)).unwrap(),
3615 None,
3616 None,
3617 None
3618 )
3619 .unwrap());
3620 }
3621
3622 #[test]
3623 fn test_unary_real_imag() {
3624 let x = Array::from_complex(complex64::new(0.0, 1.0));
3625 assert_array_eq(
3626 real(&x).unwrap(),
3627 Array::from_f32(0.0),
3628 tolerances::EXACT.rtol,
3629 tolerances::EXACT.atol,
3630 );
3631 assert_array_eq(
3632 imag(&x).unwrap(),
3633 Array::from_f32(1.0),
3634 tolerances::EXACT.rtol,
3635 tolerances::EXACT.atol,
3636 );
3637 }
3638
3639 #[test]
3640 fn test_binary_add() {
3641 let x = array![1.0];
3642 let y = array![1.0];
3643 let z = add(&x, &y).unwrap();
3644 assert_eq!(z.item_exact::<f32>(), 2.0);
3645
3646 let z = &x + y;
3647 assert_eq!(z.item_exact::<f32>(), 2.0);
3648
3649 let z = add(z, &x).unwrap();
3650 assert_eq!(z.item_exact::<f32>(), 3.0);
3651
3652 let mut out = x.deep_clone();
3654 for _ in 0..10 {
3655 out = add(&out, &x).unwrap();
3656 }
3657 assert_eq!(out.item_exact::<f32>(), 11.0);
3658
3659 let x = array!([1.0, 2.0, 3.0]);
3661 let y = array!([1.0, 2.0, 3.0]);
3662 let z = add(&x, &y).unwrap();
3663 assert_eq!(z.shape(), &[3]);
3664 assert_array_eq(
3665 z,
3666 array!([2.0, 4.0, 6.0]),
3667 tolerances::EXACT.rtol,
3668 tolerances::EXACT.atol,
3669 );
3670
3671 let x = array!([1.0, 2.0, 3.0]);
3673 let y = &x + 2.0;
3674 assert_eq!(y.dtype(), Dtype::Float32);
3675 assert_array_eq(
3676 y,
3677 array!([3.0, 4.0, 5.0]),
3678 tolerances::EXACT.rtol,
3679 tolerances::EXACT.atol,
3680 );
3681 let y = &x + 2.0;
3682 assert_eq!(y.dtype(), Dtype::Float32);
3683 assert_array_eq(
3684 y,
3685 array!([3.0, 4.0, 5.0]),
3686 tolerances::EXACT.rtol,
3687 tolerances::EXACT.atol,
3688 );
3689
3690 let y = x + 2;
3692 assert_eq!(y.dtype(), Dtype::Float32);
3693
3694 let y = array!([1, 2, 3]) + 2.0;
3695 assert_eq!(y.dtype(), Dtype::Float32);
3696 assert_array_eq(
3698 y,
3699 array!([3.0, 4.0, 5.0]),
3700 tolerances::EXACT.rtol,
3701 tolerances::EXACT.atol,
3702 );
3703
3704 let x = broadcast_to(&array!(1.0), &[10]).unwrap();
3706 let y = broadcast_to(&array!(2.0), &[10]).unwrap();
3707 let z = add(&x, &y).unwrap();
3708 assert_array_eq(
3709 z,
3710 full::<f32>(&[10], array!(3.0)).unwrap(),
3711 tolerances::EXACT.rtol,
3712 tolerances::EXACT.atol,
3713 );
3714
3715 let x = Array::from_slice(&[1.0, 2.0], &[1, 2]);
3716 let y = Array::from_slice(&[1.0, 2.0], &[2, 1]);
3717 let z = add(&x, &y).unwrap();
3718 assert_eq!(z.shape(), &[2, 2]);
3719 assert_array_eq(
3720 z,
3721 Array::from_slice(&[2.0, 3.0, 3.0, 4.0], &[2, 2]),
3722 tolerances::EXACT.rtol,
3723 tolerances::EXACT.atol,
3724 );
3725
3726 let x = ones::<f32>(&[3, 2, 1]).unwrap();
3727 let z = x + 2.0;
3728 assert_eq!(z.shape(), &[3, 2, 1]);
3729 let expected = Array::from_slice(&[3.0, 3.0, 3.0, 3.0, 3.0, 3.0], &[3, 2, 1]);
3730 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
3731
3732 let x = array!();
3734 let y = array!();
3735 let z = x + y;
3736 z.eval().unwrap();
3737 assert_eq!(z.size(), 0);
3738 assert_eq!(z.shape(), &[0]);
3739 }
3740
3741 #[test]
3742 fn test_binary_sub() {
3743 let x = array!([3.0, 2.0, 1.0]);
3744 let y = array!([1.0, 1.0, 1.0]);
3745 assert_array_eq(
3746 x - y,
3747 array!([2.0, 1.0, 0.0]),
3748 tolerances::EXACT.rtol,
3749 tolerances::EXACT.atol,
3750 );
3751 }
3752
3753 #[test]
3754 fn test_binary_mul() {
3755 let x = array!([1.0, 2.0, 3.0]);
3756 let y = array!([2.0, 2.0, 2.0]);
3757 assert_array_eq(
3758 x * y,
3759 array!([2.0, 4.0, 6.0]),
3760 tolerances::EXACT.rtol,
3761 tolerances::EXACT.atol,
3762 );
3763 }
3764
3765 #[test]
3766 fn test_binary_div() {
3767 let x = array![1.0];
3768 let y = array![1.0];
3769 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3770
3771 let x = array![1.0];
3772 let y = array![0.5];
3773 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 2.0);
3774
3775 let x = array![1.0];
3776 let y = array![4.0];
3777 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 0.25);
3778
3779 let x = array![true];
3780 let y = array![true];
3781 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3782
3783 let x = array![false];
3784 let y = array![true];
3785 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 0.0);
3786
3787 let x = array![true];
3788 let y = array![false];
3789 assert!(divide(&x, &y).unwrap().item_exact::<f32>().is_infinite());
3790
3791 let x = array![false];
3792 let y = array![false];
3793 assert!(divide(&x, &y).unwrap().item_exact::<f32>().is_nan());
3794 }
3795
3796 #[test]
3797 fn test_binary_maximum_minimum() {
3798 let x = array![1.0];
3799 let y = array![0.0];
3800 assert_eq!(maximum(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3801 assert_eq!(minimum(&x, &y).unwrap().item_exact::<f32>(), 0.0);
3802
3803 let y = array![2.0];
3804 assert_eq!(maximum(&x, &y).unwrap().item_exact::<f32>(), 2.0);
3805 assert_eq!(minimum(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3806 }
3807
3808 #[test]
3809 fn test_binary_logaddexp() {
3810 let x = array![0.0];
3811 let y = array![0.0];
3812 assert_float_eq! {
3813 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3814 2.0f32.ln(),
3815 abs <= 1e-5
3816 };
3817
3818 let x = array!([0u32]);
3819 let y = array!([10000u32]);
3820 assert_eq!(logaddexp(&x, &y).unwrap().item_exact::<f32>(), 10000.0);
3821
3822 let x = array![f32::INFINITY];
3823 let y = array![3.0];
3824 assert_eq!(
3825 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3826 f32::INFINITY
3827 );
3828
3829 let x = array![f32::NEG_INFINITY];
3830 let y = array![3.0];
3831 assert_eq!(logaddexp(&x, &y).unwrap().item_exact::<f32>(), 3.0);
3832
3833 let x = array![f32::NEG_INFINITY];
3834 let y = array![f32::NEG_INFINITY];
3835 assert_eq!(
3836 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3837 f32::NEG_INFINITY
3838 );
3839
3840 let x = array![f32::INFINITY];
3841 let y = array![f32::INFINITY];
3842 assert_eq!(
3843 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3844 f32::INFINITY
3845 );
3846
3847 let x = array![f32::NEG_INFINITY];
3848 let y = array![f32::INFINITY];
3849 assert_eq!(
3850 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3851 f32::INFINITY
3852 );
3853 }
3854
3855 #[test]
3856 fn test_basic_clip() {
3857 let a = array!([1.0, 4.0, 3.0, 8.0, 5.0]);
3858 let expected = array!([2.0, 4.0, 3.0, 6.0, 5.0]);
3859 let clipped = clip(&a, (array!(2.0), array!(6.0))).unwrap();
3860 assert_array_eq(
3861 clipped,
3862 &expected,
3863 tolerances::EXACT.rtol,
3864 tolerances::EXACT.atol,
3865 );
3866
3867 let clipped = clip(&a, (2.0, 6.0)).unwrap();
3869 assert_array_eq(
3870 clipped,
3871 &expected,
3872 tolerances::EXACT.rtol,
3873 tolerances::EXACT.atol,
3874 );
3875 }
3876
3877 #[test]
3878 fn test_clip_with_only_min() {
3879 let a = array!([-1.0, 1.0, 0.0, 5.0]);
3880 let expected = array!([0.0, 1.0, 0.0, 5.0]);
3881 let clipped = clip(&a, (array!(0.0), ())).unwrap();
3882 assert_array_eq(
3883 clipped,
3884 &expected,
3885 tolerances::EXACT.rtol,
3886 tolerances::EXACT.atol,
3887 );
3888
3889 let clipped = clip(&a, (0.0, ())).unwrap();
3891 assert_array_eq(
3892 clipped,
3893 expected,
3894 tolerances::EXACT.rtol,
3895 tolerances::EXACT.atol,
3896 );
3897 }
3898
3899 #[test]
3900 fn test_clip_with_only_max() {
3901 let a = array!([2.0, 3.0, 4.0, 5.0]);
3902 let expected = array!([2.0, 3.0, 4.0, 4.0]);
3903 let clipped = clip(&a, ((), array!(4.0))).unwrap();
3904 assert_array_eq(
3905 clipped,
3906 &expected,
3907 tolerances::EXACT.rtol,
3908 tolerances::EXACT.atol,
3909 );
3910
3911 let clipped = clip(&a, ((), 4.0)).unwrap();
3913 assert_array_eq(
3914 clipped,
3915 expected,
3916 tolerances::EXACT.rtol,
3917 tolerances::EXACT.atol,
3918 );
3919 }
3920
3921 #[test]
3922 fn test_tensordot() {
3923 let x = reshape(arange::<_, f32>(None, 60.0, None).unwrap(), &[3, 4, 5]).unwrap();
3924 let y = reshape(arange::<_, f32>(None, 24.0, None).unwrap(), &[4, 3, 2]).unwrap();
3925 let z = tensordot_axes(&x, &y, &[1i32, 0], &[0i32, 1]).unwrap();
3926 let expected = Array::from_slice(
3927 &[
3928 4400.0_f32, 4730.0, 4532.0, 4874.0, 4664.0, 5018.0, 4796.0, 5162.0, 4928.0, 5306.0,
3929 ],
3930 &[5, 2],
3931 );
3932 assert_array_eq_with_context(
3933 z,
3934 expected,
3935 tolerances::EXACT.rtol,
3936 tolerances::EXACT.atol,
3937 "float32 explicit-axis contraction",
3938 );
3939
3940 let x = reshape(arange::<_, f32>(None, 360.0, None).unwrap(), &[3, 4, 5, 6]).unwrap();
3941 let y = reshape(arange::<_, f32>(None, 360.0, None).unwrap(), &[6, 4, 5, 3]).unwrap();
3942 assert!(tensordot_axes(&x, &y, &[2, 1, 3], &[1, 2, 0]).is_err());
3943
3944 let x = reshape(arange::<_, f32>(None, 60.0, None).unwrap(), &[3, 4, 5]).unwrap();
3945 let y = reshape(arange::<_, f32>(None, 120.0, None).unwrap(), &[4, 5, 6]).unwrap();
3946
3947 let z = tensordot_axis(&x, &y, 2).unwrap();
3948 let expected = Array::from_slice(
3949 &[
3950 14820.0, 15010.0, 15200.0, 15390.0, 15580.0, 15770.0, 37620.0, 38210.0, 38800.0,
3951 39390.0, 39980.0, 40570.0, 60420.0, 61410.0, 62400.0, 63390.0, 64380.0, 65370.0,
3952 ],
3953 &[3, 6],
3954 );
3955 assert_array_eq_with_context(
3956 z,
3957 expected,
3958 tolerances::EXACT.rtol,
3959 tolerances::EXACT.atol,
3960 "float32 axis-count contraction",
3961 );
3962 }
3963
3964 #[test]
3965 fn test_outer() {
3966 let x = arange::<_, f32>(1.0, 5.0, None).unwrap();
3967 let y = arange::<_, f32>(1.0, 4.0, None).unwrap();
3968 let z = outer(&x, &y).unwrap();
3969 let expected = Array::from_slice(
3970 &[1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, 4.0, 8.0, 12.0],
3971 &[4, 3],
3972 );
3973 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
3974
3975 let x = ones::<f32>(&[5]).unwrap();
3976 let y = linspace::<_, f32>(
3977 -2.0,
3978 2.0,
3979 crate::ops::LinspaceOptions {
3980 count: 5,
3981 endpoint: true,
3982 },
3983 )
3984 .unwrap();
3985 let z = outer(&x, &y).unwrap();
3986 let expected = Array::from_slice(
3987 &[
3988 -2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0,
3989 -2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0,
3990 ],
3991 &[5, 5],
3992 );
3993 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
3994 }
3995
3996 #[test]
3997 fn test_inner() {
3998 let x = reshape(arange::<_, f32>(None, 5.0, None).unwrap(), &[1, 5]).unwrap();
3999 let y = reshape(arange::<_, f32>(None, 6.0, None).unwrap(), &[2, 3]).unwrap();
4000 assert!(inner(&x, &y).is_err());
4001
4002 let x = array!([1.0, 2.0, 3.0]);
4003 let y = array!([0.0, 1.0, 0.0]);
4004 let z = inner(&x, &y).unwrap();
4005 assert_eq!(z.item_exact::<f32>(), 2.0);
4006
4007 let x = reshape(arange::<_, f32>(None, 24.0, None).unwrap(), &[2, 3, 4]).unwrap();
4008 let y = arange::<_, f32>(None, 4.0, None).unwrap();
4009 let z = inner(&x, &y).unwrap();
4010 let expected = Array::from_slice(&[14.0, 38.0, 62.0, 86.0, 110.0, 134.0], &[2, 3]);
4011 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4012
4013 let x = reshape(arange::<_, f32>(None, 2.0, None).unwrap(), &[1, 1, 2]).unwrap();
4014 let y = reshape(arange::<_, f32>(None, 6.0, None).unwrap(), &[3, 2]).unwrap();
4015 let z = inner(&x, &y).unwrap();
4016 let expected = Array::from_slice(&[1.0, 3.0, 5.0], &[1, 1, 3]);
4017 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4018
4019 let x = eye::<f32>(2, None, None).unwrap();
4020 let y = Array::from_f32(7.0);
4021 let z = inner(&x, &y).unwrap();
4022 let expected = Array::from_slice(&[7.0, 0.0, 0.0, 7.0], &[2, 2]);
4023 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4024 }
4025
4026 #[test]
4027 fn test_divmod() {
4028 let x = array!([1.0, 2.0, 3.0]);
4029 let y = array!([1.0, 1.0, 1.0]);
4030 let out = divmod(&x, &y).unwrap();
4031 assert_array_eq(
4032 out.0,
4033 array!([1.0, 2.0, 3.0]),
4034 tolerances::EXACT.rtol,
4035 tolerances::EXACT.atol,
4036 );
4037 assert_array_eq(
4038 out.1,
4039 array!([0.0, 0.0, 0.0]),
4040 tolerances::EXACT.rtol,
4041 tolerances::EXACT.atol,
4042 );
4043
4044 let x = array!([5.0, 6.0, 7.0]);
4045 let y = array!([2.0, 2.0, 2.0]);
4046 let out = divmod(&x, &y).unwrap();
4047 assert_array_eq(
4048 out.0,
4049 array!([2.0, 3.0, 3.0]),
4050 tolerances::EXACT.rtol,
4051 tolerances::EXACT.atol,
4052 );
4053 assert_array_eq(
4054 out.1,
4055 array!([1.0, 0.0, 1.0]),
4056 tolerances::EXACT.rtol,
4057 tolerances::EXACT.atol,
4058 );
4059
4060 let x = array!([5.0, 6.0, 7.0]);
4061 let y = array!([2.0, 2.0, 2.0]);
4062 let out = divmod(&x, &y).unwrap();
4063 assert_array_eq(
4064 out.0,
4065 array!([2.0, 3.0, 3.0]),
4066 tolerances::EXACT.rtol,
4067 tolerances::EXACT.atol,
4068 );
4069 assert_array_eq(
4070 out.1,
4071 array!([1.0, 0.0, 1.0]),
4072 tolerances::EXACT.rtol,
4073 tolerances::EXACT.atol,
4074 );
4075
4076 let x = array![complex64::new(1.0, 0.0)];
4077 let y = array![complex64::new(2.0, 0.0)];
4078 assert!(divmod(&x, &y).is_err());
4079
4080 let x = array![1.0];
4082 let y = array![2.0];
4083 let (quo, rem) = divmod(&x, &y).unwrap();
4084 eval([&quo, &rem]).unwrap();
4085 assert_eq!(quo.item_exact::<f32>(), 0.0);
4086 assert_eq!(rem.item_exact::<f32>(), 1.0);
4087
4088 let x = array![1.0];
4090 let y = array![2.0];
4091 let (quo, rem) = divmod(&x, &y).unwrap();
4092 let z = quo + rem;
4093 assert_eq!(z.item_exact::<f32>(), 1.0);
4094
4095 let mut out_holder = {
4097 let (quo, _) = divmod(&x, &y).unwrap();
4098 vec![quo]
4099 };
4100 eval(out_holder.iter()).unwrap();
4101 assert_eq!(out_holder[0].item_exact::<f32>(), 0.0);
4102
4103 out_holder.clear();
4105 let out_holder = {
4106 let (_, rem) = divmod(&x, &y).unwrap();
4107 vec![rem]
4108 };
4109 eval(out_holder.iter()).unwrap();
4110 assert_eq!(out_holder[0].item_exact::<f32>(), 1.0);
4111 }
4112
4113 #[test]
4115 fn test_segmented_mm() {
4116 use crate::ops::{indexing::*, stack};
4117 use crate::random;
4118
4119 fn segmented_mm_ref(a: &Array, b: &Array, segments: &Array) -> Array {
4121 let segments_data: Vec<Vec<u32>> = (0..segments.shape()[0])
4122 .map(|i| {
4123 let row = segments.index(i);
4124 vec![
4125 row.index(0).item_exact::<u32>(),
4126 row.index(1).item_exact::<u32>(),
4127 ]
4128 })
4129 .collect();
4130
4131 let results: Vec<Array> = segments_data
4132 .iter()
4133 .map(|seg| {
4134 let s1 = seg[0] as i32;
4135 let s2 = seg[1] as i32;
4136 let a_slice = a.index((.., s1..s2));
4137 let b_slice = b.index((s1..s2, ..));
4138 a_slice.matmul(&b_slice).unwrap()
4139 })
4140 .collect();
4141
4142 stack(&results, 0).unwrap()
4143 }
4144
4145 let shapes = [(10, 10, 10), (10, 10, 100), (100, 100, 100)];
4147
4148 let all_segments: Vec<Vec<f32>> = vec![
4150 vec![0.0, 0.0, 1.0],
4151 vec![0.0, 0.5, 1.0],
4152 (0..10).map(|r| r as f32 / 9.0).collect(),
4153 ];
4154
4155 random::seed(42).unwrap();
4156
4157 for (m, n, k) in shapes {
4158 for s in &all_segments {
4159 let mut segments_vec: Vec<[u32; 2]> = Vec::new();
4161 for i in 0..s.len() - 1 {
4162 let s1 = ((k as f32 * s[i]) as u32).min(k as u32 - 1);
4163 let s2 = ((k as f32 * s[i + 1]) as u32).min(k as u32 - 1);
4164 segments_vec.push([s1, s2]);
4165 }
4166 let segments_flat: Vec<u32> = segments_vec.iter().flat_map(|x| *x).collect();
4167 let segments = Array::from_slice(&segments_flat, &[segments_vec.len() as i32, 2]);
4168
4169 let a = random::normal::<f32>(&[m, k], None, None, None).unwrap();
4171 let b = random::normal::<f32>(&[k, n], None, None, None).unwrap();
4172 let c1 = segmented_mm_ref(&a, &b, &segments);
4173 let c2 = segmented_mm(&a, &b, &segments).unwrap();
4174 assert!(
4175 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4176 "segmented_mm failed for shape ({}, {}, {}) with segments {:?}",
4177 m,
4178 n,
4179 k,
4180 s
4181 );
4182
4183 let a = random::normal::<f32>(&[k, m], None, None, None).unwrap();
4185 let b = random::normal::<f32>(&[k, n], None, None, None).unwrap();
4186 let a_t = a.t();
4187 let c1 = segmented_mm_ref(&a_t, &b, &segments);
4188 let c2 = segmented_mm(&a_t, &b, &segments).unwrap();
4189 assert!(
4190 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4191 "segmented_mm with transposed a failed for shape ({}, {}, {})",
4192 m,
4193 n,
4194 k
4195 );
4196
4197 let a = random::normal::<f32>(&[m, k], None, None, None).unwrap();
4199 let b = random::normal::<f32>(&[n, k], None, None, None).unwrap();
4200 let b_t = b.t();
4201 let c1 = segmented_mm_ref(&a, &b_t, &segments);
4202 let c2 = segmented_mm(&a, &b_t, &segments).unwrap();
4203 assert!(
4204 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4205 "segmented_mm with transposed b failed for shape ({}, {}, {})",
4206 m,
4207 n,
4208 k
4209 );
4210
4211 let a = random::normal::<f32>(&[k, m], None, None, None).unwrap();
4213 let b = random::normal::<f32>(&[n, k], None, None, None).unwrap();
4214 let a_t = a.t();
4215 let b_t = b.t();
4216 let c1 = segmented_mm_ref(&a_t, &b_t, &segments);
4217 let c2 = segmented_mm(&a_t, &b_t, &segments).unwrap();
4218 assert!(
4219 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4220 "segmented_mm with both transposed failed for shape ({}, {}, {})",
4221 m,
4222 n,
4223 k
4224 );
4225 }
4226 }
4227 }
4228
4229 #[test]
4230 fn test_segmented_mm_batched_error() {
4231 let a = ones::<f32>(&[2, 10, 10]).unwrap();
4233 let segments = Array::from_slice(&[0u32, 5, 5, 10], &[2, 2]);
4234 let result = segmented_mm(&a, &a, &segments);
4235 assert!(
4236 result.is_err(),
4237 "segmented_mm should fail for batched input"
4238 );
4239 }
4240
4241 #[test]
4243 fn test_gather_mm() {
4244 use crate::ops::indexing::take_axis;
4245 use crate::random;
4246
4247 random::seed(0).unwrap();
4248
4249 fn gather_mm_ref(
4251 a: &Array,
4252 b: &Array,
4253 lhs_indices: Option<&Array>,
4254 rhs_indices: Option<&Array>,
4255 ) -> Array {
4256 let a = a
4257 .reshape(&[-1, a.shape()[a.ndim() - 2], a.shape()[a.ndim() - 1]])
4258 .unwrap();
4259 let b = b
4260 .reshape(&[-1, b.shape()[b.ndim() - 2], b.shape()[b.ndim() - 1]])
4261 .unwrap();
4262
4263 let a_gathered = match lhs_indices {
4264 Some(idx) => take_axis(&a, idx, 0).unwrap(),
4265 None => a,
4266 };
4267 let b_gathered = match rhs_indices {
4268 Some(idx) => take_axis(&b, idx, 0).unwrap(),
4269 None => b,
4270 };
4271 a_gathered.matmul(&b_gathered).unwrap()
4272 }
4273
4274 let a = random::normal::<f32>(&[1, 32, 32], None, None, None).unwrap();
4276 let b = random::normal::<f32>(&[3, 32, 32], None, None, None).unwrap();
4277 let lhs_indices = Array::from_slice(&[0u32], &[1]);
4278 let rhs_indices = Array::from_slice(&[2u32, 1], &[2]);
4279
4280 let out_ref = gather_mm_ref(&a, &b, Some(&lhs_indices), Some(&rhs_indices));
4281 let out_test = gather_mm(&a, &b, &lhs_indices, &rhs_indices, None).unwrap();
4282 assert!(
4283 out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
4284 "gather_mm test case 1 failed"
4285 );
4286
4287 let out_ref = gather_mm_ref(&a, &b, None, Some(&rhs_indices));
4289 let out_test = gather_mm(&a, &b, None::<&Array>, &rhs_indices, None).unwrap();
4290 assert!(
4291 out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
4292 "gather_mm test case 2 failed"
4293 );
4294
4295 let a = random::normal::<f32>(&[5, 32, 32], None, None, None).unwrap();
4297 let lhs_indices = Array::from_slice(&[0u32, 2], &[2]);
4298
4299 let out_ref = gather_mm_ref(&a, &b, Some(&lhs_indices), Some(&rhs_indices));
4300 let out_test = gather_mm(&a, &b, &lhs_indices, &rhs_indices, None).unwrap();
4301 assert!(
4302 out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
4303 "gather_mm test case 3 failed"
4304 );
4305 }
4306
4307 #[test]
4309 fn test_gather_mm_sorted() {
4310 use crate::ops::indexing::take_axis;
4311 use crate::ops::sort;
4312 use crate::random;
4313
4314 random::seed(0).unwrap();
4315
4316 fn gather_mm_ref(a: &Array, b: &Array, rhs: &Array) -> Array {
4318 let b_gathered = take_axis(b, rhs, 0).unwrap();
4319 a.matmul(&b_gathered).unwrap()
4320 }
4321
4322 let a = random::normal::<f32>(&[100, 1, 100], None, None, None).unwrap();
4323 let b = random::normal::<f32>(&[8, 100, 100], None, None, None).unwrap();
4324 let rhs = sort(&random::randint::<_, i32>(0, 8, &[100], None).unwrap()).unwrap();
4325
4326 let c1 = gather_mm_ref(&a, &b, &rhs);
4327 let c2 = gather_mm(&a, &b, None::<&Array>, &rhs, true).unwrap();
4328 assert!(
4329 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4330 "gather_mm_sorted failed"
4331 );
4332 }
4333}