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