1use mdarray::{Array, Dim, Layout, Shape, Slice};
12use mdarray_linalg::{
13 lu::{InvError, LU},
14 utils::{into_i32, ipiv_to_perm_mat, transpose_in_place},
15};
16use num_complex::ComplexFloat;
17
18use super::{
19 scalar::{LapackScalar, Workspace},
20 simple::{getrf, getri, potrf},
21};
22use crate::Lapack;
23
24impl<T, D0: Dim, D1: Dim> LU<T, D0, D1> for Lapack
25where
26 T: ComplexFloat + Default + LapackScalar + Workspace,
27 T::Real: Into<T>,
28{
29 fn lu_write<L: Layout, Ll: Layout, Lu: Layout, Lp: Layout>(
30 &self,
31 a: &mut Slice<T, (D0, D1), L>,
32 l: &mut Slice<T, (D0, D0), Ll>,
33 u: &mut Slice<T, (D0, D1), Lu>,
34 p: &mut Slice<T, (D0, D0), Lp>,
35 ) {
36 let ash = *a.shape();
37 let m = ash.dim(0);
38
39 let ipiv = getrf(a, l, u);
40
41 let p_matrix = ipiv_to_perm_mat::<T, D0, D1>(&ipiv, m);
42
43 for i in 0..m {
44 for j in 0..m {
45 p[[i, j]] = p_matrix[[i, j]];
46 }
47 }
48 }
49
50 fn lu<L: Layout>(
51 &self,
52 a: &mut Slice<T, (D0, D1), L>,
53 ) -> (
54 Array<T, (D0, D0)>,
55 Array<T, (D0, D1)>,
56 Array<T, (D0, D0)>,
57 ) {
58 let ash = *a.shape();
59 let (m, n) = (ash.dim(0), ash.dim(1));
60
61 let min_mn = m.min(n);
62
63 let l_shape = <(D0, D0) as Shape>::from_dims(&[m, min_mn]);
64 let u_shape = <(D0, D1) as Shape>::from_dims(&[min_mn, n]);
65
66 let mut l = Array::from_elem(l_shape, T::default());
67 let mut u = Array::from_elem(u_shape, T::default());
68
69 let ipiv = getrf::<T, D0, D1, _, _, _>(a, &mut l, &mut u);
70
71 let p_matrix = ipiv_to_perm_mat::<T, D0, D0>(&ipiv, m);
72
73 (l, u, p_matrix)
74 }
75
76 fn inv_write<L: Layout>(&self, a: &mut Slice<T, (D0, D1), L>) -> Result<(), InvError> {
77 let ash = *a.shape();
78 let (m, n) = (ash.dim(0), ash.dim(1));
79
80 if m != n {
81 return Err(InvError::NotSquare {
82 rows: into_i32(m),
83 cols: into_i32(n),
84 });
85 }
86
87 let min_mn = m.min(n);
88
89 let l_shape = <(D0, D0) as Shape>::from_dims(&[m, min_mn]);
90 let u_shape = <(D0, D1) as Shape>::from_dims(&[min_mn, n]);
91
92 let mut l = Array::from_elem(l_shape, T::default());
93 let mut u = Array::from_elem(u_shape, T::default());
94 let mut ipiv = getrf::<T, D0, D1, _, _, _>(a, &mut l, &mut u);
95
96 match getri::<T, D0, D1, _>(a, &mut ipiv) {
97 0 => Ok(()),
98 i if i > 0 => Err(InvError::Singular { pivot: i }),
99 i => Err(InvError::BackendError(i)),
100 }
101 }
102
103 fn inv<L: Layout>(
104 &self,
105 a: &mut Slice<T, (D0, D1), L>,
106 ) -> Result<Array<T, (D0, D1)>, InvError> {
107 let ash = *a.shape();
108 let (m, n) = (ash.dim(0), ash.dim(1));
109
110 if m != n {
111 return Err(InvError::NotSquare {
112 rows: into_i32(m),
113 cols: into_i32(n),
114 });
115 }
116
117 let mut a_inv = Array::<T, (D0, D1)>::zeros(ash);
118
119 for i in 0..n {
122 for j in 0..m {
123 a_inv[[i, j]] = a[[i, j]];
124 }
125 }
126
127 let min_mn = m.min(n);
128
129 let l_shape = <(D0, D0) as Shape>::from_dims(&[m, min_mn]);
130 let u_shape = <(D0, D1) as Shape>::from_dims(&[min_mn, n]);
131
132 let mut l = Array::from_elem(l_shape, T::default());
133 let mut u = Array::from_elem(u_shape, T::default());
134 let mut ipiv = getrf::<T, D0, D1, _, _, _>(&mut a_inv, &mut l, &mut u);
135
136 match getri::<T, D0, D1, L>(a, &mut ipiv) {
137 0 => Ok(a.to_tensor()),
138 i if i > 0 => Err(InvError::Singular { pivot: i }),
139 i => Err(InvError::BackendError(i)),
140 }
141 }
142
143 fn det<L: Layout>(&self, a: &mut Slice<T, (D0, D1), L>) -> T {
144 let ash = *a.shape();
145 let (m, n) = (ash.dim(0), ash.dim(1));
146 assert_eq!(m, n, "determinant is only defined for square matrices");
147
148 let l_shape = <(D0, D0) as Shape>::from_dims(&[n, n]);
149 let u_shape = <(D0, D1) as Shape>::from_dims(&[n, n]);
150 let mut l = Array::from_elem(l_shape, T::default());
151 let mut u = Array::from_elem(u_shape, T::default());
152
153 let ipiv = getrf::<T, D0, D1, _, _, _>(a, &mut l, &mut u);
154
155 let mut det = T::one();
156 for i in 0..n {
157 det = det * u[[i, i]];
158 }
159
160 let mut sign = T::one();
161 for (i, &pivot) in ipiv.iter().enumerate() {
162 if (i as i32) != (pivot - 1) {
163 sign = sign * (-T::one());
164 }
165 }
166 det * sign
167 }
168
169 fn cholesky<L: Layout>(
171 &self,
172 a: &mut Slice<T, (D0, D1), L>,
173 ) -> Result<Array<T, (D0, D1)>, InvError> {
174 let ash = *a.shape();
175 let (m, n) = (ash.dim(0), ash.dim(1));
176 assert_eq!(m, n, "Matrix must be square for Cholesky decomposition");
177
178 let mut l = Array::<T, (D0, D1)>::zeros(ash);
179
180 match potrf::<T, D0, D1, _>(a, 'L') {
181 0 => {
182 for i in 0..m {
183 for j in 0..n {
184 if i >= j {
185 l[[i, j]] = a[[j, i]];
186 } else {
187 l[[i, j]] = T::zero();
188 }
189 }
190 }
191 Ok(l)
192 }
193 i if i > 0 => Err(InvError::NotPositiveDefinite { lpm: i }),
194 i => Err(InvError::BackendError(i)),
195 }
196 }
197
198 fn cholesky_write<L: Layout>(&self, a: &mut Slice<T, (D0, D1), L>) -> Result<(), InvError> {
200 let ash = *a.shape();
201 let (m, n) = (ash.dim(0), ash.dim(1));
202 assert_eq!(m, n, "Matrix must be square for Cholesky decomposition");
203
204 match potrf::<T, D0, D1, _>(a, 'L') {
205 0 => {
206 transpose_in_place(a);
207 Ok(())
208 }
209 i if i > 0 => Err(InvError::NotPositiveDefinite { lpm: i }),
210 i => Err(InvError::BackendError(i)),
211 }
212 }
213}