extendr_api/optional/
faer.rs

1use faer::{mat, Mat, MatRef};
2
3use crate::scalar::Rfloat;
4use crate::*;
5
6/// Convert a `faer::Mat<f64>` into an `RMatrix<f64>` which is not NA aware.
7impl From<Mat<f64>> for RMatrix<f64> {
8    fn from(value: Mat<f64>) -> Self {
9        RMatrix::new_matrix(value.nrows(), value.ncols(), |i, j| value.read(i, j))
10    }
11}
12
13impl From<Mat<f64>> for Robj {
14    fn from(value: Mat<f64>) -> Self {
15        RMatrix::<f64>::from(value).into()
16    }
17}
18
19/// Convert a `faer::Mat<f64>` into an `RMatrix<f64>` which is not NA aware.
20impl From<MatRef<'_, f64>> for RMatrix<f64> {
21    /// Convert a faer MatRef<f64> into Robj.
22    fn from(value: MatRef<'_, f64>) -> Self {
23        RMatrix::new_matrix(value.nrows(), value.ncols(), |i, j| value.read(i, j))
24    }
25}
26
27impl From<MatRef<'_, f64>> for Robj {
28    fn from(value: MatRef<'_, f64>) -> Self {
29        RMatrix::<f64>::from(value).into_robj()
30    }
31}
32
33impl From<Mat<f64>> for RMatrix<Rfloat> {
34    fn from(value: Mat<f64>) -> Self {
35        RMatrix::new_matrix(value.nrows(), value.ncols(), |i, j| value.read(i, j).into())
36    }
37}
38
39impl From<MatRef<'_, f64>> for RMatrix<Rfloat> {
40    fn from(value: MatRef<f64>) -> Self {
41        RMatrix::new_matrix(value.nrows(), value.ncols(), |i, j| {
42            Rfloat::from(value.read(i, j))
43        })
44    }
45}
46
47impl From<RMatrix<f64>> for Mat<f64> {
48    fn from(value: RMatrix<f64>) -> Self {
49        let nrow = value.nrows();
50        let ncol = value.ncols();
51        let slice = value.as_real_slice().expect("RMatrix should be doubles");
52        Mat::from_fn(nrow, ncol, |i, j| slice[i + j * nrow])
53    }
54}
55
56impl<'a> From<&'_ RMatrix<f64>> for MatRef<'a, f64> {
57    fn from(value: &RMatrix<f64>) -> Self {
58        let nrow = value.nrows();
59        let ncol = value.ncols();
60        let slice = value.as_typed_slice().expect("RMatrix should be doubles");
61        let mat_ref = faer::mat::from_column_major_slice(slice, nrow, ncol);
62        mat_ref
63    }
64}
65
66impl TryFrom<&Robj> for Mat<f64> {
67    type Error = Error;
68
69    fn try_from(robj: &Robj) -> Result<Self> {
70        let rmat = &RMatrix::<f64>::try_from(robj)?;
71        let nrow = rmat.nrows();
72        let ncol = rmat.ncols();
73
74        if let Some(slice) = robj.as_real_slice() {
75            let fmat = Mat::from_fn(nrow, ncol, |i, j| slice[i + j * nrow]);
76            Ok(fmat)
77        } else {
78            Err(Error::ExpectedReal(robj.clone()))
79        }
80    }
81}
82
83impl<'a> TryFrom<&'_ Robj> for MatRef<'a, f64> {
84    type Error = Error;
85
86    fn try_from(robj: &Robj) -> Result<Self> {
87        let rmat = &RMatrix::<f64>::try_from(robj)?;
88        let nrows = rmat.nrows();
89        let ncols = rmat.ncols();
90
91        if let Some(slice) = robj.as_typed_slice() {
92            let fmat = mat::from_column_major_slice(slice, nrows, ncols);
93            Ok(fmat)
94        } else {
95            Err(Error::ExpectedReal(robj.clone()))
96        }
97    }
98}
99
100impl TryFrom<Robj> for Mat<f64> {
101    type Error = crate::Error;
102
103    fn try_from(robj: Robj) -> Result<Self> {
104        Self::try_from(&robj)
105    }
106}
107
108impl<'a> TryFrom<Robj> for MatRef<'a, f64> {
109    type Error = crate::Error;
110
111    fn try_from(robj: Robj) -> Result<Self> {
112        Self::try_from(&robj)
113    }
114}
115
116impl From<RMatrix<i32>> for Mat<f64> {
117    fn from(value: RMatrix<i32>) -> Self {
118        let nrow = value.nrows();
119        let ncol = value.ncols();
120        let slice = value
121            .as_integer_slice()
122            .expect("RMatrix should be integers");
123        Mat::from_fn(nrow, ncol, |i, j| slice[i + j * nrow] as f64)
124    }
125}
126
127#[cfg(test)]
128mod test {
129    use crate as extendr_api;
130    use crate::*;
131    use faer::{mat, Mat, MatRef};
132
133    #[test]
134    fn test_rmatrix_to_faer_mat() {
135        test! {
136            let values = [
137                [1.0, 5.0, 9.0],
138                [2.0, 6.0, 10.0],
139                [3.0, 7.0, 11.0],
140                [4.0, 8.0, 12.0f64]
141            ];
142            let a = Mat::<f64>::from_fn(4, 3, |i, j| values[i][j] as f64);
143
144            let rmatrix = RMatrix::new_matrix(4, 3, |i, j| values[i][j]);
145            let b = Mat::<f64>::try_from(rmatrix);
146            assert_eq!(a, b.expect("matrix to be converted"));
147        }
148    }
149
150    #[test]
151    fn test_rmatrix_to_faer_mat_with_nan() {
152        test! {
153            let values = [
154                [1.0, 5.0, 9.0],
155                [2.0, 6.0, 10.0],
156                [3.0, 7.0, 11.0],
157                [f64::NAN, 8.0, 12.0f64]
158            ];
159
160            let rmatrix = RMatrix::new_matrix(4, 3, |i, j| values[i][j]);
161            let b = Mat::<f64>::try_from(rmatrix);
162            assert!(b.expect("matrix to be converted").read(3, 0).is_nan());
163        }
164    }
165
166    #[test]
167    fn test_rmatrix_to_faer_mat_ref() {
168        test! {
169            let values = [
170                [1.0, 5.0, 9.0],
171                [2.0, 6.0, 10.0],
172                [3.0, 7.0, 11.0],
173                [4.0, 8.0, 12.0f64]
174            ];
175            let mat = Mat::<f64>::from_fn(4, 3, |i, j| values[i][j] as f64);
176            let a = mat.as_ref();
177
178            let rmatrix = RMatrix::new_matrix(4, 3, |i, j| values[i][j]);
179            let b = MatRef::<f64>::try_from(&rmatrix);
180            assert_eq!(a, b.expect("matrix to be converted"));
181        }
182    }
183
184    #[test]
185    fn test_faer_mat_to_rmatrix() {
186        test! {
187            let vec: Vec<f64> = (1..13).map(f64::from).collect();
188            let a = mat![
189                [1.0, 5.0, 9.0],
190                [2.0, 6.0, 10.0],
191                [3.0, 7.0, 11.0],
192                [4.0, 8.0, 12.0f64],
193            ];
194            let rmatrix: RMatrix<f64> = a.into();
195            assert_eq!(rmatrix.as_real_slice().expect("slice"), &vec);
196        }
197    }
198
199    #[test]
200    fn test_faer_mat_ref_to_rmatrix() {
201        test! {
202            let vec: Vec<f64> = (1..13).map(f64::from).collect();
203            let a = mat![
204                [1.0, 5.0, 9.0],
205                [2.0, 6.0, 10.0],
206                [3.0, 7.0, 11.0],
207                [4.0, 8.0, 12.0f64],
208            ];
209            let rmatrix: RMatrix<f64> = a.as_ref().into();
210            assert_eq!(rmatrix.as_real_slice().expect("slice"), &vec);
211        }
212    }
213
214    #[test]
215    fn test_try_from_robj_to_faer_mat() {
216        test! {
217            let values = [
218                [1.0, 5.0, 9.0],
219                [2.0, 6.0, 10.0],
220                [3.0, 7.0, 11.0],
221                [4.0, 8.0, 12.0f64]
222            ];
223            let a = Mat::<f64>::from_fn(4, 3, |i, j| values[i][j] as f64);
224
225            let rmatrix = RMatrix::new_matrix(4, 3, |i, j| values[i][j]);
226            let b = Mat::<f64>::try_from(&Robj::from(rmatrix));
227            assert_eq!(a, b.expect("matrix to be converted"));
228        }
229    }
230
231    #[test]
232    fn test_try_from_robj_to_faer_mat_ref() {
233        test! {
234            let values = [
235                [1.0, 5.0, 9.0],
236                [2.0, 6.0, 10.0],
237                [3.0, 7.0, 11.0],
238                [4.0, 8.0, 12.0f64]
239            ];
240            let mat = Mat::<f64>::from_fn(4, 3, |i, j| values[i][j] as f64);
241            let a = mat.as_ref();
242
243            let rmatrix = RMatrix::new_matrix(4, 3, |i, j| values[i][j]);
244            let robj = Robj::from(rmatrix);
245            let b = MatRef::<f64>::try_from(&robj);
246            assert_eq!(a, b.expect("matrix to be converted"));
247        }
248    }
249
250    #[test]
251    fn test_int_rmatrix_to_faer_mat() {
252        test! {
253            let values = [
254                [1, 5, 9],
255                [2, 6, 10],
256                [3, 7, 11],
257                [4, 8, 12]
258            ];
259            let a = Mat::<f64>::from_fn(4, 3, |i, j| values[i][j] as f64);
260
261            let rmatrix = RMatrix::new_matrix(4, 3, |i, j| values[i][j]);
262            let b = Mat::<f64>::try_from(rmatrix);
263            assert_eq!(a, b.expect("matrix to be converted"));
264        }
265    }
266}