mdarray_linalg_faer/
lib.rs

1//! ```rust
2//! use mdarray::{DTensor, tensor};
3//! use mdarray_linalg::prelude::*; // Imports only traits
4//! use mdarray_linalg::eig::EigDecomp;
5//! use mdarray_linalg::svd::SVDDecomp;
6//!
7//! use mdarray_linalg_faer::Faer;
8//!
9//! // Declare two matrices
10//! let a = tensor![[1., 2.], [3., 4.]];
11//! let b = tensor![[5., 6.], [7., 8.]];
12//!
13//! // ----- Matrix multiplication -----
14//! let c = Faer.matmul(&a, &b).eval();
15//! println!("A * B = {:?}", c);
16//!
17//! // ----- Eigenvalue decomposition -----
18//! // Note: we must clone `a` here because decomposition routines destroy the input.
19//! let bd = Faer;
20//! let EigDecomp {
21//!     eigenvalues,
22//!     right_eigenvectors,
23//!     ..
24//! } = bd.eig(&mut a.clone()).expect("Eigenvalue decomposition failed");
25//!
26//! println!("Eigenvalues: {:?}", eigenvalues);
27//! if let Some(vectors) = right_eigenvectors {
28//!     println!("Right eigenvectors: {:?}", vectors);
29//! }
30//!
31//! // ----- Singular Value Decomposition (SVD) -----
32//! let SVDDecomp { s, u, vt } = bd.svd(&mut a.clone()).expect("SVD failed");
33//! println!("Singular values: {:?}", s);
34//! println!("Left singular vectors U: {:?}", u);
35//! println!("Right singular vectors V^T: {:?}", vt);
36//!
37//! // ----- QR Decomposition -----
38//! let (m, n) = *a.shape();
39//! let mut q = DTensor::<f64, 2>::zeros([m, m]);
40//! let mut r = DTensor::<f64, 2>::zeros([m, n]);
41//!
42//! bd.qr_overwrite(&mut a.clone(), &mut q, &mut r); //
43//! println!("Q: {:?}", q);
44//! println!("R: {:?}", r);
45//! ```
46
47pub mod eig;
48pub mod lu;
49pub mod matmul;
50pub mod qr;
51pub mod solve;
52pub mod svd;
53
54#[derive(Default)]
55pub struct Faer;
56
57use mdarray::{DSlice, DTensor, Layout, Strided, StridedMapping, View};
58use std::mem::ManuallyDrop;
59
60/// Converts a `DSlice<T, 2, L>` (from `mdarray`) into a `faer::MatRef<'static, T>`.
61/// This function **does not copy** any data.
62pub fn into_faer<T, L: Layout>(mat: &DSlice<T, 2, L>) -> faer::mat::MatRef<'static, T> {
63    let (nrows, ncols) = *mat.shape();
64    let strides = (mat.stride(0), mat.stride(1));
65
66    // SAFETY:
67    // We are constructing a MatRef from raw parts. This requires that:
68    // - `mat.as_ptr()` points to a valid matrix of size `nrows x ncols`
69    // - The given strides correctly describe the memory layout
70    unsafe { faer::MatRef::from_raw_parts(mat.as_ptr(), nrows, ncols, strides.0, strides.1) }
71}
72
73/// Converts a `DSlice<T, 2, L>` (from `mdarray`) into a `faer::MatMut<'static, T>`.
74/// This function **does not copy** any data.
75pub fn into_faer_mut<T, L: Layout>(mat: &mut DSlice<T, 2, L>) -> faer::mat::MatMut<'static, T> {
76    let (nrows, ncols) = *mat.shape();
77    let strides = (mat.stride(0), mat.stride(1));
78
79    // SAFETY:
80    // We are constructing a MatMut from raw parts. This requires that:
81    // - `mat.as_mut_ptr()` points to a valid mutable matrix of size `nrows x ncols`
82    // - The given strides correctly describe the memory layout
83    unsafe {
84        faer::MatMut::from_raw_parts_mut(
85            mat.as_mut_ptr() as *mut _,
86            nrows,
87            ncols,
88            strides.0,
89            strides.1,
90        )
91    }
92}
93
94/// Converts a `faer::Mat<T>` into a `DTensor<T, 2>` (from `mdarray`) by constructing
95/// a strided view over the matrix memory. This function **does not copy** any data.
96pub fn into_mdarray<T: std::clone::Clone>(mat: faer::Mat<T>) -> DTensor<T, 2> {
97    // Manually dropping to avoid a double free: DTensor will take ownership of the data,
98    // so we must prevent Rust from automatically dropping the original matrix.
99    let mut mat = ManuallyDrop::new(mat);
100
101    let (nrows, ncols) = (mat.nrows(), mat.ncols());
102
103    // faer and mdarray have different memory layouts; we need to construct a
104    // strided mapping explicitly to describe the layout of `mat` to mdarray.
105    let mapping = StridedMapping::new((nrows, ncols), &[mat.row_stride(), mat.col_stride()]);
106
107    // SAFETY:
108    // We use `new_unchecked` because the memory layout in faer isn't guaranteed
109    // to satisfy mdarray's internal invariants automatically.
110    // `from_raw_parts` isn't usable here due to layout incompatibilities.
111    let view_strided: View<'_, _, (usize, usize), Strided> =
112        unsafe { mdarray::View::new_unchecked(mat.as_ptr_mut(), mapping) };
113
114    DTensor::<T, 2>::from(view_strided)
115}
116
117/// Converts a `DSlice<T, 2, L>` (from `mdarray`) into a
118/// `faer::MatMut<'static, T>` and transposes data.  This function
119/// **does not copy** any data.
120pub fn into_faer_mut_transpose<T, L: Layout>(
121    mat: &mut DSlice<T, 2, L>,
122) -> faer::mat::MatMut<'static, T> {
123    let (nrows, ncols) = *mat.shape();
124    let strides = (mat.stride(0), mat.stride(1));
125
126    // SAFETY:
127    // We are constructing a MatMut from raw parts. This requires that:
128    // - `mat.as_mut_ptr()` points to a valid mutable matrix of size `nrows x ncols`
129    // - The given strides correctly describe the memory layout
130    unsafe {
131        faer::MatMut::from_raw_parts_mut(
132            mat.as_mut_ptr() as *mut _,
133            nrows,
134            ncols,
135            strides.1,
136            strides.0,
137        )
138    }
139}
140
141/// Converts a mutable `DSlice<T, 2, L>` (from `mdarray`) into a `faer::diag::DiagMut<'static, T>`,
142/// which is a mutable view over the diagonal elements of a matrix in Faer.
143///
144/// # Important Notes for Users:
145/// - This function **does not copy** any data. It gives direct mutable access to
146///   the diagonal values of the matrix represented by `mat`.
147/// - The stride along the **Y-axis (i.e., column stride)** is chosen to be consistent
148///   with LAPACK-style storage, where singular values are typically stored in the first row.
149/// - This function is unsafe internally and assumes that `mat` contains at least `n` elements
150///   in memory laid out consistently with the given stride.
151pub fn into_faer_diag_mut<T, L: Layout>(
152    mat: &mut DSlice<T, 2, L>,
153) -> faer::diag::DiagMut<'static, T> {
154    let (n, _) = *mat.shape();
155
156    // SAFETY:
157    // - `mat.as_mut_ptr()` must point to a buffer with at least `n` diagonal elements.
158    // - `mat.stride(1)` is used as the step between diagonal elements, assuming storage
159    //   along the first row for compatibility with LAPACK convention.
160    unsafe { faer::diag::DiagMut::from_raw_parts_mut(mat.as_mut_ptr() as *mut _, n, mat.stride(1)) }
161}