matfile/
ndarray.rs

1//! Helpers for converting between `matfile::Array` and `ndarray::Array`.
2//!
3//! While `matfile` arrays abstract over the underlying data type, `ndarray`
4//! arrays are parameterized by a concrete data type. Thus the conversions
5//! provided are fallible in case the data types are not compatible.
6//!
7//! # Examples
8//!
9//! First, bring the `TryInto` trait into scope:
10//!
11//! ```rust
12//! use std::convert::TryInto;
13//! ```
14//!
15//! ## Dynamically dimensioned arrays
16//!
17//! Converting a `matfile` array `mf_arr` to a dynamic dimension `ndarray` array
18//! `nd_arr`:
19//! ```rust
20//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
21//! #     let data = include_bytes!("../tests/multidimensional.mat");
22//! #     let mat_file = matfile::MatFile::parse(data.as_ref()).unwrap();
23//! #     let mf_arr = &mat_file.arrays()[0];
24//! #     use ndarr as ndarray;
25//! #     use std::convert::TryInto;
26//! let nd_arr: ndarray::ArrayD<f64> = mf_arr.try_into()?;
27//! #     Ok(())
28//! # }
29//! ```
30//!
31//! ## Statically dimensioned arrays
32//!
33//! Converting a `matfile` array `mf_arr` to a static dimension `ndarray` array
34//! `nd_arr`:
35//! ```rust
36//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
37//! #     let data = include_bytes!("../tests/single_complex.mat");
38//! #     let mat_file = matfile::MatFile::parse(data.as_ref()).unwrap();
39//! #     let mf_arr = &mat_file.arrays()[0];
40//! #     use ndarr as ndarray;
41//! #     use num_complex;
42//! #     use std::convert::TryInto;
43//! let nd_arr: ndarray::Array2<num_complex::Complex<f32>> = mf_arr.try_into()?;
44//! #     Ok(())
45//! # }
46//! ```
47
48use ndarr as nd;
49use ndarr::IntoDimension;
50use ndarr::ShapeBuilder;
51use num_complex::Complex;
52use std::convert::TryInto;
53
54#[derive(Debug)]
55pub enum Error {
56    /// Generated when the shape (number of dimensions and their respective
57    /// sizes) do not match
58    ShapeError,
59    /// Generated when the number formats are incompatible
60    TypeError,
61}
62
63impl std::fmt::Display for Error {
64    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
65        match self {
66            Error::ShapeError => write!(f, "Array shapes do not match"),
67            Error::TypeError => write!(f, "Array types are not compatible"),
68        }
69    }
70}
71
72impl std::error::Error for Error {
73    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74        None
75    }
76}
77
78macro_rules! dynamic_conversions {
79    ( $num:ty, $variant:ident ) => {
80        impl<'me> TryInto<nd::ArrayViewD<'me, $num>> for &'me crate::Array {
81            type Error = Error;
82            fn try_into(self) -> Result<nd::ArrayViewD<'me, $num>, Self::Error> {
83                match self.data() {
84                    crate::NumericData::$variant {
85                        ref real,
86                        imag: None,
87                    } => {
88                        let dimension: nd::IxDyn = self.size().clone().into_dimension();
89                        nd::ArrayView::from_shape(dimension.set_f(true), real)
90                            .map_err(|_err| Error::ShapeError)
91                    }
92                    _ => Err(Error::TypeError),
93                }
94            }
95        }
96
97        impl TryInto<nd::ArrayD<$num>> for &crate::Array {
98            type Error = Error;
99            fn try_into(self) -> Result<nd::ArrayD<$num>, Self::Error> {
100                match self.data() {
101                    crate::NumericData::$variant {
102                        ref real,
103                        imag: None,
104                    } => {
105                        let dimension: nd::IxDyn = self.size().clone().into_dimension();
106                        nd::Array::from_shape_vec(dimension.set_f(true), real.clone())
107                            .map_err(|_err| Error::ShapeError)
108                    }
109                    _ => Err(Error::TypeError),
110                }
111            }
112        }
113
114        impl TryInto<nd::ArrayD<Complex<$num>>> for &crate::Array {
115            type Error = Error;
116            fn try_into(self) -> Result<nd::ArrayD<Complex<$num>>, Self::Error> {
117                match self.data() {
118                    crate::NumericData::$variant {
119                        ref real,
120                        imag: Some(ref imag),
121                    } => {
122                        let dimension: nd::IxDyn = self.size().clone().into_dimension();
123                        let values = real
124                            .iter()
125                            .zip(imag.iter())
126                            .map(|(&re, &im)| Complex::new(re, im))
127                            .collect();
128                        nd::Array::from_shape_vec(dimension.set_f(true), values)
129                            .map_err(|_err| Error::ShapeError)
130                    }
131                    _ => Err(Error::TypeError),
132                }
133            }
134        }
135    };
136}
137
138macro_rules! static_conversions_n {
139    ( $num:ty, $variant:ident, $ndims:literal ) => {
140        impl<'me> TryInto<nd::ArrayView<'me, $num, nd::Dim<[nd::Ix; $ndims]>>>
141            for &'me crate::Array
142        {
143            type Error = Error;
144            fn try_into(
145                self,
146            ) -> Result<nd::ArrayView<'me, $num, nd::Dim<[nd::Ix; $ndims]>>, Self::Error> {
147                let size = self.size();
148                if size.len() != $ndims {
149                    return Err(Error::ShapeError);
150                }
151                let mut shape = [0; $ndims];
152                shape.copy_from_slice(size);
153                match self.data() {
154                    crate::NumericData::$variant {
155                        ref real,
156                        imag: None,
157                    } => {
158                        let dimension: nd::Dim<[nd::Ix; $ndims]> = shape.into_dimension();
159                        nd::ArrayView::from_shape(dimension.set_f(true), real)
160                            .map_err(|_err| Error::ShapeError)
161                    }
162                    _ => Err(Error::TypeError),
163                }
164            }
165        }
166
167        impl TryInto<nd::Array<$num, nd::Dim<[nd::Ix; $ndims]>>> for &crate::Array {
168            type Error = Error;
169            fn try_into(self) -> Result<nd::Array<$num, nd::Dim<[nd::Ix; $ndims]>>, Self::Error> {
170                let size = self.size();
171                if size.len() != $ndims {
172                    return Err(Error::ShapeError);
173                }
174                let mut shape = [0; $ndims];
175                shape.copy_from_slice(size);
176                match self.data() {
177                    crate::NumericData::$variant {
178                        ref real,
179                        imag: None,
180                    } => {
181                        let dimension: nd::Dim<[nd::Ix; $ndims]> = shape.into_dimension();
182                        nd::Array::from_shape_vec(dimension.set_f(true), real.clone())
183                            .map_err(|_err| Error::ShapeError)
184                    }
185                    _ => Err(Error::TypeError),
186                }
187            }
188        }
189
190        impl TryInto<nd::Array<Complex<$num>, nd::Dim<[nd::Ix; $ndims]>>> for &crate::Array {
191            type Error = Error;
192            fn try_into(
193                self,
194            ) -> Result<nd::Array<Complex<$num>, nd::Dim<[nd::Ix; $ndims]>>, Self::Error> {
195                let size = self.size();
196                if size.len() != $ndims {
197                    return Err(Error::ShapeError);
198                }
199                let mut shape = [0; $ndims];
200                shape.copy_from_slice(size);
201                match self.data() {
202                    crate::NumericData::$variant {
203                        ref real,
204                        imag: Some(ref imag),
205                    } => {
206                        let dimension: nd::Dim<[nd::Ix; $ndims]> = shape.into_dimension();
207                        let values = real
208                            .iter()
209                            .zip(imag.iter())
210                            .map(|(&re, &im)| Complex::new(re, im))
211                            .collect();
212                        nd::Array::from_shape_vec(dimension.set_f(true), values)
213                            .map_err(|_err| Error::ShapeError)
214                    }
215                    _ => Err(Error::TypeError),
216                }
217            }
218        }
219    };
220}
221
222macro_rules! static_conversions {
223    ( $num:ty, $variant:ident ) => {
224        static_conversions_n!($num, $variant, 2);
225        static_conversions_n!($num, $variant, 3);
226        static_conversions_n!($num, $variant, 4);
227        static_conversions_n!($num, $variant, 5);
228        static_conversions_n!($num, $variant, 6);
229    };
230}
231
232macro_rules! all_conversions {
233    ( $num:ty, $variant:ident ) => {
234        dynamic_conversions!($num, $variant);
235        static_conversions!($num, $variant);
236    };
237}
238
239all_conversions!(f64, Double);
240all_conversions!(f32, Single);
241all_conversions!(i64, Int64);
242all_conversions!(u64, UInt64);
243all_conversions!(i32, Int32);
244all_conversions!(u32, UInt32);
245all_conversions!(i16, Int16);
246all_conversions!(u16, UInt16);
247all_conversions!(i8, Int8);
248all_conversions!(u8, UInt8);