matfile_ndarray/
lib.rs

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