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}