1use crate::{
2 dtype::Dtype,
3 error::{AsSliceError, ConversionError},
4 sealed::Sealed,
5 utils::{guard::Guarded, SUCCESS},
6 Stream,
7};
8use element::FromSliceElement;
9use mlx_internal_macros::default_device;
10use mlx_sys::mlx_array;
11use num_complex::Complex;
12use std::{
13 ffi::{c_void, CStr},
14 iter::Sum,
15};
16
17mod element;
18mod operators;
19
20cfg_safetensors! {
21 mod safetensors;
22}
23
24pub use element::ArrayElement;
25
26#[allow(non_camel_case_types)]
30pub type complex64 = Complex<f32>;
31
32#[repr(transparent)]
34pub struct Array {
35 c_array: mlx_array,
36}
37
38impl Sealed for Array {}
39
40impl Sealed for &Array {}
41
42impl std::fmt::Debug for Array {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(f, "{self}")
45 }
46}
47
48impl std::fmt::Display for Array {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 unsafe {
51 let mut mlx_str = mlx_sys::mlx_string_new();
52 let status = mlx_sys::mlx_array_tostring(&mut mlx_str as *mut _, self.as_ptr());
53 if status != SUCCESS {
54 return Err(std::fmt::Error);
55 }
56 let ptr = mlx_sys::mlx_string_data(mlx_str);
57 let c_str = CStr::from_ptr(ptr);
58 write!(f, "{}", c_str.to_str().map_err(|_| std::fmt::Error)?)?;
59 mlx_sys::mlx_string_free(mlx_str);
60 Ok(())
61 }
62 }
63}
64
65impl Drop for Array {
66 fn drop(&mut self) {
67 unsafe { mlx_sys::mlx_array_free(self.as_ptr()) };
71 }
72}
73
74unsafe impl Send for Array {}
76
77impl Array {
78 pub unsafe fn from_ptr(c_array: mlx_array) -> Array {
99 Self { c_array }
100 }
101
102 pub fn as_ptr(&self) -> mlx_array {
104 self.c_array
105 }
106
107 pub fn from_bool(val: bool) -> Array {
109 let c_array = unsafe { mlx_sys::mlx_array_new_bool(val) };
110 Array { c_array }
111 }
112
113 pub fn from_int(val: i32) -> Array {
115 let c_array = unsafe { mlx_sys::mlx_array_new_int(val) };
116 Array { c_array }
117 }
118
119 pub fn from_f32(val: f32) -> Array {
121 let c_array = unsafe { mlx_sys::mlx_array_new_float32(val) };
122 Array { c_array }
123 }
124
125 pub fn from_f64(val: f64) -> Array {
127 let c_array = unsafe { mlx_sys::mlx_array_new_float64(val) };
128 Array { c_array }
129 }
130
131 pub fn from_complex(val: complex64) -> Array {
133 let c_array = unsafe { mlx_sys::mlx_array_new_complex(val.re, val.im) };
134 Array { c_array }
135 }
136
137 pub fn from_slice<T: FromSliceElement>(data: &[T], shape: &[i32]) -> Self {
153 assert_eq!(data.len(), shape.iter().product::<i32>() as usize);
155
156 unsafe { Self::from_raw_data(data.as_ptr() as *const c_void, shape, T::DTYPE) }
157 }
158
159 pub fn from_slice_f64(data: &[f64], shape: &[i32]) -> Self {
164 assert_eq!(data.len(), shape.iter().product::<i32>() as usize);
166
167 unsafe { Self::from_raw_data(data.as_ptr() as *const c_void, shape, Dtype::Float64) }
168 }
169
170 #[inline]
179 pub unsafe fn from_raw_data(data: *const c_void, shape: &[i32], dtype: Dtype) -> Self {
180 let dim = if shape.len() > i32::MAX as usize {
181 panic!("Shape is too large")
182 } else {
183 shape.len() as i32
184 };
185
186 let c_array = mlx_sys::mlx_array_new_data(data, shape.as_ptr(), dim, dtype.into());
187 Array { c_array }
188 }
189
190 pub fn from_iter<I: IntoIterator<Item = T>, T: FromSliceElement>(
212 iter: I,
213 shape: &[i32],
214 ) -> Self {
215 let data: Vec<T> = iter.into_iter().collect();
216 Self::from_slice(&data, shape)
217 }
218
219 pub fn from_iter_f64<I: IntoIterator<Item = f64>>(iter: I, shape: &[i32]) -> Self {
224 let data: Vec<f64> = iter.into_iter().collect();
225 Self::from_slice_f64(&data, shape)
226 }
227
228 pub fn item_size(&self) -> usize {
230 unsafe { mlx_sys::mlx_array_itemsize(self.as_ptr()) }
231 }
232
233 pub fn size(&self) -> usize {
235 unsafe { mlx_sys::mlx_array_size(self.as_ptr()) }
236 }
237
238 pub fn strides(&self) -> &[usize] {
240 let ndim = self.ndim();
241 if ndim == 0 {
242 return &[];
244 }
245
246 unsafe {
247 let data = mlx_sys::mlx_array_strides(self.as_ptr());
248 std::slice::from_raw_parts(data, ndim)
249 }
250 }
251
252 pub fn nbytes(&self) -> usize {
254 unsafe { mlx_sys::mlx_array_nbytes(self.as_ptr()) }
255 }
256
257 pub fn ndim(&self) -> usize {
259 unsafe { mlx_sys::mlx_array_ndim(self.as_ptr()) }
260 }
261
262 pub fn shape(&self) -> &[i32] {
266 let ndim = self.ndim();
267 if ndim == 0 {
268 return &[];
270 }
271
272 unsafe {
273 let data = mlx_sys::mlx_array_shape(self.as_ptr());
274 std::slice::from_raw_parts(data, ndim)
275 }
276 }
277
278 pub fn dim(&self, dim: i32) -> i32 {
286 let dim = if dim.is_negative() {
287 (self.ndim() as i32).checked_add(dim).unwrap()
288 } else {
289 dim
290 };
291
292 unsafe { mlx_sys::mlx_array_dim(self.as_ptr(), dim) }
294 }
295
296 pub fn dtype(&self) -> Dtype {
298 let dtype = unsafe { mlx_sys::mlx_array_dtype(self.as_ptr()) };
299 Dtype::try_from(dtype).unwrap()
300 }
301
302 pub fn eval(&self) -> crate::error::Result<()> {
304 <() as Guarded>::try_from_op(|_| unsafe { mlx_sys::mlx_array_eval(self.as_ptr()) })
305 }
306
307 pub fn item_exact<T: ArrayElement>(&self) -> T {
312 self.try_item_exact().unwrap()
313 }
314
315 pub fn try_item_exact<T: ArrayElement>(&self) -> Result<T, ConversionError> {
320 if self.size() != 1 {
321 return Err(ConversionError::NotScalar {
322 actual: self.size(),
323 });
324 }
325 if self.dtype() != T::DTYPE {
326 return Err(ConversionError::DtypeMismatch {
327 expected: T::DTYPE,
328 actual: self.dtype(),
329 });
330 }
331 self.eval()?;
332 Ok(T::array_item(self)?)
333 }
334
335 pub fn item_cast<T: ArrayElement>(&self) -> T {
340 self.try_item_cast().unwrap()
341 }
342
343 pub fn try_item_cast<T: ArrayElement>(&self) -> Result<T, ConversionError> {
348 if self.size() != 1 {
349 return Err(ConversionError::NotScalar {
350 actual: self.size(),
351 });
352 }
353 self.eval()?;
354
355 if self.dtype() != T::DTYPE {
356 return self.as_type::<T>()?.try_item_exact();
357 }
358
359 Ok(T::array_item(self)?)
360 }
361
362 #[deprecated(since = "0.26.0", note = "use `item_cast` or `item_exact`")]
364 pub fn item<T: ArrayElement>(&self) -> T {
365 self.item_cast()
366 }
367
368 #[deprecated(since = "0.26.0", note = "use `try_item_cast` or `try_item_exact`")]
370 pub fn try_item<T: ArrayElement>(&self) -> crate::error::Result<T> {
371 self.try_item_cast().map_err(|error| match error {
372 ConversionError::Exception(error) => error,
373 error => crate::error::Exception::custom(error.to_string()),
374 })
375 }
376
377 pub fn to_vec_exact<T: ArrayElement + Clone>(&self) -> Result<Vec<T>, ConversionError> {
383 if self.dtype() != T::DTYPE {
384 return Err(ConversionError::DtypeMismatch {
385 expected: T::DTYPE,
386 actual: self.dtype(),
387 });
388 }
389 Ok(self.try_as_slice::<T>()?.to_vec())
390 }
391
392 pub fn to_vec_cast<T: ArrayElement + Clone>(&self) -> Result<Vec<T>, ConversionError> {
398 if self.dtype() == T::DTYPE {
399 return self.to_vec_exact();
400 }
401 let converted = self.as_type::<T>()?;
402 Ok(converted.try_as_slice::<T>()?.to_vec())
403 }
404
405 pub unsafe fn as_slice_unchecked<T: ArrayElement>(&self) -> &[T] {
428 self.eval().unwrap();
429
430 unsafe {
431 let data = T::array_data(self);
432 let size = self.size();
433 std::slice::from_raw_parts(data, size)
434 }
435 }
436
437 pub fn try_as_slice<T: ArrayElement>(&self) -> Result<&[T], AsSliceError> {
455 if self.dtype() != T::DTYPE {
456 return Err(AsSliceError::DtypeMismatch {
457 expecting: T::DTYPE,
458 found: self.dtype(),
459 });
460 }
461
462 self.eval()?;
463
464 let row_contiguous = bool::try_from_op(|res| unsafe {
465 mlx_sys::_mlx_array_is_row_contiguous(res, self.as_ptr())
466 })?;
467 if !row_contiguous {
468 return Err(AsSliceError::NotContiguous);
469 }
470
471 unsafe {
472 let size = self.size();
473 let data = T::array_data(self);
474 if data.is_null() || size == 0 {
475 return Err(AsSliceError::Null);
476 }
477
478 Ok(std::slice::from_raw_parts(data, size))
479 }
480 }
481
482 pub fn as_slice<T: ArrayElement>(&self) -> &[T] {
501 self.try_as_slice().unwrap()
502 }
503
504 pub fn deep_clone(&self) -> Self {
508 unsafe {
509 let dtype = self.dtype();
510 let shape = self.shape();
511 let data = match dtype {
512 Dtype::Bool => mlx_sys::mlx_array_data_bool(self.as_ptr()) as *const c_void,
513 Dtype::Uint8 => mlx_sys::mlx_array_data_uint8(self.as_ptr()) as *const c_void,
514 Dtype::Uint16 => mlx_sys::mlx_array_data_uint16(self.as_ptr()) as *const c_void,
515 Dtype::Uint32 => mlx_sys::mlx_array_data_uint32(self.as_ptr()) as *const c_void,
516 Dtype::Uint64 => mlx_sys::mlx_array_data_uint64(self.as_ptr()) as *const c_void,
517 Dtype::Int8 => mlx_sys::mlx_array_data_int8(self.as_ptr()) as *const c_void,
518 Dtype::Int16 => mlx_sys::mlx_array_data_int16(self.as_ptr()) as *const c_void,
519 Dtype::Int32 => mlx_sys::mlx_array_data_int32(self.as_ptr()) as *const c_void,
520 Dtype::Int64 => mlx_sys::mlx_array_data_int64(self.as_ptr()) as *const c_void,
521 Dtype::Float16 => mlx_sys::mlx_array_data_float16(self.as_ptr()) as *const c_void,
522 Dtype::Float32 => mlx_sys::mlx_array_data_float32(self.as_ptr()) as *const c_void,
523 Dtype::Float64 => mlx_sys::mlx_array_data_float64(self.as_ptr()) as *const c_void,
524 Dtype::Bfloat16 => mlx_sys::mlx_array_data_bfloat16(self.as_ptr()) as *const c_void,
525 Dtype::Complex64 => {
526 mlx_sys::mlx_array_data_complex64(self.as_ptr()) as *const c_void
527 }
528 };
529
530 let new_c_array =
531 mlx_sys::mlx_array_new_data(data, shape.as_ptr(), shape.len() as i32, dtype.into());
532
533 Array::from_ptr(new_c_array)
534 }
535 }
536}
537
538impl Clone for Array {
539 fn clone(&self) -> Self {
540 Array::try_from_op(|res| unsafe { mlx_sys::mlx_array_set(res, self.as_ptr()) })
541 .expect("Failed to clone array")
543 }
544}
545
546impl Sum for Array {
547 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
548 iter.fold(Array::from_int(0), |acc, x| acc.add(&x).unwrap())
549 }
550}
551
552#[default_device]
557pub fn stop_gradient_device(
558 a: impl AsRef<Array>,
559 stream: impl AsRef<Stream>,
560) -> crate::error::Result<Array> {
561 Array::try_from_op(|res| unsafe {
562 mlx_sys::mlx_stop_gradient(res, a.as_ref().as_ptr(), stream.as_ref().as_ptr())
563 })
564}
565
566impl From<bool> for Array {
567 fn from(value: bool) -> Self {
568 Array::from_bool(value)
569 }
570}
571
572impl From<i32> for Array {
573 fn from(value: i32) -> Self {
574 Array::from_int(value)
575 }
576}
577
578impl From<f32> for Array {
579 fn from(value: f32) -> Self {
580 Array::from_f32(value)
581 }
582}
583
584impl From<complex64> for Array {
585 fn from(value: complex64) -> Self {
586 Array::from_complex(value)
587 }
588}
589
590impl<T> From<T> for Array
591where
592 Array: FromNested<T>,
593{
594 fn from(value: T) -> Self {
595 Array::from_nested(value)
596 }
597}
598
599impl AsRef<Array> for Array {
600 fn as_ref(&self) -> &Array {
601 self
602 }
603}
604
605pub trait FromScalar<T>
609where
610 T: ArrayElement,
611{
612 fn from_scalar(val: T) -> Array;
614}
615
616impl FromScalar<bool> for Array {
617 fn from_scalar(val: bool) -> Array {
618 Array::from_bool(val)
619 }
620}
621
622impl FromScalar<i32> for Array {
623 fn from_scalar(val: i32) -> Array {
624 Array::from_int(val)
625 }
626}
627
628impl FromScalar<f32> for Array {
629 fn from_scalar(val: f32) -> Array {
630 Array::from_f32(val)
631 }
632}
633
634impl FromScalar<complex64> for Array {
635 fn from_scalar(val: complex64) -> Array {
636 Array::from_complex(val)
637 }
638}
639
640pub trait FromNested<T> {
649 fn from_nested(data: T) -> Array;
651}
652
653impl<T: FromSliceElement> FromNested<&[T]> for Array {
654 fn from_nested(data: &[T]) -> Self {
655 Array::from_slice(data, &[data.len() as i32])
656 }
657}
658
659impl<T: FromSliceElement, const N: usize> FromNested<[T; N]> for Array {
660 fn from_nested(data: [T; N]) -> Self {
661 Array::from_slice(&data, &[N as i32])
662 }
663}
664
665impl<T: FromSliceElement, const N: usize> FromNested<&[T; N]> for Array {
666 fn from_nested(data: &[T; N]) -> Self {
667 Array::from_slice(data, &[N as i32])
668 }
669}
670
671impl<T: FromSliceElement + Copy> FromNested<&[&[T]]> for Array {
672 fn from_nested(data: &[&[T]]) -> Self {
673 let row_len = data[0].len();
675 assert!(
676 data.iter().all(|row| row.len() == row_len),
677 "Rows must have the same length"
678 );
679
680 let shape = [data.len() as i32, row_len as i32];
681 let data = data
682 .iter()
683 .flat_map(|x| x.iter())
684 .copied()
685 .collect::<Vec<T>>();
686 Array::from_slice(&data, &shape)
687 }
688}
689
690impl<T: FromSliceElement + Copy, const N: usize> FromNested<[&[T]; N]> for Array {
691 fn from_nested(data: [&[T]; N]) -> Self {
692 let row_len = data[0].len();
694 assert!(
695 data.iter().all(|row| row.len() == row_len),
696 "Rows must have the same length"
697 );
698
699 let shape = [N as i32, row_len as i32];
700 let data = data
701 .iter()
702 .flat_map(|x| x.iter())
703 .copied()
704 .collect::<Vec<T>>();
705 Array::from_slice(&data, &shape)
706 }
707}
708
709impl<T: FromSliceElement + Copy, const N: usize> FromNested<&[[T; N]]> for Array {
710 fn from_nested(data: &[[T; N]]) -> Self {
711 let shape = [data.len() as i32, N as i32];
712 let data = data
713 .iter()
714 .flat_map(|x| x.iter().copied())
715 .collect::<Vec<T>>();
716 Array::from_slice(&data, &shape)
717 }
718}
719
720impl<T: FromSliceElement + Copy, const N: usize> FromNested<&[&[T; N]]> for Array {
721 fn from_nested(data: &[&[T; N]]) -> Self {
722 let shape = [data.len() as i32, N as i32];
723 let data = data
724 .iter()
725 .flat_map(|x| x.iter().copied())
726 .collect::<Vec<T>>();
727 Array::from_slice(&data, &shape)
728 }
729}
730
731impl<T: FromSliceElement + Copy, const N: usize, const M: usize> FromNested<[[T; N]; M]> for Array {
732 fn from_nested(data: [[T; N]; M]) -> Self {
733 let shape = [M as i32, N as i32];
734 let data = data
735 .iter()
736 .flat_map(|x| x.iter().copied())
737 .collect::<Vec<T>>();
738 Array::from_slice(&data, &shape)
739 }
740}
741
742impl<T: FromSliceElement + Copy, const N: usize, const M: usize> FromNested<&[[T; N]; M]>
743 for Array
744{
745 fn from_nested(data: &[[T; N]; M]) -> Self {
746 let shape = [M as i32, N as i32];
747 let data = data
748 .iter()
749 .flat_map(|x| x.iter().copied())
750 .collect::<Vec<T>>();
751 Array::from_slice(&data, &shape)
752 }
753}
754
755impl<T: FromSliceElement + Copy, const N: usize, const M: usize> FromNested<&[&[T; N]; M]>
756 for Array
757{
758 fn from_nested(data: &[&[T; N]; M]) -> Self {
759 let shape = [M as i32, N as i32];
760 let data = data
761 .iter()
762 .flat_map(|x| x.iter().copied())
763 .collect::<Vec<T>>();
764 Array::from_slice(&data, &shape)
765 }
766}
767
768impl<T: FromSliceElement + Copy> FromNested<&[&[&[T]]]> for Array {
769 fn from_nested(data: &[&[&[T]]]) -> Self {
770 let len_2d = data[0].len();
772 assert!(
773 data.iter().all(|x| x.len() == len_2d),
774 "2nd dimension must have the same length"
775 );
776
777 let len_3d = data[0][0].len();
779 assert!(
780 data.iter().all(|x| x.iter().all(|y| y.len() == len_3d)),
781 "3rd dimension must have the same length"
782 );
783
784 let shape = [data.len() as i32, len_2d as i32, len_3d as i32];
785 let data = data
786 .iter()
787 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
788 .collect::<Vec<T>>();
789 Array::from_slice(&data, &shape)
790 }
791}
792
793impl<T: FromSliceElement + Copy, const N: usize> FromNested<[&[&[T]]; N]> for Array {
794 fn from_nested(data: [&[&[T]]; N]) -> Self {
795 let len_2d = data[0].len();
797 assert!(
798 data.iter().all(|x| x.len() == len_2d),
799 "2nd dimension must have the same length"
800 );
801
802 let len_3d = data[0][0].len();
804 assert!(
805 data.iter().all(|x| x.iter().all(|y| y.len() == len_3d)),
806 "3rd dimension must have the same length"
807 );
808
809 let shape = [N as i32, len_2d as i32, len_3d as i32];
810 let data = data
811 .iter()
812 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
813 .collect::<Vec<T>>();
814 Array::from_slice(&data, &shape)
815 }
816}
817
818impl<T: FromSliceElement + Copy, const N: usize> FromNested<&[[&[T]; N]]> for Array {
819 fn from_nested(data: &[[&[T]; N]]) -> Self {
820 let len_3d = data[0][0].len();
822 assert!(
823 data.iter().all(|x| x.iter().all(|y| y.len() == len_3d)),
824 "3rd dimension must have the same length"
825 );
826
827 let shape = [data.len() as i32, N as i32, len_3d as i32];
828 let data = data
829 .iter()
830 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
831 .collect::<Vec<T>>();
832 Array::from_slice(&data, &shape)
833 }
834}
835
836impl<T: FromSliceElement + Copy, const N: usize> FromNested<&[&[[T; N]]]> for Array {
837 fn from_nested(data: &[&[[T; N]]]) -> Self {
838 let len_2d = data[0].len();
840 assert!(
841 data.iter().all(|x| x.len() == len_2d),
842 "2nd dimension must have the same length"
843 );
844
845 let shape = [data.len() as i32, len_2d as i32, N as i32];
846 let data = data
847 .iter()
848 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
849 .collect::<Vec<T>>();
850 Array::from_slice(&data, &shape)
851 }
852}
853
854impl<T: FromSliceElement + Copy, const N: usize, const M: usize> FromNested<[[&[T]; N]; M]>
855 for Array
856{
857 fn from_nested(data: [[&[T]; N]; M]) -> Self {
858 let len_3d = data[0][0].len();
860 assert!(
861 data.iter().all(|x| x.iter().all(|y| y.len() == len_3d)),
862 "3rd dimension must have the same length"
863 );
864
865 let shape = [M as i32, N as i32, len_3d as i32];
866 let data = data
867 .iter()
868 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
869 .collect::<Vec<T>>();
870 Array::from_slice(&data, &shape)
871 }
872}
873
874impl<T: FromSliceElement + Copy, const N: usize, const M: usize> FromNested<&[[&[T]; N]; M]>
875 for Array
876{
877 fn from_nested(data: &[[&[T]; N]; M]) -> Self {
878 let len_3d = data[0][0].len();
880 assert!(
881 data.iter().all(|x| x.iter().all(|y| y.len() == len_3d)),
882 "3rd dimension must have the same length"
883 );
884
885 let shape = [M as i32, N as i32, len_3d as i32];
886 let data = data
887 .iter()
888 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
889 .collect::<Vec<T>>();
890 Array::from_slice(&data, &shape)
891 }
892}
893
894impl<T: FromSliceElement + Copy, const N: usize, const M: usize> FromNested<&[&[[T; N]]; M]>
895 for Array
896{
897 fn from_nested(data: &[&[[T; N]]; M]) -> Self {
898 let len_2d = data[0].len();
900 assert!(
901 data.iter().all(|x| x.len() == len_2d),
902 "2nd dimension must have the same length"
903 );
904
905 let shape = [M as i32, len_2d as i32, N as i32];
906 let data = data
907 .iter()
908 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
909 .collect::<Vec<T>>();
910 Array::from_slice(&data, &shape)
911 }
912}
913
914impl<T: FromSliceElement + Copy, const N: usize, const M: usize, const O: usize>
915 FromNested<[[[T; N]; M]; O]> for Array
916{
917 fn from_nested(data: [[[T; N]; M]; O]) -> Self {
918 let shape = [O as i32, M as i32, N as i32];
919 let data = data
920 .iter()
921 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
922 .collect::<Vec<T>>();
923 Array::from_slice(&data, &shape)
924 }
925}
926
927impl<T: FromSliceElement + Copy, const N: usize, const M: usize, const O: usize>
928 FromNested<&[[[T; N]; M]; O]> for Array
929{
930 fn from_nested(data: &[[[T; N]; M]; O]) -> Self {
931 let shape = [O as i32, M as i32, N as i32];
932 let data = data
933 .iter()
934 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
935 .collect::<Vec<T>>();
936 Array::from_slice(&data, &shape)
937 }
938}
939
940impl<T: FromSliceElement + Copy, const N: usize, const M: usize, const O: usize>
941 FromNested<&[&[[T; N]; M]; O]> for Array
942{
943 fn from_nested(data: &[&[[T; N]; M]; O]) -> Self {
944 let shape = [O as i32, M as i32, N as i32];
945 let data = data
946 .iter()
947 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
948 .collect::<Vec<T>>();
949 Array::from_slice(&data, &shape)
950 }
951}
952
953impl<T: FromSliceElement + Copy, const N: usize, const M: usize, const O: usize>
954 FromNested<&[[&[T; N]; M]; O]> for Array
955{
956 fn from_nested(data: &[[&[T; N]; M]; O]) -> Self {
957 let shape = [O as i32, M as i32, N as i32];
958 let data = data
959 .iter()
960 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
961 .collect::<Vec<T>>();
962 Array::from_slice(&data, &shape)
963 }
964}
965
966impl<T: FromSliceElement + Copy, const N: usize, const M: usize, const O: usize>
967 FromNested<&[&[&[T; N]; M]; O]> for Array
968{
969 fn from_nested(data: &[&[&[T; N]; M]; O]) -> Self {
970 let shape = [O as i32, M as i32, N as i32];
971 let data = data
972 .iter()
973 .flat_map(|x| x.iter().flat_map(|y| y.iter().copied()))
974 .collect::<Vec<T>>();
975 Array::from_slice(&data, &shape)
976 }
977}
978
979#[cfg(test)]
980mod tests {
981 use super::*;
982 use crate::{array, ops::broadcast_to};
983
984 #[test]
985 fn broadcast_view_cannot_be_borrowed_as_slice() {
986 let broadcast = broadcast_to(&array!([1, 2]), &[3, 2]).unwrap();
987
988 assert_eq!(
989 broadcast.try_as_slice::<i32>(),
990 Err(AsSliceError::NotContiguous)
991 );
992 }
993
994 #[test]
995 fn new_scalar_array_from_bool() {
996 let array = Array::from_bool(true);
997 assert!(array.item_exact::<bool>());
998 assert_eq!(array.item_size(), 1);
999 assert_eq!(array.size(), 1);
1000 assert!(array.strides().is_empty());
1001 assert_eq!(array.nbytes(), 1);
1002 assert_eq!(array.ndim(), 0);
1003 assert!(array.shape().is_empty());
1004 assert_eq!(array.dtype(), Dtype::Bool);
1005 }
1006
1007 #[test]
1008 fn new_scalar_array_from_int() {
1009 let array = Array::from_int(42);
1010 assert_eq!(array.item_exact::<i32>(), 42);
1011 assert_eq!(array.item_size(), 4);
1012 assert_eq!(array.size(), 1);
1013 assert!(array.strides().is_empty());
1014 assert_eq!(array.nbytes(), 4);
1015 assert_eq!(array.ndim(), 0);
1016 assert!(array.shape().is_empty());
1017 assert_eq!(array.dtype(), Dtype::Int32);
1018 }
1019
1020 #[test]
1021 fn new_scalar_array_from_f32() {
1022 let array = Array::from_f32(3.14);
1023 assert_eq!(array.item_exact::<f32>(), 3.14);
1024 assert_eq!(array.item_size(), 4);
1025 assert_eq!(array.size(), 1);
1026 assert!(array.strides().is_empty());
1027 assert_eq!(array.nbytes(), 4);
1028 assert_eq!(array.ndim(), 0);
1029 assert!(array.shape().is_empty());
1030 assert_eq!(array.dtype(), Dtype::Float32);
1031 }
1032
1033 #[test]
1034 fn new_scalar_array_from_f64() {
1035 let array = Array::from_f64(3.14).as_dtype(Dtype::Float64).unwrap();
1036 float_eq::assert_float_eq!(array.item_exact::<f64>(), 3.14, abs <= 1e-5);
1037 assert_eq!(array.item_size(), 8);
1038 assert_eq!(array.size(), 1);
1039 assert!(array.strides().is_empty());
1040 assert_eq!(array.nbytes(), 8);
1041 assert_eq!(array.ndim(), 0);
1042 assert!(array.shape().is_empty());
1043 assert_eq!(array.dtype(), Dtype::Float64);
1044 }
1045
1046 #[test]
1047 fn new_array_from_slice_f64() {
1048 let array = Array::from_slice_f64(&[1.0, 2.0, 3.0], &[3]);
1049 assert_eq!(array.item_size(), 8);
1050 assert_eq!(array.size(), 3);
1051 assert_eq!(array.strides(), &[1]);
1052 assert_eq!(array.nbytes(), 24);
1053 assert_eq!(array.ndim(), 1);
1054 assert_eq!(array.dim(0), 3);
1055 assert_eq!(array.shape(), &[3]);
1056 assert_eq!(array.dtype(), Dtype::Float64);
1057 }
1058
1059 #[test]
1060 fn new_scalar_array_from_complex() {
1061 let val = complex64::new(1.0, 2.0);
1062 let array = Array::from_complex(val);
1063 assert_eq!(array.item_exact::<complex64>(), val);
1064 assert_eq!(array.item_size(), 8);
1065 assert_eq!(array.size(), 1);
1066 assert!(array.strides().is_empty());
1067 assert_eq!(array.nbytes(), 8);
1068 assert_eq!(array.ndim(), 0);
1069 assert!(array.shape().is_empty());
1070 assert_eq!(array.dtype(), Dtype::Complex64);
1071 }
1072
1073 #[test]
1074 fn new_array_from_single_element_slice() {
1075 let data = [1i32];
1076 let array = Array::from_slice(&data, &[1]);
1077 assert_eq!(array.as_slice::<i32>(), &data[..]);
1078 assert_eq!(array.item_exact::<i32>(), 1);
1079 assert_eq!(array.item_size(), 4);
1080 assert_eq!(array.size(), 1);
1081 assert_eq!(array.strides(), &[1]);
1082 assert_eq!(array.nbytes(), 4);
1083 assert_eq!(array.ndim(), 1);
1084 assert_eq!(array.dim(0), 1);
1085 assert_eq!(array.shape(), &[1]);
1086 assert_eq!(array.dtype(), Dtype::Int32);
1087 }
1088
1089 #[test]
1090 fn new_array_from_multi_element_slice() {
1091 let data = [1i32, 2, 3, 4, 5];
1092 let array = Array::from_slice(&data, &[5]);
1093 assert_eq!(array.as_slice::<i32>(), &data[..]);
1094 assert_eq!(array.item_size(), 4);
1095 assert_eq!(array.size(), 5);
1096 assert_eq!(array.strides(), &[1]);
1097 assert_eq!(array.nbytes(), 20);
1098 assert_eq!(array.ndim(), 1);
1099 assert_eq!(array.dim(0), 5);
1100 assert_eq!(array.shape(), &[5]);
1101 assert_eq!(array.dtype(), Dtype::Int32);
1102 }
1103
1104 #[test]
1105 fn new_2d_array_from_slice() {
1106 let data = [1i32, 2, 3, 4, 5, 6];
1107 let array = Array::from_slice(&data, &[2, 3]);
1108 assert_eq!(array.as_slice::<i32>(), &data[..]);
1109 assert_eq!(array.item_size(), 4);
1110 assert_eq!(array.size(), 6);
1111 assert_eq!(array.strides(), &[3, 1]);
1112 assert_eq!(array.nbytes(), 24);
1113 assert_eq!(array.ndim(), 2);
1114 assert_eq!(array.dim(0), 2);
1115 assert_eq!(array.dim(1), 3);
1116 assert_eq!(array.dim(-1), 3); assert_eq!(array.dim(-2), 2); assert_eq!(array.shape(), &[2, 3]);
1119 assert_eq!(array.dtype(), Dtype::Int32);
1120 }
1121
1122 #[test]
1123 fn deep_cloned_array_has_different_ptr() {
1124 let data = [1i32, 2, 3, 4, 5];
1125 let orig = Array::from_slice(&data, &[5]);
1126 let clone = orig.deep_clone();
1127
1128 assert_eq!(orig.as_slice::<i32>(), clone.as_slice::<i32>());
1130
1131 assert_ne!(orig.as_ptr().ctx, clone.as_ptr().ctx);
1133
1134 assert_ne!(
1136 orig.as_slice::<i32>().as_ptr(),
1137 clone.as_slice::<i32>().as_ptr()
1138 );
1139 }
1140
1141 #[test]
1142 fn test_array_eq() {
1143 let data = [1i32, 2, 3, 4, 5];
1144 let array1 = Array::from_slice(&data, &[5]);
1145 let array2 = Array::from_slice(&data, &[5]);
1146 let array3 = Array::from_slice(&[1i32, 2, 3, 4, 6], &[5]);
1147
1148 assert!(array1.eq_exact(&array2).unwrap());
1149 assert!(!array1.eq_exact(&array3).unwrap());
1150 }
1151
1152 #[test]
1153 fn test_array_item_non_scalar() {
1154 let data = [1i32, 2, 3, 4, 5];
1155 let array = Array::from_slice(&data, &[5]);
1156 assert!(array.try_item_exact::<i32>().is_err());
1157 }
1158
1159 #[test]
1160 fn test_item_type_conversion() {
1161 let array = Array::from_f32(1.0);
1162 assert_eq!(array.item_cast::<i32>(), 1);
1163 assert_eq!(array.item_cast::<complex64>(), complex64::new(1.0, 0.0));
1164 assert_eq!(array.item_cast::<u8>(), 1);
1165
1166 assert_eq!(array.as_slice::<f32>(), &[1.0]);
1167 }
1168
1169 #[test]
1170 fn exact_observers_reject_dtype_conversion() {
1171 crate::with_device(crate::Device::cpu(), || {
1172 let array = Array::from_slice(&[1_i32, 2, 3], &[3]);
1173 let scalar = Array::from_int(1);
1174
1175 let vector_error = array.to_vec_exact::<f32>().unwrap_err();
1176 assert!(
1177 matches!(
1178 vector_error,
1179 crate::error::ConversionError::DtypeMismatch {
1180 expected: Dtype::Float32,
1181 actual: Dtype::Int32,
1182 }
1183 ),
1184 "{vector_error:?}; dtype={:?}",
1185 array.dtype()
1186 );
1187 assert!(matches!(
1188 scalar.try_item_exact::<f32>(),
1189 Err(crate::error::ConversionError::DtypeMismatch {
1190 expected: Dtype::Float32,
1191 actual: Dtype::Int32,
1192 })
1193 ));
1194 });
1195 }
1196
1197 #[test]
1198 fn casting_observers_convert_values() {
1199 crate::with_device(crate::Device::cpu(), || {
1200 let vector = Array::from_slice(&[1_i32, 2, 3], &[3]);
1201 let scalar = Array::from_int(7);
1202
1203 assert_eq!(vector.to_vec_cast::<f32>().unwrap(), vec![1.0, 2.0, 3.0]);
1204 assert_eq!(scalar.try_item_cast::<f32>().unwrap(), 7.0);
1205 });
1206 }
1207
1208 #[test]
1209 fn explicit_array_comparisons_distinguish_dtype_and_values() {
1210 crate::with_device(crate::Device::cpu(), || {
1211 let integers = Array::from_slice(&[1_i32, 2, 3], &[3]);
1212 let same_integers = Array::from_slice(&[1_i32, 2, 3], &[3]);
1213 let floats = Array::from_slice(&[1.0_f32, 2.0, 3.0], &[3]);
1214 let nearby = Array::from_slice(&[1.0_f32, 2.0, 3.000_001], &[3]);
1215
1216 assert!(integers.eq_exact(&same_integers).unwrap());
1217 assert!(!integers.eq_exact(&floats).unwrap());
1218 assert!(integers.eq_values(&floats).unwrap());
1219 assert!(floats
1220 .all_close(&nearby, Some(1e-5), Some(1e-8), Some(false))
1221 .unwrap());
1222 });
1223 }
1224}