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(
1688 a: impl AsRef<Array>,
1689 b: impl AsRef<Array>,
1690 segments: impl AsRef<Array>,
1691) -> Result<Array> {
1692 let stream = Stream::thread_local_or_default();
1693 Array::try_from_op(|res| unsafe {
1694 mlx_sys::mlx_segmented_mm(
1695 res,
1696 a.as_ref().as_ptr(),
1697 b.as_ref().as_ptr(),
1698 segments.as_ref().as_ptr(),
1699 stream.as_ref().as_ptr(),
1700 )
1701 })
1702}
1703
1704#[generate_macro(customize(forwarding_shim = true))]
1706#[deprecated(
1707 since = "0.26.0",
1708 note = "use `with_stream` or `with_device` around `segmented_mm`"
1709)]
1710pub fn segmented_mm_device(
1711 a: impl AsRef<Array>,
1712 b: impl AsRef<Array>,
1713 segments: impl AsRef<Array>,
1714 #[optional] stream: impl AsRef<Stream>,
1715) -> Result<Array> {
1716 crate::with_stream(stream.as_ref(), || segmented_mm(a, b, segments))
1717}
1718
1719pub fn maximum(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1724 let stream = Stream::thread_local_or_default();
1725 let a_ptr = a.as_ref().as_ptr();
1726 let b_ptr = b.as_ref().as_ptr();
1727
1728 Array::try_from_op(|res| unsafe {
1729 mlx_sys::mlx_maximum(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
1730 })
1731}
1732
1733#[generate_macro(customize(forwarding_shim = true))]
1735#[deprecated(
1736 since = "0.26.0",
1737 note = "use `with_stream` or `with_device` around `maximum`"
1738)]
1739pub fn maximum_device(
1740 a: impl AsRef<Array>,
1741 b: impl AsRef<Array>,
1742 #[optional] stream: impl AsRef<Stream>,
1743) -> Result<Array> {
1744 crate::with_stream(stream.as_ref(), || maximum(a, b))
1745}
1746
1747pub fn minimum(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1752 let stream = Stream::thread_local_or_default();
1753 let a_ptr = a.as_ref().as_ptr();
1754 let b_ptr = b.as_ref().as_ptr();
1755
1756 Array::try_from_op(|res| unsafe {
1757 mlx_sys::mlx_minimum(res, a_ptr, b_ptr, stream.as_ref().as_ptr())
1758 })
1759}
1760
1761#[generate_macro(customize(forwarding_shim = true))]
1763#[deprecated(
1764 since = "0.26.0",
1765 note = "use `with_stream` or `with_device` around `minimum`"
1766)]
1767pub fn minimum_device(
1768 a: impl AsRef<Array>,
1769 b: impl AsRef<Array>,
1770 #[optional] stream: impl AsRef<Stream>,
1771) -> Result<Array> {
1772 crate::with_stream(stream.as_ref(), || minimum(a, b))
1773}
1774
1775pub fn multiply(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1777 a.as_ref().multiply(b)
1778}
1779
1780#[generate_macro(customize(forwarding_shim = true))]
1782#[deprecated(
1783 since = "0.26.0",
1784 note = "use `with_stream` or `with_device` around `multiply`"
1785)]
1786pub fn multiply_device(
1787 a: impl AsRef<Array>,
1788 b: impl AsRef<Array>,
1789 #[optional] stream: impl AsRef<Stream>,
1790) -> Result<Array> {
1791 crate::with_stream(stream.as_ref(), || multiply(a, b))
1792}
1793
1794pub fn negative(a: impl AsRef<Array>) -> Result<Array> {
1796 a.as_ref().negative()
1797}
1798
1799#[generate_macro(customize(forwarding_shim = true))]
1801#[deprecated(
1802 since = "0.26.0",
1803 note = "use `with_stream` or `with_device` around `negative`"
1804)]
1805pub fn negative_device(
1806 a: impl AsRef<Array>,
1807 #[optional] stream: impl AsRef<Stream>,
1808) -> Result<Array> {
1809 crate::with_stream(stream.as_ref(), || negative(a))
1810}
1811
1812pub fn power(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1814 a.as_ref().power(b)
1815}
1816
1817#[generate_macro(customize(forwarding_shim = true))]
1819#[deprecated(
1820 since = "0.26.0",
1821 note = "use `with_stream` or `with_device` around `power`"
1822)]
1823pub fn power_device(
1824 a: impl AsRef<Array>,
1825 b: impl AsRef<Array>,
1826 #[optional] stream: impl AsRef<Stream>,
1827) -> Result<Array> {
1828 crate::with_stream(stream.as_ref(), || power(a, b))
1829}
1830
1831pub fn radians(a: impl AsRef<Array>) -> Result<Array> {
1833 let stream = Stream::thread_local_or_default();
1834 Array::try_from_op(|res| unsafe {
1835 mlx_sys::mlx_radians(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1836 })
1837}
1838
1839#[generate_macro(customize(forwarding_shim = true))]
1841#[deprecated(
1842 since = "0.26.0",
1843 note = "use `with_stream` or `with_device` around `radians`"
1844)]
1845pub fn radians_device(
1846 a: impl AsRef<Array>,
1847 #[optional] stream: impl AsRef<Stream>,
1848) -> Result<Array> {
1849 crate::with_stream(stream.as_ref(), || radians(a))
1850}
1851
1852pub fn reciprocal(a: impl AsRef<Array>) -> Result<Array> {
1854 a.as_ref().reciprocal()
1855}
1856
1857#[generate_macro(customize(forwarding_shim = true))]
1859#[deprecated(
1860 since = "0.26.0",
1861 note = "use `with_stream` or `with_device` around `reciprocal`"
1862)]
1863pub fn reciprocal_device(
1864 a: impl AsRef<Array>,
1865 #[optional] stream: impl AsRef<Stream>,
1866) -> Result<Array> {
1867 crate::with_stream(stream.as_ref(), || reciprocal(a))
1868}
1869
1870pub fn remainder(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
1872 a.as_ref().remainder(b)
1873}
1874
1875#[generate_macro(customize(forwarding_shim = true))]
1877#[deprecated(
1878 since = "0.26.0",
1879 note = "use `with_stream` or `with_device` around `remainder`"
1880)]
1881pub fn remainder_device(
1882 a: impl AsRef<Array>,
1883 b: impl AsRef<Array>,
1884 #[optional] stream: impl AsRef<Stream>,
1885) -> Result<Array> {
1886 crate::with_stream(stream.as_ref(), || remainder(a, b))
1887}
1888
1889pub fn round(a: impl AsRef<Array>, decimals: impl Into<Option<i32>>) -> Result<Array> {
1891 a.as_ref().round(decimals)
1892}
1893
1894#[generate_macro(customize(forwarding_shim = true))]
1896#[deprecated(
1897 since = "0.26.0",
1898 note = "use `with_stream` or `with_device` around `round`"
1899)]
1900pub fn round_device(
1901 a: impl AsRef<Array>,
1902 decimals: impl Into<Option<i32>>,
1903 #[optional] stream: impl AsRef<Stream>,
1904) -> Result<Array> {
1905 crate::with_stream(stream.as_ref(), || round(a, decimals))
1906}
1907
1908pub fn rsqrt(a: impl AsRef<Array>) -> Result<Array> {
1910 a.as_ref().rsqrt()
1911}
1912
1913#[generate_macro(customize(forwarding_shim = true))]
1915#[deprecated(
1916 since = "0.26.0",
1917 note = "use `with_stream` or `with_device` around `rsqrt`"
1918)]
1919pub fn rsqrt_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1920 crate::with_stream(stream.as_ref(), || rsqrt(a))
1921}
1922
1923pub fn sigmoid(a: impl AsRef<Array>) -> Result<Array> {
1929 let stream = Stream::thread_local_or_default();
1930 Array::try_from_op(|res| unsafe {
1931 mlx_sys::mlx_sigmoid(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1932 })
1933}
1934
1935#[generate_macro(customize(forwarding_shim = true))]
1937#[deprecated(
1938 since = "0.26.0",
1939 note = "use `with_stream` or `with_device` around `sigmoid`"
1940)]
1941pub fn sigmoid_device(
1942 a: impl AsRef<Array>,
1943 #[optional] stream: impl AsRef<Stream>,
1944) -> Result<Array> {
1945 crate::with_stream(stream.as_ref(), || sigmoid(a))
1946}
1947
1948pub fn sign(a: impl AsRef<Array>) -> Result<Array> {
1950 let stream = Stream::thread_local_or_default();
1951 Array::try_from_op(|res| unsafe {
1952 mlx_sys::mlx_sign(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1953 })
1954}
1955
1956#[generate_macro(customize(forwarding_shim = true))]
1958#[deprecated(
1959 since = "0.26.0",
1960 note = "use `with_stream` or `with_device` around `sign`"
1961)]
1962pub fn sign_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1963 crate::with_stream(stream.as_ref(), || sign(a))
1964}
1965
1966pub fn sin(a: impl AsRef<Array>) -> Result<Array> {
1968 a.as_ref().sin()
1969}
1970
1971#[generate_macro(customize(forwarding_shim = true))]
1973#[deprecated(
1974 since = "0.26.0",
1975 note = "use `with_stream` or `with_device` around `sin`"
1976)]
1977pub fn sin_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1978 crate::with_stream(stream.as_ref(), || sin(a))
1979}
1980
1981pub fn sinh(a: impl AsRef<Array>) -> Result<Array> {
1983 let stream = Stream::thread_local_or_default();
1984 Array::try_from_op(|res| unsafe {
1985 mlx_sys::mlx_sinh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
1986 })
1987}
1988
1989#[generate_macro(customize(forwarding_shim = true))]
1991#[deprecated(
1992 since = "0.26.0",
1993 note = "use `with_stream` or `with_device` around `sinh`"
1994)]
1995pub fn sinh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
1996 crate::with_stream(stream.as_ref(), || sinh(a))
1997}
1998
1999pub fn softmax_axes(
2005 a: impl AsRef<Array>,
2006 axes: &[i32],
2007 precise: impl Into<Option<bool>>,
2008) -> Result<Array> {
2009 let stream = Stream::thread_local_or_default();
2010 let precise = precise.into().unwrap_or(false);
2011 let s = stream.as_ref().as_ptr();
2012
2013 Array::try_from_op(|res| unsafe {
2014 mlx_sys::mlx_softmax_axes(
2015 res,
2016 a.as_ref().as_ptr(),
2017 axes.as_ptr(),
2018 axes.len(),
2019 precise,
2020 s,
2021 )
2022 })
2023}
2024
2025#[generate_macro(customize(forwarding_shim = true))]
2027#[deprecated(
2028 since = "0.26.0",
2029 note = "use `with_stream` or `with_device` around `softmax_axes`"
2030)]
2031pub fn softmax_axes_device(
2032 a: impl AsRef<Array>,
2033 axes: &[i32],
2034 precise: impl Into<Option<bool>>,
2035 #[optional] stream: impl AsRef<Stream>,
2036) -> Result<Array> {
2037 crate::with_stream(stream.as_ref(), || softmax_axes(a, axes, precise))
2038}
2039
2040pub fn softmax_axis(
2042 a: impl AsRef<Array>,
2043 axis: i32,
2044 precise: impl Into<Option<bool>>,
2045) -> Result<Array> {
2046 let stream = Stream::thread_local_or_default();
2047 let precise = precise.into().unwrap_or(false);
2048 let s = stream.as_ref().as_ptr();
2049
2050 Array::try_from_op(|res| unsafe {
2051 mlx_sys::mlx_softmax_axis(res, a.as_ref().as_ptr(), axis, precise, s)
2052 })
2053}
2054
2055#[generate_macro(customize(forwarding_shim = true))]
2057#[deprecated(
2058 since = "0.26.0",
2059 note = "use `with_stream` or `with_device` around `softmax_axis`"
2060)]
2061pub fn softmax_axis_device(
2062 a: impl AsRef<Array>,
2063 axis: i32,
2064 precise: impl Into<Option<bool>>,
2065 #[optional] stream: impl AsRef<Stream>,
2066) -> Result<Array> {
2067 crate::with_stream(stream.as_ref(), || softmax_axis(a, axis, precise))
2068}
2069
2070pub fn softmax(a: impl AsRef<Array>, precise: impl Into<Option<bool>>) -> Result<Array> {
2072 let stream = Stream::thread_local_or_default();
2073 let precise = precise.into().unwrap_or(false);
2074 let s = stream.as_ref().as_ptr();
2075
2076 Array::try_from_op(|res| unsafe { mlx_sys::mlx_softmax(res, a.as_ref().as_ptr(), precise, s) })
2077}
2078
2079#[generate_macro(customize(forwarding_shim = true))]
2081#[deprecated(
2082 since = "0.26.0",
2083 note = "use `with_stream` or `with_device` around `softmax`"
2084)]
2085pub fn softmax_device(
2086 a: impl AsRef<Array>,
2087 precise: impl Into<Option<bool>>,
2088 #[optional] stream: impl AsRef<Stream>,
2089) -> Result<Array> {
2090 crate::with_stream(stream.as_ref(), || softmax(a, precise))
2091}
2092
2093pub fn sqrt(a: impl AsRef<Array>) -> Result<Array> {
2095 a.as_ref().sqrt()
2096}
2097
2098#[generate_macro(customize(forwarding_shim = true))]
2100#[deprecated(
2101 since = "0.26.0",
2102 note = "use `with_stream` or `with_device` around `sqrt`"
2103)]
2104pub fn sqrt_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2105 crate::with_stream(stream.as_ref(), || sqrt(a))
2106}
2107
2108pub fn square(a: impl AsRef<Array>) -> Result<Array> {
2110 a.as_ref().square()
2111}
2112
2113#[generate_macro(customize(forwarding_shim = true))]
2115#[deprecated(
2116 since = "0.26.0",
2117 note = "use `with_stream` or `with_device` around `square`"
2118)]
2119pub fn square_device(
2120 a: impl AsRef<Array>,
2121 #[optional] stream: impl AsRef<Stream>,
2122) -> Result<Array> {
2123 crate::with_stream(stream.as_ref(), || square(a))
2124}
2125
2126pub fn subtract(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
2128 a.as_ref().subtract(b)
2129}
2130
2131#[generate_macro(customize(forwarding_shim = true))]
2133#[deprecated(
2134 since = "0.26.0",
2135 note = "use `with_stream` or `with_device` around `subtract`"
2136)]
2137pub fn subtract_device(
2138 a: impl AsRef<Array>,
2139 b: impl AsRef<Array>,
2140 #[optional] stream: impl AsRef<Stream>,
2141) -> Result<Array> {
2142 crate::with_stream(stream.as_ref(), || subtract(a, b))
2143}
2144
2145pub fn tan(a: impl AsRef<Array>) -> Result<Array> {
2147 let stream = Stream::thread_local_or_default();
2148 Array::try_from_op(|res| unsafe {
2149 mlx_sys::mlx_tan(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2150 })
2151}
2152
2153#[generate_macro(customize(forwarding_shim = true))]
2155#[deprecated(
2156 since = "0.26.0",
2157 note = "use `with_stream` or `with_device` around `tan`"
2158)]
2159pub fn tan_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2160 crate::with_stream(stream.as_ref(), || tan(a))
2161}
2162
2163pub fn tanh(a: impl AsRef<Array>) -> Result<Array> {
2165 let stream = Stream::thread_local_or_default();
2166 Array::try_from_op(|res| unsafe {
2167 mlx_sys::mlx_tanh(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2168 })
2169}
2170
2171#[generate_macro(customize(forwarding_shim = true))]
2173#[deprecated(
2174 since = "0.26.0",
2175 note = "use `with_stream` or `with_device` around `tanh`"
2176)]
2177pub fn tanh_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2178 crate::with_stream(stream.as_ref(), || tanh(a))
2179}
2180
2181pub fn real(a: impl AsRef<Array>) -> Result<Array> {
2183 let stream = Stream::thread_local_or_default();
2184 Array::try_from_op(|res| unsafe {
2185 mlx_sys::mlx_real(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2186 })
2187}
2188
2189#[generate_macro(customize(forwarding_shim = true))]
2191#[deprecated(
2192 since = "0.26.0",
2193 note = "use `with_stream` or `with_device` around `real`"
2194)]
2195pub fn real_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2196 crate::with_stream(stream.as_ref(), || real(a))
2197}
2198
2199pub fn imag(a: impl AsRef<Array>) -> Result<Array> {
2201 let stream = Stream::thread_local_or_default();
2202 Array::try_from_op(|res| unsafe {
2203 mlx_sys::mlx_imag(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
2204 })
2205}
2206
2207#[generate_macro(customize(forwarding_shim = true))]
2209#[deprecated(
2210 since = "0.26.0",
2211 note = "use `with_stream` or `with_device` around `imag`"
2212)]
2213pub fn imag_device(a: impl AsRef<Array>, #[optional] stream: impl AsRef<Stream>) -> Result<Array> {
2214 crate::with_stream(stream.as_ref(), || imag(a))
2215}
2216
2217pub fn block_masked_mm<'mo, 'lhs, 'rhs>(
2223 a: impl AsRef<Array>,
2224 b: impl AsRef<Array>,
2225 block_size: impl Into<Option<i32>>,
2226 mask_out: impl Into<Option<&'mo Array>>,
2227 mask_lhs: impl Into<Option<&'lhs Array>>,
2228 mask_rhs: impl Into<Option<&'rhs Array>>,
2229) -> Result<Array> {
2230 let stream = Stream::thread_local_or_default();
2231 let a_ptr = a.as_ref().as_ptr();
2232 let b_ptr = b.as_ref().as_ptr();
2233 unsafe {
2234 let mask_out_ptr = mask_out
2235 .into()
2236 .map(|m| m.as_ptr())
2237 .unwrap_or(mlx_sys::mlx_array_new());
2238 let mask_lhs_ptr = mask_lhs
2239 .into()
2240 .map(|m| m.as_ptr())
2241 .unwrap_or(mlx_sys::mlx_array_new());
2242 let mask_rhs_ptr = mask_rhs
2243 .into()
2244 .map(|m| m.as_ptr())
2245 .unwrap_or(mlx_sys::mlx_array_new());
2246
2247 Array::try_from_op(|res| {
2248 mlx_sys::mlx_block_masked_mm(
2249 res,
2250 a_ptr,
2251 b_ptr,
2252 block_size.into().unwrap_or(32),
2253 mask_out_ptr,
2254 mask_lhs_ptr,
2255 mask_rhs_ptr,
2256 stream.as_ref().as_ptr(),
2257 )
2258 })
2259 }
2260}
2261
2262#[generate_macro(customize(forwarding_shim = true))]
2264#[deprecated(
2265 since = "0.26.0",
2266 note = "use `with_stream` or `with_device` around `block_masked_mm`"
2267)]
2268pub fn block_masked_mm_device<'mo, 'lhs, 'rhs>(
2269 a: impl AsRef<Array>,
2270 b: impl AsRef<Array>,
2271 #[optional] block_size: impl Into<Option<i32>>,
2272 #[optional] mask_out: impl Into<Option<&'mo Array>>,
2273 #[optional] mask_lhs: impl Into<Option<&'lhs Array>>,
2274 #[optional] mask_rhs: impl Into<Option<&'rhs Array>>,
2275 #[optional] stream: impl AsRef<Stream>,
2276) -> Result<Array> {
2277 crate::with_stream(stream.as_ref(), || {
2278 block_masked_mm(a, b, block_size, mask_out, mask_lhs, mask_rhs)
2279 })
2280}
2281
2282pub fn addmm(
2295 c: impl AsRef<Array>,
2296 a: impl AsRef<Array>,
2297 b: impl AsRef<Array>,
2298 alpha: impl Into<Option<f32>>,
2299 beta: impl Into<Option<f32>>,
2300) -> Result<Array> {
2301 let stream = Stream::thread_local_or_default();
2302 let c_ptr = c.as_ref().as_ptr();
2303 let a_ptr = a.as_ref().as_ptr();
2304 let b_ptr = b.as_ref().as_ptr();
2305 let alpha = alpha.into().unwrap_or(1.0);
2306 let beta = beta.into().unwrap_or(1.0);
2307
2308 Array::try_from_op(|res| unsafe {
2309 mlx_sys::mlx_addmm(
2310 res,
2311 c_ptr,
2312 a_ptr,
2313 b_ptr,
2314 alpha,
2315 beta,
2316 stream.as_ref().as_ptr(),
2317 )
2318 })
2319}
2320
2321#[generate_macro(customize(forwarding_shim = true))]
2323#[deprecated(
2324 since = "0.26.0",
2325 note = "use `with_stream` or `with_device` around `addmm`"
2326)]
2327pub fn addmm_device(
2328 c: impl AsRef<Array>,
2329 a: impl AsRef<Array>,
2330 b: impl AsRef<Array>,
2331 #[optional] alpha: impl Into<Option<f32>>,
2332 #[optional] beta: impl Into<Option<f32>>,
2333 #[optional] stream: impl AsRef<Stream>,
2334) -> Result<Array> {
2335 crate::with_stream(stream.as_ref(), || addmm(c, a, b, alpha, beta))
2336}
2337
2338pub fn inner(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
2341 let stream = Stream::thread_local_or_default();
2342 let a = a.as_ref();
2343 let b = b.as_ref();
2344 Array::try_from_op(|res| unsafe {
2345 mlx_sys::mlx_inner(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
2346 })
2347}
2348
2349#[generate_macro(customize(forwarding_shim = true))]
2351#[deprecated(
2352 since = "0.26.0",
2353 note = "use `with_stream` or `with_device` around `inner`"
2354)]
2355pub fn inner_device(
2356 a: impl AsRef<Array>,
2357 b: impl AsRef<Array>,
2358 #[optional] stream: impl AsRef<Stream>,
2359) -> Result<Array> {
2360 crate::with_stream(stream.as_ref(), || inner(a, b))
2361}
2362
2363pub fn outer(a: impl AsRef<Array>, b: impl AsRef<Array>) -> Result<Array> {
2366 let stream = Stream::thread_local_or_default();
2367 let a = a.as_ref();
2368 let b = b.as_ref();
2369 Array::try_from_op(|res| unsafe {
2370 mlx_sys::mlx_outer(res, a.as_ptr(), b.as_ptr(), stream.as_ref().as_ptr())
2371 })
2372}
2373
2374#[generate_macro(customize(forwarding_shim = true))]
2376#[deprecated(
2377 since = "0.26.0",
2378 note = "use `with_stream` or `with_device` around `outer`"
2379)]
2380pub fn outer_device(
2381 a: impl AsRef<Array>,
2382 b: impl AsRef<Array>,
2383 #[optional] stream: impl AsRef<Stream>,
2384) -> Result<Array> {
2385 crate::with_stream(stream.as_ref(), || outer(a, b))
2386}
2387
2388pub fn tensordot_axes(
2390 a: impl AsRef<Array>,
2391 b: impl AsRef<Array>,
2392 axes_a: &[i32],
2393 axes_b: &[i32],
2394) -> Result<Array> {
2395 let stream = Stream::thread_local_or_default();
2396 let a = a.as_ref();
2397 let b = b.as_ref();
2398 Array::try_from_op(|res| unsafe {
2399 mlx_sys::mlx_tensordot(
2400 res,
2401 a.as_ptr(),
2402 b.as_ptr(),
2403 axes_a.as_ptr(),
2404 axes_a.len(),
2405 axes_b.as_ptr(),
2406 axes_b.len(),
2407 stream.as_ref().as_ptr(),
2408 )
2409 })
2410}
2411
2412#[generate_macro(customize(forwarding_shim = true))]
2414#[deprecated(
2415 since = "0.26.0",
2416 note = "use `with_stream` or `with_device` around `tensordot_axes`"
2417)]
2418pub fn tensordot_axes_device(
2419 a: impl AsRef<Array>,
2420 b: impl AsRef<Array>,
2421 axes_a: &[i32],
2422 axes_b: &[i32],
2423 #[optional] stream: impl AsRef<Stream>,
2424) -> Result<Array> {
2425 crate::with_stream(stream.as_ref(), || tensordot_axes(a, b, axes_a, axes_b))
2426}
2427
2428pub fn tensordot_axis(a: impl AsRef<Array>, b: impl AsRef<Array>, axis: i32) -> Result<Array> {
2430 let stream = Stream::thread_local_or_default();
2431 let a = a.as_ref();
2432 let b = b.as_ref();
2433 Array::try_from_op(|res| unsafe {
2434 mlx_sys::mlx_tensordot_axis(res, a.as_ptr(), b.as_ptr(), axis, stream.as_ref().as_ptr())
2435 })
2436}
2437
2438#[generate_macro(customize(forwarding_shim = true))]
2440#[deprecated(
2441 since = "0.26.0",
2442 note = "use `with_stream` or `with_device` around `tensordot_axis`"
2443)]
2444pub fn tensordot_axis_device(
2445 a: impl AsRef<Array>,
2446 b: impl AsRef<Array>,
2447 axis: i32,
2448 #[optional] stream: impl AsRef<Stream>,
2449) -> Result<Array> {
2450 crate::with_stream(stream.as_ref(), || tensordot_axis(a, b, axis))
2451}
2452
2453pub fn gather_mm<'lhs, 'rhs>(
2481 a: impl AsRef<Array>,
2482 b: impl AsRef<Array>,
2483 lhs_indices: impl Into<Option<&'lhs Array>>,
2484 rhs_indices: impl Into<Option<&'rhs Array>>,
2485 sorted_indices: impl Into<Option<bool>>,
2486) -> Result<Array> {
2487 let stream = Stream::thread_local_or_default();
2488 let a_ptr = a.as_ref().as_ptr();
2489 let b_ptr = b.as_ref().as_ptr();
2490 let sorted = sorted_indices.into().unwrap_or(false);
2491
2492 unsafe {
2493 let lhs_ptr = lhs_indices
2494 .into()
2495 .map(|i| i.as_ptr())
2496 .unwrap_or(mlx_sys::mlx_array_new());
2497 let rhs_ptr = rhs_indices
2498 .into()
2499 .map(|i| i.as_ptr())
2500 .unwrap_or(mlx_sys::mlx_array_new());
2501
2502 Array::try_from_op(|res| {
2503 mlx_sys::mlx_gather_mm(
2504 res,
2505 a_ptr,
2506 b_ptr,
2507 lhs_ptr,
2508 rhs_ptr,
2509 sorted,
2510 stream.as_ref().as_ptr(),
2511 )
2512 })
2513 }
2514}
2515
2516#[generate_macro(customize(forwarding_shim = true))]
2518#[deprecated(
2519 since = "0.26.0",
2520 note = "use `with_stream` or `with_device` around `gather_mm`"
2521)]
2522pub fn gather_mm_device<'lhs, 'rhs>(
2523 a: impl AsRef<Array>,
2524 b: impl AsRef<Array>,
2525 #[optional] lhs_indices: impl Into<Option<&'lhs Array>>,
2526 #[optional] rhs_indices: impl Into<Option<&'rhs Array>>,
2527 #[optional] sorted_indices: impl Into<Option<bool>>,
2528 #[optional] stream: impl AsRef<Stream>,
2529) -> Result<Array> {
2530 crate::with_stream(stream.as_ref(), || {
2531 gather_mm(a, b, lhs_indices, rhs_indices, sorted_indices)
2532 })
2533}
2534
2535#[cfg(test)]
2536mod tests {
2537 use std::f32::consts::PI;
2538
2539 use super::*;
2540 use crate::{
2541 array, complex64,
2542 ops::{all_close, arange, broadcast_to, eye, full, linspace, ones, reshape, split_equal},
2543 test_utils::{assert_array_eq, assert_array_eq_with_context, tolerances},
2544 transforms::eval,
2545 Dtype,
2546 };
2547 use float_eq::assert_float_eq;
2548 use pretty_assertions::assert_eq;
2549
2550 #[test]
2551 fn test_abs() {
2552 let data = [1i32, 2, -3, -4, -5];
2553 let array = Array::from_slice(&data, &[5]);
2554 let result = array.abs().unwrap();
2555
2556 let data: &[i32] = result.as_slice();
2557 assert_eq!(data, [1, 2, 3, 4, 5]);
2558
2559 let data: &[i32] = array.as_slice();
2561 assert_eq!(data, [1, 2, -3, -4, -5]);
2562 }
2563
2564 #[test]
2565 fn test_add() {
2566 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2567 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2568
2569 let c = &a + &b;
2570
2571 let c_data: &[f32] = c.as_slice();
2572 assert_eq!(c_data, &[5.0, 7.0, 9.0]);
2573
2574 let a_data: &[f32] = a.as_slice();
2576 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2577
2578 let b_data: &[f32] = b.as_slice();
2579 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2580 }
2581
2582 #[test]
2583 fn test_add_invalid_broadcast() {
2584 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2585 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2586
2587 let c = a.add(&b);
2588 assert!(c.is_err());
2589 }
2590
2591 #[test]
2592 fn test_sub() {
2593 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2594 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2595
2596 let c = &a - &b;
2597
2598 let c_data: &[f32] = c.as_slice();
2599 assert_eq!(c_data, &[-3.0, -3.0, -3.0]);
2600
2601 let a_data: &[f32] = a.as_slice();
2603 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2604
2605 let b_data: &[f32] = b.as_slice();
2606 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2607 }
2608
2609 #[test]
2610 fn test_sub_invalid_broadcast() {
2611 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2612 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2613 let c = a.subtract(&b);
2614 assert!(c.is_err());
2615 }
2616
2617 #[test]
2618 fn test_neg() {
2619 let a = Array::from_slice::<f32>(&[1.0, 2.0, 3.0], &[3]);
2620 let b = a.negative().unwrap();
2621
2622 let b_data: &[f32] = b.as_slice();
2623 assert_eq!(b_data, &[-1.0, -2.0, -3.0]);
2624
2625 let a_data: &[f32] = a.as_slice();
2627 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2628 }
2629
2630 #[test]
2631 fn test_neg_bool() {
2632 let a = Array::from_slice(&[true, false, true], &[3]);
2633 let b = a.negative();
2634 assert!(b.is_err());
2635 }
2636
2637 #[test]
2638 fn test_logical_not() {
2639 let a: Array = false.into();
2640 let b = a.logical_not().unwrap();
2641
2642 let b_data: &[bool] = b.as_slice();
2643 assert_eq!(b_data, [true]);
2644 }
2645
2646 #[test]
2647 fn test_mul() {
2648 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2649 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2650
2651 let c = &a * &b;
2652
2653 let c_data: &[f32] = c.as_slice();
2654 assert_eq!(c_data, &[4.0, 10.0, 18.0]);
2655
2656 let a_data: &[f32] = a.as_slice();
2658 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2659
2660 let b_data: &[f32] = b.as_slice();
2661 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2662 }
2663
2664 #[test]
2665 fn test_mul_invalid_broadcast() {
2666 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2667 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2668 let c = a.multiply(&b);
2669 assert!(c.is_err());
2670 }
2671
2672 #[test]
2673 fn test_nan_to_num() {
2674 let a = array!([1.0, 2.0, f32::NAN, 4.0, 5.0]);
2675 let b = a.nan_to_num(0.0, 1.0, 0.0).unwrap();
2676
2677 let b_data: &[f32] = b.as_slice();
2678 assert_eq!(b_data, &[1.0, 2.0, 0.0, 4.0, 5.0]);
2679 }
2680
2681 #[test]
2682 fn test_div() {
2683 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2684 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2685
2686 let c = &a / &b;
2687
2688 let c_data: &[f32] = c.as_slice();
2689 assert_eq!(c_data, &[0.25, 0.4, 0.5]);
2690
2691 let a_data: &[f32] = a.as_slice();
2693 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2694
2695 let b_data: &[f32] = b.as_slice();
2696 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2697 }
2698
2699 #[test]
2700 fn test_div_invalid_broadcast() {
2701 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2702 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2703 let c = a.divide(&b);
2704 assert!(c.is_err());
2705 }
2706
2707 #[test]
2708 fn test_pow() {
2709 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2710 let b = Array::from_slice(&[2.0, 3.0, 4.0], &[3]);
2711
2712 let c = a.power(&b).unwrap();
2713
2714 assert_array_eq(
2715 &c,
2716 array!([1.0, 8.0, 81.0]),
2717 tolerances::MLX_DEFAULT.rtol,
2718 tolerances::MLX_DEFAULT.atol,
2719 );
2720
2721 let a_data: &[f32] = a.as_slice();
2723 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2724
2725 let b_data: &[f32] = b.as_slice();
2726 assert_eq!(b_data, &[2.0, 3.0, 4.0]);
2727 }
2728
2729 #[test]
2730 fn test_pow_invalid_broadcast() {
2731 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2732 let b = Array::from_slice(&[2.0, 3.0], &[2]);
2733 let c = a.power(&b);
2734 assert!(c.is_err());
2735 }
2736
2737 #[test]
2738 fn test_rem() {
2739 let a = Array::from_slice(&[10.0, 11.0, 12.0], &[3]);
2740 let b = Array::from_slice(&[3.0, 4.0, 5.0], &[3]);
2741
2742 let c = &a % &b;
2743
2744 let c_data: &[f32] = c.as_slice();
2745 assert_eq!(c_data, &[1.0, 3.0, 2.0]);
2746
2747 let a_data: &[f32] = a.as_slice();
2749 assert_eq!(a_data, &[10.0, 11.0, 12.0]);
2750
2751 let b_data: &[f32] = b.as_slice();
2752 assert_eq!(b_data, &[3.0, 4.0, 5.0]);
2753 }
2754
2755 #[test]
2756 fn test_rem_invalid_broadcast() {
2757 let a = Array::from_slice(&[10.0, 11.0, 12.0], &[3]);
2758 let b = Array::from_slice(&[3.0, 4.0], &[2]);
2759 let c = a.remainder(&b);
2760 assert!(c.is_err());
2761 }
2762
2763 #[test]
2764 fn test_sqrt() {
2765 let a = Array::from_slice(&[1.0, 4.0, 9.0], &[3]);
2766 let b = a.sqrt().unwrap();
2767
2768 let b_data: &[f32] = b.as_slice();
2769 assert_eq!(b_data, &[1.0, 2.0, 3.0]);
2770
2771 let a_data: &[f32] = a.as_slice();
2773 assert_eq!(a_data, &[1.0, 4.0, 9.0]);
2774 }
2775
2776 #[test]
2777 fn test_cos() {
2778 let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
2779 let b = a.cos().unwrap();
2780
2781 let b_expected = array!([1.0, 0.54030234, -0.41614687]);
2782 assert_array_all_close!(b, b_expected);
2783
2784 let a_expected = array!([0.0, 1.0, 2.0]);
2786 assert_array_all_close!(a, a_expected);
2787 }
2788
2789 #[test]
2790 fn test_exp() {
2791 let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
2792 let b = a.exp().unwrap();
2793
2794 let b_expected = array!([1.0, 2.7182817, 7.389056]);
2795 assert_array_all_close!(b, b_expected);
2796
2797 let a_expected = array!([0.0, 1.0, 2.0]);
2799 assert_array_all_close!(a, a_expected);
2800 }
2801
2802 #[test]
2803 fn test_floor() {
2804 let a = Array::from_slice(&[0.1, 1.9, 2.5], &[3]);
2805 let b = a.floor().unwrap();
2806
2807 let b_data: &[f32] = b.as_slice();
2808 assert_eq!(b_data, &[0.0, 1.0, 2.0]);
2809
2810 let a_data: &[f32] = a.as_slice();
2812 assert_eq!(a_data, &[0.1, 1.9, 2.5]);
2813 }
2814
2815 #[test]
2816 fn test_floor_complex64() {
2817 let val = complex64::new(1.0, 2.0);
2818 let a = Array::from_complex(val);
2819 let b = a.floor();
2820 assert!(b.is_err());
2821 }
2822
2823 #[test]
2824 fn test_floor_divide() {
2825 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2826 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2827
2828 let c = a.floor_divide(&b).unwrap();
2829
2830 let c_data: &[f32] = c.as_slice();
2831 assert_eq!(c_data, &[0.0, 0.0, 0.0]);
2832
2833 let a_data: &[f32] = a.as_slice();
2835 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2836
2837 let b_data: &[f32] = b.as_slice();
2838 assert_eq!(b_data, &[4.0, 5.0, 6.0]);
2839 }
2840
2841 #[test]
2842 fn test_floor_divide_complex64() {
2843 let val = complex64::new(1.0, 2.0);
2844 let a = Array::from_complex(val);
2845 let b = Array::from_slice(&[4.0, 5.0, 6.0], &[3]);
2846 let c = a.floor_divide(&b);
2847 assert!(c.is_err());
2848 }
2849
2850 #[test]
2851 fn test_floor_divide_invalid_broadcast() {
2852 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2853 let b = Array::from_slice(&[4.0, 5.0], &[2]);
2854 let c = a.floor_divide(&b);
2855 assert!(c.is_err());
2856 }
2857
2858 #[test]
2859 fn test_is_nan() {
2860 let a = Array::from_slice(&[1.0, f32::NAN, 3.0], &[3]);
2861 let b = a.is_nan().unwrap();
2862
2863 let b_data: &[bool] = b.as_slice();
2864 assert_eq!(b_data, &[false, true, false]);
2865 }
2866
2867 #[test]
2868 fn test_is_inf() {
2869 let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
2870 let b = a.is_inf().unwrap();
2871
2872 let b_data: &[bool] = b.as_slice();
2873 assert_eq!(b_data, &[false, true, false]);
2874 }
2875
2876 #[test]
2877 fn test_is_finite() {
2878 let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
2879 let b = a.is_finite().unwrap();
2880
2881 let b_data: &[bool] = b.as_slice();
2882 assert_eq!(b_data, &[true, false, true]);
2883 }
2884
2885 #[test]
2886 fn test_is_neg_inf() {
2887 let a = Array::from_slice(&[1.0, f32::NEG_INFINITY, 3.0], &[3]);
2888 let b = a.is_neg_inf().unwrap();
2889
2890 let b_data: &[bool] = b.as_slice();
2891 assert_eq!(b_data, &[false, true, false]);
2892 }
2893
2894 #[test]
2895 fn test_is_pos_inf() {
2896 let a = Array::from_slice(&[1.0, f32::INFINITY, 3.0], &[3]);
2897 let b = a.is_pos_inf().unwrap();
2898
2899 let b_data: &[bool] = b.as_slice();
2900 assert_eq!(b_data, &[false, true, false]);
2901 }
2902
2903 #[test]
2904 fn test_log() {
2905 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2906 let b = a.log().unwrap();
2907
2908 let b_data: &[f32] = b.as_slice();
2909 assert_eq!(b_data, &[0.0, 0.6931472, 1.0986123]);
2910
2911 let a_data: &[f32] = a.as_slice();
2913 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2914 }
2915
2916 #[test]
2917 fn test_log2() {
2918 let a = Array::from_slice(&[1.0, 2.0, 4.0, 8.0], &[4]);
2919 let b = a.log2().unwrap();
2920
2921 let b_data: &[f32] = b.as_slice();
2922 assert_eq!(b_data, &[0.0, 1.0, 2.0, 3.0]);
2923
2924 let a_data: &[f32] = a.as_slice();
2926 assert_eq!(a_data, &[1.0, 2.0, 4.0, 8.0]);
2927 }
2928
2929 #[test]
2930 fn test_log10() {
2931 let a = Array::from_slice(&[1.0, 10.0, 100.0], &[3]);
2932 let b = a.log10().unwrap();
2933
2934 let b_data: &[f32] = b.as_slice();
2935 assert_eq!(b_data, &[0.0, 1.0, 2.0]);
2936
2937 let a_data: &[f32] = a.as_slice();
2939 assert_eq!(a_data, &[1.0, 10.0, 100.0]);
2940 }
2941
2942 #[test]
2943 fn test_log1p() {
2944 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
2945 let b = a.log1p().unwrap();
2946
2947 assert_array_eq(
2948 &b,
2949 array!([0.6931472, 1.0986123, 1.3862944]),
2950 tolerances::MLX_DEFAULT.rtol,
2951 tolerances::MLX_DEFAULT.atol,
2952 );
2953
2954 let a_data: &[f32] = a.as_slice();
2956 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
2957 }
2958
2959 #[test]
2960 fn test_matmul() {
2961 let a = Array::from_slice(&[1, 2, 3, 4], &[2, 2]);
2962 let b = Array::from_slice(&[-5.0, 37.5, 4., 7., 1., 0.], &[2, 3]);
2963
2964 let c = a.matmul(&b).unwrap();
2965
2966 assert_eq!(c.shape(), &[2, 3]);
2967 let c_data: &[f32] = c.as_slice();
2968 assert_eq!(c_data, &[9.0, 39.5, 4.0, 13.0, 116.5, 12.0]);
2969
2970 let a_data: &[i32] = a.as_slice();
2972 assert_eq!(a_data, &[1, 2, 3, 4]);
2973
2974 let b_data: &[f32] = b.as_slice();
2975 assert_eq!(b_data, &[-5.0, 37.5, 4., 7., 1., 0.]);
2976 }
2977
2978 #[test]
2979 fn test_matmul_ndim_zero() {
2980 let a: Array = 1.0.into();
2981 let b = Array::from_slice::<i32>(&[1], &[1]);
2982 let c = a.matmul(&b);
2983 assert!(c.is_err());
2984 }
2985
2986 #[test]
2987 fn test_matmul_ndim_one() {
2988 let a = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4]);
2989 let b = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[4]);
2990 let c = a.matmul(&b);
2991 assert!(c.is_ok());
2992 }
2993
2994 #[test]
2995 fn test_matmul_dim_mismatch() {
2996 let a = Array::from_slice(&[1, 2, 3, 4, 5, 6], &[2, 3]);
2997 let b = Array::from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], &[2, 5]);
2998 let c = a.matmul(&b);
2999 assert!(c.is_err());
3000 }
3001
3002 #[test]
3003 fn test_matmul_non_float_output_type() {
3004 let a = Array::from_slice(&[1, 2, 3, 4], &[2, 2]);
3005 let b = Array::from_slice(&[5, 37, 4, 7, 1, 0], &[2, 3]);
3006
3007 let c = a.matmul(&b);
3008 assert!(c.is_err());
3009 }
3010
3011 #[test]
3012 fn test_reciprocal() {
3013 let a = Array::from_slice(&[1.0, 2.0, 4.0], &[3]);
3014 let b = a.reciprocal().unwrap();
3015
3016 let b_data: &[f32] = b.as_slice();
3017 assert_eq!(b_data, &[1.0, 0.5, 0.25]);
3018
3019 let a_data: &[f32] = a.as_slice();
3021 assert_eq!(a_data, &[1.0, 2.0, 4.0]);
3022 }
3023
3024 #[test]
3025 fn test_round() {
3026 let a = Array::from_slice(&[1.1, 2.9, 3.5], &[3]);
3027 let b = a.round(None).unwrap();
3028
3029 let b_data: &[f32] = b.as_slice();
3030 assert_eq!(b_data, &[1.0, 3.0, 4.0]);
3031
3032 let a_data: &[f32] = a.as_slice();
3034 assert_eq!(a_data, &[1.1, 2.9, 3.5]);
3035 }
3036
3037 #[test]
3038 fn test_rsqrt() {
3039 let a = Array::from_slice(&[1.0, 2.0, 4.0], &[3]);
3040 let b = a.rsqrt().unwrap();
3041
3042 let b_data: &[f32] = b.as_slice();
3043 assert_eq!(b_data, &[1.0, 0.70710677, 0.5]);
3044
3045 let a_data: &[f32] = a.as_slice();
3047 assert_eq!(a_data, &[1.0, 2.0, 4.0]);
3048 }
3049
3050 #[test]
3051 fn test_sin() {
3052 let a = Array::from_slice(&[0.0, 1.0, 2.0], &[3]);
3053 let b = a.sin().unwrap();
3054
3055 assert_array_eq(
3056 &b,
3057 array!([0.0, 0.841471, 0.9092974]),
3058 tolerances::MLX_DEFAULT.rtol,
3059 tolerances::MLX_DEFAULT.atol,
3060 );
3061
3062 let a_data: &[f32] = a.as_slice();
3064 assert_eq!(a_data, &[0.0, 1.0, 2.0]);
3065 }
3066
3067 #[test]
3068 fn test_square() {
3069 let a = Array::from_slice(&[1.0, 2.0, 3.0], &[3]);
3070 let b = a.square().unwrap();
3071
3072 let b_data: &[f32] = b.as_slice();
3073 assert_eq!(b_data, &[1.0, 4.0, 9.0]);
3074
3075 let a_data: &[f32] = a.as_slice();
3077 assert_eq!(a_data, &[1.0, 2.0, 3.0]);
3078 }
3079
3080 #[test]
3083 fn test_unary_neg() {
3084 let x = array!(1.0);
3085 assert_eq!(negative(&x).unwrap().item_exact::<f32>(), -1.0);
3086 assert_eq!((-x).item_exact::<f32>(), -1.0);
3087
3088 assert_array_eq(
3090 -array!(),
3091 array!(),
3092 tolerances::EXACT.rtol,
3093 tolerances::EXACT.atol,
3094 );
3095
3096 let x = array!(true);
3098 assert!(negative(&x).is_err());
3099 }
3100
3101 #[test]
3102 fn test_unary_abs() {
3103 let x = array!([-1.0, 0.0, 1.0]);
3104 assert_array_eq(
3105 abs(&x).unwrap(),
3106 array!([1.0, 0.0, 1.0]),
3107 tolerances::EXACT.rtol,
3108 tolerances::EXACT.atol,
3109 );
3110
3111 assert_array_eq(
3113 abs(array!()).unwrap(),
3114 array!(),
3115 tolerances::EXACT.rtol,
3116 tolerances::EXACT.atol,
3117 );
3118
3119 let x = array!([-1, 0, 1]);
3121 assert_array_eq(
3122 abs(&x).unwrap(),
3123 array!([1, 0, 1]),
3124 tolerances::EXACT.rtol,
3125 tolerances::EXACT.atol,
3126 );
3127
3128 let x = array!([1u32, 0, 1]);
3130 assert_array_eq(
3131 abs(&x).unwrap(),
3132 array!([1u32, 0, 1]),
3133 tolerances::EXACT.rtol,
3134 tolerances::EXACT.atol,
3135 );
3136
3137 let x = array!([false, true]);
3139 assert_array_eq(
3140 abs(&x).unwrap(),
3141 array!([false, true]),
3142 tolerances::EXACT.rtol,
3143 tolerances::EXACT.atol,
3144 );
3145 }
3146
3147 #[test]
3148 fn test_unary_sign() {
3149 let x = array!([-1.0, 0.0, 1.0]);
3150 assert_array_eq(
3151 sign(&x).unwrap(),
3152 x,
3153 tolerances::EXACT.rtol,
3154 tolerances::EXACT.atol,
3155 );
3156
3157 assert_array_eq(
3159 sign(array!()).unwrap(),
3160 array!(),
3161 tolerances::EXACT.rtol,
3162 tolerances::EXACT.atol,
3163 );
3164
3165 let x = array!([-1, 0, 1]);
3167 assert_array_eq(
3168 sign(&x).unwrap(),
3169 x,
3170 tolerances::EXACT.rtol,
3171 tolerances::EXACT.atol,
3172 );
3173
3174 let x = array!([1u32, 0, 1]);
3176 assert_array_eq(
3177 sign(&x).unwrap(),
3178 x,
3179 tolerances::EXACT.rtol,
3180 tolerances::EXACT.atol,
3181 );
3182
3183 let x = array!([false, true]);
3185 assert_array_eq(
3186 sign(&x).unwrap(),
3187 x,
3188 tolerances::EXACT.rtol,
3189 tolerances::EXACT.atol,
3190 );
3191 }
3192
3193 const NEG_INF: f32 = f32::NEG_INFINITY;
3194
3195 #[test]
3196 fn test_unary_floor_ceil() {
3197 let x = array![1.0];
3198 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), 1.0);
3199 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), 1.0);
3200
3201 let x = array![1.5];
3202 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), 1.0);
3203 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), 2.0);
3204
3205 let x = array![-1.5];
3206 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), -2.0);
3207 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), -1.0);
3208
3209 let x = array![NEG_INF];
3210 assert_eq!(floor(&x).unwrap().item_exact::<f32>(), NEG_INF);
3211 assert_eq!(ceil(&x).unwrap().item_exact::<f32>(), NEG_INF);
3212
3213 let x = array!([1.0, 1.0]).as_type::<complex64>().unwrap();
3214 assert!(floor(&x).is_err());
3215 assert!(ceil(&x).is_err());
3216 }
3217
3218 #[test]
3219 fn test_unary_round() {
3220 let x = array!([0.5, -0.5, 1.5, -1.5, 2.3, 2.6]);
3221 assert_array_eq_with_context(
3222 round(&x, None).unwrap(),
3223 array!([0.0_f32, 0.0, 2.0, -2.0, 2.0, 3.0]),
3224 tolerances::EXACT.rtol,
3225 tolerances::EXACT.atol,
3226 "float32 half-to-even rounding",
3227 );
3228
3229 let x = array!([11, 222, 32]);
3230 assert_array_eq_with_context(
3231 round(&x, -1).unwrap(),
3232 array!([10, 220, 30]),
3233 tolerances::EXACT.rtol,
3234 tolerances::EXACT.atol,
3235 "int32 negative-decimal rounding",
3236 );
3237 }
3238
3239 #[test]
3240 fn test_unary_exp() {
3241 let x = array![0.0];
3242 assert_eq!(exp(&x).unwrap().item_exact::<f32>(), 1.0);
3243
3244 let x = array![2.0];
3245 assert_float_eq! {
3246 exp(&x).unwrap().item_exact::<f32>(),
3247 2.0f32.exp(),
3248 abs <= 1e-5
3249 };
3250
3251 assert_array_eq(
3252 exp(array!()).unwrap(),
3253 array!(),
3254 tolerances::EXACT.rtol,
3255 tolerances::EXACT.atol,
3256 );
3257
3258 let x = array![NEG_INF];
3259 assert_eq!(exp(&x).unwrap().item_exact::<f32>(), 0.0);
3260
3261 let x = array![2];
3263 assert_eq!(x.dtype(), Dtype::Int32);
3264 assert_float_eq! {
3265 exp(&x).unwrap().item_exact::<f32>(),
3266 2.0f32.exp(),
3267 abs <= 1e-5
3268 };
3269
3270 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3272 let res = exp(&x).unwrap();
3273 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.exp())).unwrap();
3274 assert!(all_close(&res, &expected, None, None, None).unwrap());
3275
3276 let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
3277 let x = split_equal(&data, 2, 1).unwrap();
3278 let expected = Array::from_slice(&[0.0f32.exp(), 2.0f32.exp()], &[2, 1]);
3279 assert!(all_close(exp(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3280 }
3281
3282 #[test]
3283 fn test_unary_expm1() {
3284 let x = array![-1.0];
3285 assert_float_eq! {
3286 expm1(&x).unwrap().item_exact::<f32>(),
3287 (-1.0f32).exp_m1(),
3288 abs <= 1e-5
3289 };
3290
3291 let x = array![1.0];
3292 assert_float_eq! {
3293 expm1(&x).unwrap().item_exact::<f32>(),
3294 1.0f32.exp_m1(),
3295 abs <= 1e-5
3296 };
3297
3298 let x = array![1];
3300 assert_eq!(expm1(&x).unwrap().dtype(), Dtype::Float32);
3301 assert_float_eq! {
3302 expm1(&x).unwrap().item_exact::<f32>(),
3303 1.0f32.exp_m1(),
3304 abs <= 1e-5
3305 };
3306 }
3307
3308 #[test]
3309 fn test_unary_sin() {
3310 let x = array![0.0];
3311 assert_eq!(sin(&x).unwrap().item_exact::<f32>(), 0.0);
3312
3313 let x = array![std::f32::consts::PI / 2.0];
3314 assert_float_eq! {
3315 sin(&x).unwrap().item_exact::<f32>(),
3316 (std::f32::consts::PI / 2.0f32).sin(),
3317 abs <= 1e-5
3318 };
3319
3320 assert_array_eq(
3321 sin(array!()).unwrap(),
3322 array!(),
3323 tolerances::EXACT.rtol,
3324 tolerances::EXACT.atol,
3325 );
3326
3327 let x = array![0];
3329 assert_eq!(x.dtype(), Dtype::Int32);
3330 assert_float_eq! {
3331 sin(&x).unwrap().item_exact::<f32>(),
3332 0.0f32.sin(),
3333 abs <= 1e-5
3334 };
3335
3336 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3338 let res = sin(&x).unwrap();
3339 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.sin())).unwrap();
3340 assert!(all_close(&res, &expected, None, None, None).unwrap());
3341
3342 let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
3343 let x = split_equal(&data, 2, 1).unwrap();
3344 let expected = Array::from_slice(&[0.0f32.sin(), 2.0f32.sin()], &[2, 1]);
3345 assert!(all_close(sin(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3346 }
3347
3348 #[test]
3349 fn test_unary_cos() {
3350 let x = array![0.0];
3351 assert_float_eq! {
3352 cos(&x).unwrap().item_exact::<f32>(),
3353 0.0f32.cos(),
3354 abs <= 1e-5
3355 };
3356
3357 let x = array![std::f32::consts::PI / 2.0];
3358 assert_float_eq! {
3359 cos(&x).unwrap().item_exact::<f32>(),
3360 (std::f32::consts::PI / 2.0f32).cos(),
3361 abs <= 1e-5
3362 };
3363
3364 assert_array_eq(
3365 cos(array!()).unwrap(),
3366 array!(),
3367 tolerances::EXACT.rtol,
3368 tolerances::EXACT.atol,
3369 );
3370
3371 let x = array![0];
3373 assert_eq!(x.dtype(), Dtype::Int32);
3374 assert_float_eq! {
3375 cos(&x).unwrap().item_exact::<f32>(),
3376 0.0f32.cos(),
3377 abs <= 1e-5
3378 };
3379
3380 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3382 let res = cos(&x).unwrap();
3383 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.cos())).unwrap();
3384 assert!(all_close(&res, &expected, None, None, None).unwrap());
3385
3386 let data = Array::from_slice(&[0.0, 1.0, 2.0, 3.0], &[2, 2]);
3387 let x = split_equal(&data, 2, 1).unwrap();
3388 let expected = Array::from_slice(&[0.0f32.cos(), 2.0f32.cos()], &[2, 1]);
3389 assert!(all_close(cos(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3390 }
3391
3392 #[test]
3393 fn test_unary_degrees() {
3394 let x = array![0.0];
3395 assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 0.0);
3396
3397 let x = array![std::f32::consts::PI / 2.0];
3398 assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 90.0);
3399
3400 assert_array_eq(
3401 degrees(array!()).unwrap(),
3402 array!(),
3403 tolerances::EXACT.rtol,
3404 tolerances::EXACT.atol,
3405 );
3406
3407 let x = array![0];
3409 assert_eq!(x.dtype(), Dtype::Int32);
3410 assert_eq!(degrees(&x).unwrap().item_exact::<f32>(), 0.0);
3411
3412 let x = broadcast_to(&array!(std::f32::consts::PI / 2.0), &[2, 2, 2]).unwrap();
3414 let res = degrees(&x).unwrap();
3415 let expected = Array::full::<f32>(&[2, 2, 2], array!(90.0)).unwrap();
3416 assert!(all_close(&res, &expected, None, None, None).unwrap());
3417
3418 let angles = Array::from_slice(&[0.0, PI / 2.0, PI, 1.5 * PI], &[2, 2]);
3419 let x = split_equal(&angles, 2, 1).unwrap();
3420 let expected = Array::from_slice(&[0.0, 180.0], &[2, 1]);
3421 assert!(all_close(degrees(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3422 }
3423
3424 #[test]
3425 fn test_unary_radians() {
3426 let x = array![0.0];
3427 assert_eq!(radians(&x).unwrap().item_exact::<f32>(), 0.0);
3428
3429 let x = array![90.0];
3430 assert_eq!(
3431 radians(&x).unwrap().item_exact::<f32>(),
3432 std::f32::consts::PI / 2.0
3433 );
3434
3435 assert_array_eq(
3436 radians(array!()).unwrap(),
3437 array!(),
3438 tolerances::EXACT.rtol,
3439 tolerances::EXACT.atol,
3440 );
3441
3442 let x = array![90];
3444 assert_eq!(x.dtype(), Dtype::Int32);
3445 assert_eq!(
3446 radians(&x).unwrap().item_exact::<f32>(),
3447 std::f32::consts::PI / 2.0
3448 );
3449
3450 let x = broadcast_to(&array!(90.0), &[2, 2, 2]).unwrap();
3452 let res = radians(&x).unwrap();
3453 let expected = Array::full::<f32>(&[2, 2, 2], array!(std::f32::consts::PI / 2.0)).unwrap();
3454 assert!(all_close(&res, &expected, None, None, None).unwrap());
3455
3456 let angles = Array::from_slice(&[0.0, 90.0, 180.0, 270.0], &[2, 2]);
3457 let x = split_equal(&angles, 2, 1).unwrap();
3458 let expected = Array::from_slice(&[0.0, PI], &[2, 1]);
3459 assert!(all_close(radians(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3460 }
3461
3462 #[test]
3463 fn test_unary_log() {
3464 let x = array![0.0];
3465 assert_eq!(log(&x).unwrap().item_exact::<f32>(), NEG_INF);
3466
3467 let x = array![1.0];
3468 assert_eq!(log(&x).unwrap().item_exact::<f32>(), 0.0);
3469
3470 let x = array![1];
3472 assert_eq!(log(&x).unwrap().dtype(), Dtype::Float32);
3473 assert_eq!(log(&x).unwrap().item_exact::<f32>(), 0.0);
3474
3475 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3477 let res = log(&x).unwrap();
3478 let expected = Array::full::<f32>(&[2, 2, 2], array!(0.0)).unwrap();
3479 assert!(all_close(&res, &expected, None, None, None).unwrap());
3480
3481 let data = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
3482 let x = split_equal(&data, 2, 1).unwrap();
3483 let expected = Array::from_slice(&[1.0f32.ln(), 3.0f32.ln()], &[2, 1]);
3484 assert!(all_close(log(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3485 }
3486
3487 #[test]
3488 fn test_unary_log2() {
3489 let x = array![0.0];
3490 assert_eq!(log2(&x).unwrap().item_exact::<f32>(), NEG_INF);
3491
3492 let x = array![1.0];
3493 assert_eq!(log2(&x).unwrap().item_exact::<f32>(), 0.0);
3494
3495 let x = array![1024.0];
3496 assert_eq!(log2(&x).unwrap().item_exact::<f32>(), 10.0);
3497 }
3498
3499 #[test]
3500 fn test_unary_log10() {
3501 let x = array![0.0];
3502 assert_eq!(log10(&x).unwrap().item_exact::<f32>(), NEG_INF);
3503
3504 let x = array![1.0];
3505 assert_eq!(log10(&x).unwrap().item_exact::<f32>(), 0.0);
3506
3507 let x = array![1000.0];
3508 assert_eq!(log10(&x).unwrap().item_exact::<f32>(), 3.0);
3509 }
3510
3511 #[test]
3512 fn test_unary_log1p() {
3513 let x = array![-1.0];
3514 assert_float_eq! {
3515 log1p(&x).unwrap().item_exact::<f32>(),
3516 (-1.0f32).ln_1p(),
3517 abs <= 1e-5
3518 };
3519
3520 let x = array![1.0];
3521 assert_float_eq! {
3522 log1p(&x).unwrap().item_exact::<f32>(),
3523 1.0f32.ln_1p(),
3524 abs <= 1e-5
3525 };
3526
3527 let x = array![1];
3529 assert_eq!(log1p(&x).unwrap().dtype(), Dtype::Float32);
3530 assert_float_eq! {
3531 log1p(&x).unwrap().item_exact::<f32>(),
3532 1.0f32.ln_1p(),
3533 abs <= 1e-5
3534 };
3535
3536 let x = broadcast_to(&array!(1.0), &[2, 2, 2]).unwrap();
3538 let res = log1p(&x).unwrap();
3539 let expected = Array::full::<f32>(&[2, 2, 2], array!(1.0f32.ln_1p())).unwrap();
3540 assert!(all_close(&res, &expected, None, None, None).unwrap());
3541
3542 let data = Array::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
3543 let x = split_equal(&data, 2, 1).unwrap();
3544 let expected = Array::from_slice(&[1.0f32.ln_1p(), 3.0f32.ln_1p()], &[2, 1]);
3545 assert!(all_close(log1p(&x[0]).unwrap(), &expected, None, None, None).unwrap());
3546 }
3547
3548 #[test]
3549 fn test_unary_sigmoid() {
3550 let x = array![0.0];
3551 assert_float_eq! {
3552 sigmoid(&x).unwrap().item_exact::<f32>(),
3553 0.5,
3554 abs <= 1e-5
3555 };
3556
3557 let x = array![0];
3559 assert_eq!(sigmoid(&x).unwrap().dtype(), Dtype::Float32);
3560 assert_float_eq! {
3561 sigmoid(&x).unwrap().item_exact::<f32>(),
3562 0.5,
3563 abs <= 1e-5
3564 };
3565
3566 let inf = f32::INFINITY;
3567 let x = array![inf];
3568 assert_eq!(sigmoid(&x).unwrap().item_exact::<f32>(), 1.0);
3569
3570 let x = array![-inf];
3571 assert_eq!(sigmoid(&x).unwrap().item_exact::<f32>(), 0.0);
3572 }
3573
3574 #[test]
3575 fn test_unary_square() {
3576 let x = array![3.0];
3577 assert_eq!(square(&x).unwrap().item_exact::<f32>(), 9.0);
3578
3579 let x = array![2];
3580 assert_eq!(square(&x).unwrap().item_exact::<i32>(), 4);
3581
3582 let x = Array::full::<f32>(&[3, 3], array!(2.0)).unwrap();
3583 assert!(all_close(
3584 square(&x).unwrap(),
3585 Array::full::<f32>(&[3, 3], array!(4.0)).unwrap(),
3586 None,
3587 None,
3588 None
3589 )
3590 .unwrap());
3591 }
3592
3593 #[test]
3594 fn test_unary_sqrt_rsqrt() {
3595 let x = array![4.0];
3596 assert_eq!(sqrt(&x).unwrap().item_exact::<f32>(), 2.0);
3597 assert_eq!(rsqrt(&x).unwrap().item_exact::<f32>(), 0.5);
3598
3599 let x = Array::full::<f32>(&[3, 3], array!(9.0)).unwrap();
3600 assert!(all_close(
3601 sqrt(&x).unwrap(),
3602 Array::full::<f32>(&[3, 3], array!(3.0)).unwrap(),
3603 None,
3604 None,
3605 None
3606 )
3607 .unwrap());
3608
3609 let x = array![4i32];
3610 assert_eq!(sqrt(&x).unwrap().item_exact::<f32>(), 2.0);
3611 assert_eq!(rsqrt(&x).unwrap().item_exact::<f32>(), 0.5);
3612 }
3613
3614 #[test]
3615 fn test_unary_reciprocal() {
3616 let x = array![8.0];
3617 assert_eq!(reciprocal(&x).unwrap().item_exact::<f32>(), 0.125);
3618
3619 let x = array![2];
3620 let out = reciprocal(&x).unwrap();
3621 assert_eq!(out.dtype(), Dtype::Float32);
3622 assert_eq!(out.item_exact::<f32>(), 0.5);
3623
3624 let x = Array::full::<f32>(&[3, 3], array!(2.0)).unwrap();
3625 assert!(all_close(
3626 reciprocal(&x).unwrap(),
3627 Array::full::<f32>(&[3, 3], array!(0.5)).unwrap(),
3628 None,
3629 None,
3630 None
3631 )
3632 .unwrap());
3633 }
3634
3635 #[test]
3636 fn test_unary_real_imag() {
3637 let x = Array::from_complex(complex64::new(0.0, 1.0));
3638 assert_array_eq(
3639 real(&x).unwrap(),
3640 Array::from_f32(0.0),
3641 tolerances::EXACT.rtol,
3642 tolerances::EXACT.atol,
3643 );
3644 assert_array_eq(
3645 imag(&x).unwrap(),
3646 Array::from_f32(1.0),
3647 tolerances::EXACT.rtol,
3648 tolerances::EXACT.atol,
3649 );
3650 }
3651
3652 #[test]
3653 fn test_binary_add() {
3654 let x = array![1.0];
3655 let y = array![1.0];
3656 let z = add(&x, &y).unwrap();
3657 assert_eq!(z.item_exact::<f32>(), 2.0);
3658
3659 let z = &x + y;
3660 assert_eq!(z.item_exact::<f32>(), 2.0);
3661
3662 let z = add(z, &x).unwrap();
3663 assert_eq!(z.item_exact::<f32>(), 3.0);
3664
3665 let mut out = x.deep_clone();
3667 for _ in 0..10 {
3668 out = add(&out, &x).unwrap();
3669 }
3670 assert_eq!(out.item_exact::<f32>(), 11.0);
3671
3672 let x = array!([1.0, 2.0, 3.0]);
3674 let y = array!([1.0, 2.0, 3.0]);
3675 let z = add(&x, &y).unwrap();
3676 assert_eq!(z.shape(), &[3]);
3677 assert_array_eq(
3678 z,
3679 array!([2.0, 4.0, 6.0]),
3680 tolerances::EXACT.rtol,
3681 tolerances::EXACT.atol,
3682 );
3683
3684 let x = array!([1.0, 2.0, 3.0]);
3686 let y = &x + 2.0;
3687 assert_eq!(y.dtype(), Dtype::Float32);
3688 assert_array_eq(
3689 y,
3690 array!([3.0, 4.0, 5.0]),
3691 tolerances::EXACT.rtol,
3692 tolerances::EXACT.atol,
3693 );
3694 let y = &x + 2.0;
3695 assert_eq!(y.dtype(), Dtype::Float32);
3696 assert_array_eq(
3697 y,
3698 array!([3.0, 4.0, 5.0]),
3699 tolerances::EXACT.rtol,
3700 tolerances::EXACT.atol,
3701 );
3702
3703 let y = x + 2;
3705 assert_eq!(y.dtype(), Dtype::Float32);
3706
3707 let y = array!([1, 2, 3]) + 2.0;
3708 assert_eq!(y.dtype(), Dtype::Float32);
3709 assert_array_eq(
3711 y,
3712 array!([3.0, 4.0, 5.0]),
3713 tolerances::EXACT.rtol,
3714 tolerances::EXACT.atol,
3715 );
3716
3717 let x = broadcast_to(&array!(1.0), &[10]).unwrap();
3719 let y = broadcast_to(&array!(2.0), &[10]).unwrap();
3720 let z = add(&x, &y).unwrap();
3721 assert_array_eq(
3722 z,
3723 full::<f32>(&[10], array!(3.0)).unwrap(),
3724 tolerances::EXACT.rtol,
3725 tolerances::EXACT.atol,
3726 );
3727
3728 let x = Array::from_slice(&[1.0, 2.0], &[1, 2]);
3729 let y = Array::from_slice(&[1.0, 2.0], &[2, 1]);
3730 let z = add(&x, &y).unwrap();
3731 assert_eq!(z.shape(), &[2, 2]);
3732 assert_array_eq(
3733 z,
3734 Array::from_slice(&[2.0, 3.0, 3.0, 4.0], &[2, 2]),
3735 tolerances::EXACT.rtol,
3736 tolerances::EXACT.atol,
3737 );
3738
3739 let x = ones::<f32>(&[3, 2, 1]).unwrap();
3740 let z = x + 2.0;
3741 assert_eq!(z.shape(), &[3, 2, 1]);
3742 let expected = Array::from_slice(&[3.0, 3.0, 3.0, 3.0, 3.0, 3.0], &[3, 2, 1]);
3743 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
3744
3745 let x = array!();
3747 let y = array!();
3748 let z = x + y;
3749 z.eval().unwrap();
3750 assert_eq!(z.size(), 0);
3751 assert_eq!(z.shape(), &[0]);
3752 }
3753
3754 #[test]
3755 fn test_binary_sub() {
3756 let x = array!([3.0, 2.0, 1.0]);
3757 let y = array!([1.0, 1.0, 1.0]);
3758 assert_array_eq(
3759 x - y,
3760 array!([2.0, 1.0, 0.0]),
3761 tolerances::EXACT.rtol,
3762 tolerances::EXACT.atol,
3763 );
3764 }
3765
3766 #[test]
3767 fn test_binary_mul() {
3768 let x = array!([1.0, 2.0, 3.0]);
3769 let y = array!([2.0, 2.0, 2.0]);
3770 assert_array_eq(
3771 x * y,
3772 array!([2.0, 4.0, 6.0]),
3773 tolerances::EXACT.rtol,
3774 tolerances::EXACT.atol,
3775 );
3776 }
3777
3778 #[test]
3779 fn test_binary_div() {
3780 let x = array![1.0];
3781 let y = array![1.0];
3782 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3783
3784 let x = array![1.0];
3785 let y = array![0.5];
3786 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 2.0);
3787
3788 let x = array![1.0];
3789 let y = array![4.0];
3790 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 0.25);
3791
3792 let x = array![true];
3793 let y = array![true];
3794 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3795
3796 let x = array![false];
3797 let y = array![true];
3798 assert_eq!(divide(&x, &y).unwrap().item_exact::<f32>(), 0.0);
3799
3800 let x = array![true];
3801 let y = array![false];
3802 assert!(divide(&x, &y).unwrap().item_exact::<f32>().is_infinite());
3803
3804 let x = array![false];
3805 let y = array![false];
3806 assert!(divide(&x, &y).unwrap().item_exact::<f32>().is_nan());
3807 }
3808
3809 #[test]
3810 fn test_binary_maximum_minimum() {
3811 let x = array![1.0];
3812 let y = array![0.0];
3813 assert_eq!(maximum(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3814 assert_eq!(minimum(&x, &y).unwrap().item_exact::<f32>(), 0.0);
3815
3816 let y = array![2.0];
3817 assert_eq!(maximum(&x, &y).unwrap().item_exact::<f32>(), 2.0);
3818 assert_eq!(minimum(&x, &y).unwrap().item_exact::<f32>(), 1.0);
3819 }
3820
3821 #[test]
3822 fn test_binary_logaddexp() {
3823 let x = array![0.0];
3824 let y = array![0.0];
3825 assert_float_eq! {
3826 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3827 2.0f32.ln(),
3828 abs <= 1e-5
3829 };
3830
3831 let x = array!([0u32]);
3832 let y = array!([10000u32]);
3833 assert_eq!(logaddexp(&x, &y).unwrap().item_exact::<f32>(), 10000.0);
3834
3835 let x = array![f32::INFINITY];
3836 let y = array![3.0];
3837 assert_eq!(
3838 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3839 f32::INFINITY
3840 );
3841
3842 let x = array![f32::NEG_INFINITY];
3843 let y = array![3.0];
3844 assert_eq!(logaddexp(&x, &y).unwrap().item_exact::<f32>(), 3.0);
3845
3846 let x = array![f32::NEG_INFINITY];
3847 let y = array![f32::NEG_INFINITY];
3848 assert_eq!(
3849 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3850 f32::NEG_INFINITY
3851 );
3852
3853 let x = array![f32::INFINITY];
3854 let y = array![f32::INFINITY];
3855 assert_eq!(
3856 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3857 f32::INFINITY
3858 );
3859
3860 let x = array![f32::NEG_INFINITY];
3861 let y = array![f32::INFINITY];
3862 assert_eq!(
3863 logaddexp(&x, &y).unwrap().item_exact::<f32>(),
3864 f32::INFINITY
3865 );
3866 }
3867
3868 #[test]
3869 fn test_basic_clip() {
3870 let a = array!([1.0, 4.0, 3.0, 8.0, 5.0]);
3871 let expected = array!([2.0, 4.0, 3.0, 6.0, 5.0]);
3872 let clipped = clip(&a, (array!(2.0), array!(6.0))).unwrap();
3873 assert_array_eq(
3874 clipped,
3875 &expected,
3876 tolerances::EXACT.rtol,
3877 tolerances::EXACT.atol,
3878 );
3879
3880 let clipped = clip(&a, (2.0, 6.0)).unwrap();
3882 assert_array_eq(
3883 clipped,
3884 &expected,
3885 tolerances::EXACT.rtol,
3886 tolerances::EXACT.atol,
3887 );
3888 }
3889
3890 #[test]
3891 fn test_clip_with_only_min() {
3892 let a = array!([-1.0, 1.0, 0.0, 5.0]);
3893 let expected = array!([0.0, 1.0, 0.0, 5.0]);
3894 let clipped = clip(&a, (array!(0.0), ())).unwrap();
3895 assert_array_eq(
3896 clipped,
3897 &expected,
3898 tolerances::EXACT.rtol,
3899 tolerances::EXACT.atol,
3900 );
3901
3902 let clipped = clip(&a, (0.0, ())).unwrap();
3904 assert_array_eq(
3905 clipped,
3906 expected,
3907 tolerances::EXACT.rtol,
3908 tolerances::EXACT.atol,
3909 );
3910 }
3911
3912 #[test]
3913 fn test_clip_with_only_max() {
3914 let a = array!([2.0, 3.0, 4.0, 5.0]);
3915 let expected = array!([2.0, 3.0, 4.0, 4.0]);
3916 let clipped = clip(&a, ((), array!(4.0))).unwrap();
3917 assert_array_eq(
3918 clipped,
3919 &expected,
3920 tolerances::EXACT.rtol,
3921 tolerances::EXACT.atol,
3922 );
3923
3924 let clipped = clip(&a, ((), 4.0)).unwrap();
3926 assert_array_eq(
3927 clipped,
3928 expected,
3929 tolerances::EXACT.rtol,
3930 tolerances::EXACT.atol,
3931 );
3932 }
3933
3934 #[test]
3935 fn test_tensordot() {
3936 let x = reshape(arange::<_, f32>(None, 60.0, None).unwrap(), &[3, 4, 5]).unwrap();
3937 let y = reshape(arange::<_, f32>(None, 24.0, None).unwrap(), &[4, 3, 2]).unwrap();
3938 let z = tensordot_axes(&x, &y, &[1i32, 0], &[0i32, 1]).unwrap();
3939 let expected = Array::from_slice(
3940 &[
3941 4400.0_f32, 4730.0, 4532.0, 4874.0, 4664.0, 5018.0, 4796.0, 5162.0, 4928.0, 5306.0,
3942 ],
3943 &[5, 2],
3944 );
3945 assert_array_eq_with_context(
3946 z,
3947 expected,
3948 tolerances::EXACT.rtol,
3949 tolerances::EXACT.atol,
3950 "float32 explicit-axis contraction",
3951 );
3952
3953 let x = reshape(arange::<_, f32>(None, 360.0, None).unwrap(), &[3, 4, 5, 6]).unwrap();
3954 let y = reshape(arange::<_, f32>(None, 360.0, None).unwrap(), &[6, 4, 5, 3]).unwrap();
3955 assert!(tensordot_axes(&x, &y, &[2, 1, 3], &[1, 2, 0]).is_err());
3956
3957 let x = reshape(arange::<_, f32>(None, 60.0, None).unwrap(), &[3, 4, 5]).unwrap();
3958 let y = reshape(arange::<_, f32>(None, 120.0, None).unwrap(), &[4, 5, 6]).unwrap();
3959
3960 let z = tensordot_axis(&x, &y, 2).unwrap();
3961 let expected = Array::from_slice(
3962 &[
3963 14820.0, 15010.0, 15200.0, 15390.0, 15580.0, 15770.0, 37620.0, 38210.0, 38800.0,
3964 39390.0, 39980.0, 40570.0, 60420.0, 61410.0, 62400.0, 63390.0, 64380.0, 65370.0,
3965 ],
3966 &[3, 6],
3967 );
3968 assert_array_eq_with_context(
3969 z,
3970 expected,
3971 tolerances::EXACT.rtol,
3972 tolerances::EXACT.atol,
3973 "float32 axis-count contraction",
3974 );
3975 }
3976
3977 #[test]
3978 fn test_outer() {
3979 let x = arange::<_, f32>(1.0, 5.0, None).unwrap();
3980 let y = arange::<_, f32>(1.0, 4.0, None).unwrap();
3981 let z = outer(&x, &y).unwrap();
3982 let expected = Array::from_slice(
3983 &[1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, 4.0, 8.0, 12.0],
3984 &[4, 3],
3985 );
3986 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
3987
3988 let x = ones::<f32>(&[5]).unwrap();
3989 let y = linspace::<_, f32>(
3990 -2.0,
3991 2.0,
3992 crate::ops::LinspaceOptions {
3993 count: 5,
3994 endpoint: true,
3995 },
3996 )
3997 .unwrap();
3998 let z = outer(&x, &y).unwrap();
3999 let expected = Array::from_slice(
4000 &[
4001 -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,
4002 -2.0, -1.0, 0.0, 1.0, 2.0, -2.0, -1.0, 0.0, 1.0, 2.0,
4003 ],
4004 &[5, 5],
4005 );
4006 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4007 }
4008
4009 #[test]
4010 fn test_inner() {
4011 let x = reshape(arange::<_, f32>(None, 5.0, None).unwrap(), &[1, 5]).unwrap();
4012 let y = reshape(arange::<_, f32>(None, 6.0, None).unwrap(), &[2, 3]).unwrap();
4013 assert!(inner(&x, &y).is_err());
4014
4015 let x = array!([1.0, 2.0, 3.0]);
4016 let y = array!([0.0, 1.0, 0.0]);
4017 let z = inner(&x, &y).unwrap();
4018 assert_eq!(z.item_exact::<f32>(), 2.0);
4019
4020 let x = reshape(arange::<_, f32>(None, 24.0, None).unwrap(), &[2, 3, 4]).unwrap();
4021 let y = arange::<_, f32>(None, 4.0, None).unwrap();
4022 let z = inner(&x, &y).unwrap();
4023 let expected = Array::from_slice(&[14.0, 38.0, 62.0, 86.0, 110.0, 134.0], &[2, 3]);
4024 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4025
4026 let x = reshape(arange::<_, f32>(None, 2.0, None).unwrap(), &[1, 1, 2]).unwrap();
4027 let y = reshape(arange::<_, f32>(None, 6.0, None).unwrap(), &[3, 2]).unwrap();
4028 let z = inner(&x, &y).unwrap();
4029 let expected = Array::from_slice(&[1.0, 3.0, 5.0], &[1, 1, 3]);
4030 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4031
4032 let x = eye::<f32>(2, None, None).unwrap();
4033 let y = Array::from_f32(7.0);
4034 let z = inner(&x, &y).unwrap();
4035 let expected = Array::from_slice(&[7.0, 0.0, 0.0, 7.0], &[2, 2]);
4036 assert_array_eq(z, expected, tolerances::EXACT.rtol, tolerances::EXACT.atol);
4037 }
4038
4039 #[test]
4040 fn test_divmod() {
4041 let x = array!([1.0, 2.0, 3.0]);
4042 let y = array!([1.0, 1.0, 1.0]);
4043 let out = divmod(&x, &y).unwrap();
4044 assert_array_eq(
4045 out.0,
4046 array!([1.0, 2.0, 3.0]),
4047 tolerances::EXACT.rtol,
4048 tolerances::EXACT.atol,
4049 );
4050 assert_array_eq(
4051 out.1,
4052 array!([0.0, 0.0, 0.0]),
4053 tolerances::EXACT.rtol,
4054 tolerances::EXACT.atol,
4055 );
4056
4057 let x = array!([5.0, 6.0, 7.0]);
4058 let y = array!([2.0, 2.0, 2.0]);
4059 let out = divmod(&x, &y).unwrap();
4060 assert_array_eq(
4061 out.0,
4062 array!([2.0, 3.0, 3.0]),
4063 tolerances::EXACT.rtol,
4064 tolerances::EXACT.atol,
4065 );
4066 assert_array_eq(
4067 out.1,
4068 array!([1.0, 0.0, 1.0]),
4069 tolerances::EXACT.rtol,
4070 tolerances::EXACT.atol,
4071 );
4072
4073 let x = array!([5.0, 6.0, 7.0]);
4074 let y = array!([2.0, 2.0, 2.0]);
4075 let out = divmod(&x, &y).unwrap();
4076 assert_array_eq(
4077 out.0,
4078 array!([2.0, 3.0, 3.0]),
4079 tolerances::EXACT.rtol,
4080 tolerances::EXACT.atol,
4081 );
4082 assert_array_eq(
4083 out.1,
4084 array!([1.0, 0.0, 1.0]),
4085 tolerances::EXACT.rtol,
4086 tolerances::EXACT.atol,
4087 );
4088
4089 let x = array![complex64::new(1.0, 0.0)];
4090 let y = array![complex64::new(2.0, 0.0)];
4091 assert!(divmod(&x, &y).is_err());
4092
4093 let x = array![1.0];
4095 let y = array![2.0];
4096 let (quo, rem) = divmod(&x, &y).unwrap();
4097 eval([&quo, &rem]).unwrap();
4098 assert_eq!(quo.item_exact::<f32>(), 0.0);
4099 assert_eq!(rem.item_exact::<f32>(), 1.0);
4100
4101 let x = array![1.0];
4103 let y = array![2.0];
4104 let (quo, rem) = divmod(&x, &y).unwrap();
4105 let z = quo + rem;
4106 assert_eq!(z.item_exact::<f32>(), 1.0);
4107
4108 let mut out_holder = {
4110 let (quo, _) = divmod(&x, &y).unwrap();
4111 vec![quo]
4112 };
4113 eval(out_holder.iter()).unwrap();
4114 assert_eq!(out_holder[0].item_exact::<f32>(), 0.0);
4115
4116 out_holder.clear();
4118 let out_holder = {
4119 let (_, rem) = divmod(&x, &y).unwrap();
4120 vec![rem]
4121 };
4122 eval(out_holder.iter()).unwrap();
4123 assert_eq!(out_holder[0].item_exact::<f32>(), 1.0);
4124 }
4125
4126 #[test]
4128 fn test_segmented_mm() {
4129 use crate::ops::{indexing::*, stack};
4130 use crate::random;
4131
4132 fn segmented_mm_ref(a: &Array, b: &Array, segments: &Array) -> Array {
4134 let segments_data: Vec<Vec<u32>> = (0..segments.shape()[0])
4135 .map(|i| {
4136 let row = segments.index(i);
4137 vec![
4138 row.index(0).item_exact::<u32>(),
4139 row.index(1).item_exact::<u32>(),
4140 ]
4141 })
4142 .collect();
4143
4144 let results: Vec<Array> = segments_data
4145 .iter()
4146 .map(|seg| {
4147 let s1 = seg[0] as i32;
4148 let s2 = seg[1] as i32;
4149 let a_slice = a.index((.., s1..s2));
4150 let b_slice = b.index((s1..s2, ..));
4151 a_slice.matmul(&b_slice).unwrap()
4152 })
4153 .collect();
4154
4155 stack(&results, 0).unwrap()
4156 }
4157
4158 let shapes = [(10, 10, 10), (10, 10, 100), (100, 100, 100)];
4160
4161 let all_segments: Vec<Vec<f32>> = vec![
4163 vec![0.0, 0.0, 1.0],
4164 vec![0.0, 0.5, 1.0],
4165 (0..10).map(|r| r as f32 / 9.0).collect(),
4166 ];
4167
4168 random::seed(42).unwrap();
4169
4170 for (m, n, k) in shapes {
4171 for s in &all_segments {
4172 let mut segments_vec: Vec<[u32; 2]> = Vec::new();
4174 for i in 0..s.len() - 1 {
4175 let s1 = ((k as f32 * s[i]) as u32).min(k as u32 - 1);
4176 let s2 = ((k as f32 * s[i + 1]) as u32).min(k as u32 - 1);
4177 segments_vec.push([s1, s2]);
4178 }
4179 let segments_flat: Vec<u32> = segments_vec.iter().flat_map(|x| *x).collect();
4180 let segments = Array::from_slice(&segments_flat, &[segments_vec.len() as i32, 2]);
4181
4182 let a = random::normal::<f32>(&[m, k], None, None, None).unwrap();
4184 let b = random::normal::<f32>(&[k, n], None, None, None).unwrap();
4185 let c1 = segmented_mm_ref(&a, &b, &segments);
4186 let c2 = segmented_mm(&a, &b, &segments).unwrap();
4187 assert!(
4188 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4189 "segmented_mm failed for shape ({}, {}, {}) with segments {:?}",
4190 m,
4191 n,
4192 k,
4193 s
4194 );
4195
4196 let a = random::normal::<f32>(&[k, m], None, None, None).unwrap();
4198 let b = random::normal::<f32>(&[k, n], None, None, None).unwrap();
4199 let a_t = a.t();
4200 let c1 = segmented_mm_ref(&a_t, &b, &segments);
4201 let c2 = segmented_mm(&a_t, &b, &segments).unwrap();
4202 assert!(
4203 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4204 "segmented_mm with transposed a failed for shape ({}, {}, {})",
4205 m,
4206 n,
4207 k
4208 );
4209
4210 let a = random::normal::<f32>(&[m, k], None, None, None).unwrap();
4212 let b = random::normal::<f32>(&[n, k], None, None, None).unwrap();
4213 let b_t = b.t();
4214 let c1 = segmented_mm_ref(&a, &b_t, &segments);
4215 let c2 = segmented_mm(&a, &b_t, &segments).unwrap();
4216 assert!(
4217 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4218 "segmented_mm with transposed b failed for shape ({}, {}, {})",
4219 m,
4220 n,
4221 k
4222 );
4223
4224 let a = random::normal::<f32>(&[k, m], None, None, None).unwrap();
4226 let b = random::normal::<f32>(&[n, k], None, None, None).unwrap();
4227 let a_t = a.t();
4228 let b_t = b.t();
4229 let c1 = segmented_mm_ref(&a_t, &b_t, &segments);
4230 let c2 = segmented_mm(&a_t, &b_t, &segments).unwrap();
4231 assert!(
4232 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4233 "segmented_mm with both transposed failed for shape ({}, {}, {})",
4234 m,
4235 n,
4236 k
4237 );
4238 }
4239 }
4240 }
4241
4242 #[test]
4243 fn test_segmented_mm_batched_error() {
4244 let a = ones::<f32>(&[2, 10, 10]).unwrap();
4246 let segments = Array::from_slice(&[0u32, 5, 5, 10], &[2, 2]);
4247 let result = segmented_mm(&a, &a, &segments);
4248 assert!(
4249 result.is_err(),
4250 "segmented_mm should fail for batched input"
4251 );
4252 }
4253
4254 #[test]
4256 fn test_gather_mm() {
4257 use crate::ops::indexing::take_axis;
4258 use crate::random;
4259
4260 random::seed(0).unwrap();
4261
4262 fn gather_mm_ref(
4264 a: &Array,
4265 b: &Array,
4266 lhs_indices: Option<&Array>,
4267 rhs_indices: Option<&Array>,
4268 ) -> Array {
4269 let a = a
4270 .reshape(&[-1, a.shape()[a.ndim() - 2], a.shape()[a.ndim() - 1]])
4271 .unwrap();
4272 let b = b
4273 .reshape(&[-1, b.shape()[b.ndim() - 2], b.shape()[b.ndim() - 1]])
4274 .unwrap();
4275
4276 let a_gathered = match lhs_indices {
4277 Some(idx) => take_axis(&a, idx, 0).unwrap(),
4278 None => a,
4279 };
4280 let b_gathered = match rhs_indices {
4281 Some(idx) => take_axis(&b, idx, 0).unwrap(),
4282 None => b,
4283 };
4284 a_gathered.matmul(&b_gathered).unwrap()
4285 }
4286
4287 let a = random::normal::<f32>(&[1, 32, 32], None, None, None).unwrap();
4289 let b = random::normal::<f32>(&[3, 32, 32], None, None, None).unwrap();
4290 let lhs_indices = Array::from_slice(&[0u32], &[1]);
4291 let rhs_indices = Array::from_slice(&[2u32, 1], &[2]);
4292
4293 let out_ref = gather_mm_ref(&a, &b, Some(&lhs_indices), Some(&rhs_indices));
4294 let out_test = gather_mm(&a, &b, &lhs_indices, &rhs_indices, None).unwrap();
4295 assert!(
4296 out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
4297 "gather_mm test case 1 failed"
4298 );
4299
4300 let out_ref = gather_mm_ref(&a, &b, None, Some(&rhs_indices));
4302 let out_test = gather_mm(&a, &b, None::<&Array>, &rhs_indices, None).unwrap();
4303 assert!(
4304 out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
4305 "gather_mm test case 2 failed"
4306 );
4307
4308 let a = random::normal::<f32>(&[5, 32, 32], None, None, None).unwrap();
4310 let lhs_indices = Array::from_slice(&[0u32, 2], &[2]);
4311
4312 let out_ref = gather_mm_ref(&a, &b, Some(&lhs_indices), Some(&rhs_indices));
4313 let out_test = gather_mm(&a, &b, &lhs_indices, &rhs_indices, None).unwrap();
4314 assert!(
4315 out_ref.all_close(&out_test, 1e-5, 1e-5, None).unwrap(),
4316 "gather_mm test case 3 failed"
4317 );
4318 }
4319
4320 #[test]
4322 fn test_gather_mm_sorted() {
4323 use crate::ops::indexing::take_axis;
4324 use crate::ops::sort;
4325 use crate::random;
4326
4327 random::seed(0).unwrap();
4328
4329 fn gather_mm_ref(a: &Array, b: &Array, rhs: &Array) -> Array {
4331 let b_gathered = take_axis(b, rhs, 0).unwrap();
4332 a.matmul(&b_gathered).unwrap()
4333 }
4334
4335 let a = random::normal::<f32>(&[100, 1, 100], None, None, None).unwrap();
4336 let b = random::normal::<f32>(&[8, 100, 100], None, None, None).unwrap();
4337 let rhs = sort(random::randint::<_, i32>(0, 8, &[100], None).unwrap()).unwrap();
4338
4339 let c1 = gather_mm_ref(&a, &b, &rhs);
4340 let c2 = gather_mm(&a, &b, None::<&Array>, &rhs, true).unwrap();
4341 assert!(
4342 c1.all_close(&c2, 1e-4, 1e-4, None).unwrap(),
4343 "gather_mm_sorted failed"
4344 );
4345 }
4346}