1use crate::error::{MatrixCIError, Result};
8use crate::matrixlu::{rrlu, rrlu_mut, RrLU, RrLUOptions};
9use crate::matrixluci::block_rook::LazyBlockRookKernel;
10use crate::matrixluci::factors::CrossFactors;
11use crate::matrixluci::source::LazyMatrixSource;
12use crate::matrixluci::types::{PivotKernelOptions, PivotSelectionCore};
13use crate::matrixluci::PivotKernel;
14use crate::scalar::Scalar;
15use crate::traits::AbstractMatrixCI;
16use tensor4all_tensorbackend::{mat_mul_owned, submatrix, triangular_solve_matrix_owned, Matrix};
17
18#[derive(Debug, Clone)]
48pub struct MatrixLUCI<T: Scalar + crate::MatrixLuciScalar> {
49 nrows: usize,
50 ncols: usize,
51 row_indices: Vec<usize>,
52 col_indices: Vec<usize>,
53 left: Matrix<T>,
54 right: Matrix<T>,
55 pivot_errors: Vec<f64>,
56}
57
58#[derive(Debug, Clone)]
86pub struct MatrixLuciFactors<T> {
87 pub row_indices: Vec<usize>,
89 pub col_indices: Vec<usize>,
91 pub pivot_errors: Vec<f64>,
93 pub rank: usize,
95 pub left: Matrix<T>,
97 pub right: Matrix<T>,
99}
100
101pub(crate) fn map_backend_error(err: crate::matrixluci::MatrixLuciError) -> MatrixCIError {
102 match err {
103 crate::matrixluci::MatrixLuciError::InvalidArgument { message } => {
104 MatrixCIError::InvalidArgument { message }
105 }
106 }
107}
108
109fn factors_to_public<T>(
110 selection: PivotSelectionCore,
111 factors: CrossFactors<T>,
112 left_orthogonal: bool,
113) -> Result<MatrixLuciFactors<T>>
114where
115 T: Scalar + crate::MatrixLuciScalar,
116{
117 let left = if left_orthogonal {
118 factors.cols_solve_pivot().map_err(map_backend_error)?
119 } else {
120 factors.pivot_cols.clone()
121 };
122 let right = if left_orthogonal {
123 factors.pivot_rows.clone()
124 } else {
125 factors.solve_pivot_rows().map_err(map_backend_error)?
126 };
127
128 Ok(MatrixLuciFactors {
129 row_indices: selection.row_indices,
130 col_indices: selection.col_indices,
131 pivot_errors: selection.pivot_errors,
132 rank: selection.rank,
133 left,
134 right,
135 })
136}
137
138fn backend_linalg_error(message: impl Into<String>) -> MatrixCIError {
139 MatrixCIError::InvalidArgument {
140 message: message.into(),
141 }
142}
143
144fn index_range(start: usize, end: usize) -> Vec<usize> {
145 (start..end).collect()
146}
147
148fn identity_rect<T: Scalar>(nrows: usize, ncols: usize) -> Matrix<T> {
149 let mut result = Matrix::zeros(nrows, ncols);
150 for i in 0..nrows.min(ncols) {
151 result[[i, i]] = T::one();
152 }
153 result
154}
155
156fn apply_row_permutation<T: Scalar>(matrix: &Matrix<T>, permutation: &[usize]) -> Matrix<T> {
157 let mut result = Matrix::zeros(matrix.nrows(), matrix.ncols());
158 for col in 0..matrix.ncols() {
159 for (new_row, &old_row) in permutation.iter().enumerate().take(matrix.nrows()) {
160 result[[old_row, col]] = matrix[[new_row, col]];
161 }
162 }
163 result
164}
165
166fn apply_col_permutation<T: Scalar>(matrix: &Matrix<T>, permutation: &[usize]) -> Matrix<T> {
167 let mut result = Matrix::zeros(matrix.nrows(), matrix.ncols());
168 for (new_col, &old_col) in permutation.iter().enumerate().take(matrix.ncols()) {
169 for row in 0..matrix.nrows() {
170 result[[row, old_col]] = matrix[[row, new_col]];
171 }
172 }
173 result
174}
175
176pub(crate) fn rrlu_colmatrix<T>(lu: &RrLU<T>) -> Result<Matrix<T>>
177where
178 T: Scalar + crate::MatrixLuciScalar,
179{
180 let rank = lu.npivots();
181 if rank == 0 {
182 return Ok(Matrix::zeros(lu.nrows(), 0));
183 }
184 let rows = index_range(0, rank);
185 let cols = index_range(0, rank);
186 let right_pivot_cols = submatrix(lu.right_unpermuted(), &rows, &cols);
187 mat_mul_owned(lu.left(true), right_pivot_cols)
188 .map_err(|err| backend_linalg_error(format!("MatrixLUCI colmatrix multiply failed: {err}")))
189}
190
191pub(crate) fn rrlu_rowmatrix<T>(lu: &RrLU<T>) -> Result<Matrix<T>>
192where
193 T: Scalar + crate::MatrixLuciScalar,
194{
195 let rank = lu.npivots();
196 if rank == 0 {
197 return Ok(Matrix::zeros(0, lu.ncols()));
198 }
199 let rows = index_range(0, rank);
200 let cols = index_range(0, rank);
201 let left_pivot_rows = submatrix(lu.left_unpermuted(), &rows, &cols);
202 mat_mul_owned(left_pivot_rows, lu.right(true))
203 .map_err(|err| backend_linalg_error(format!("MatrixLUCI rowmatrix multiply failed: {err}")))
204}
205
206pub(crate) fn rrlu_cols_times_pivot_solve<T>(lu: &RrLU<T>) -> Result<Matrix<T>>
207where
208 T: Scalar + crate::MatrixLuciScalar,
209{
210 let rank = lu.npivots();
211 let mut result = identity_rect(lu.nrows(), rank);
212 if rank > 0 && rank < lu.nrows() {
213 let pivot_rows = index_range(0, rank);
214 let pivot_cols = index_range(0, rank);
215 let rest_rows = index_range(rank, lu.nrows());
216 let pivot = submatrix(lu.left_unpermuted(), &pivot_rows, &pivot_cols);
217 let rest = submatrix(lu.left_unpermuted(), &rest_rows, &pivot_cols);
218 let solved = triangular_solve_matrix_owned(pivot, rest, false, true, false, false)
219 .map_err(|err| {
220 backend_linalg_error(format!("MatrixLUCI lower triangular solve failed: {err}"))
221 })?;
222 for row in 0..solved.nrows() {
223 for col in 0..solved.ncols() {
224 result[[rank + row, col]] = solved[[row, col]];
225 }
226 }
227 }
228 Ok(apply_row_permutation(&result, lu.row_permutation()))
229}
230
231pub(crate) fn rrlu_pivot_solve_times_rows<T>(lu: &RrLU<T>) -> Result<Matrix<T>>
232where
233 T: Scalar + crate::MatrixLuciScalar,
234{
235 let rank = lu.npivots();
236 let mut result = identity_rect(rank, lu.ncols());
237 if rank > 0 && rank < lu.ncols() {
238 let pivot_rows = index_range(0, rank);
239 let pivot_cols = index_range(0, rank);
240 let rest_cols = index_range(rank, lu.ncols());
241 let pivot = submatrix(lu.right_unpermuted(), &pivot_rows, &pivot_cols);
242 let rest = submatrix(lu.right_unpermuted(), &pivot_rows, &rest_cols);
243 let solved = triangular_solve_matrix_owned(pivot, rest, true, false, false, false)
244 .map_err(|err| {
245 backend_linalg_error(format!("MatrixLUCI upper triangular solve failed: {err}"))
246 })?;
247 for row in 0..solved.nrows() {
248 for col in 0..solved.ncols() {
249 result[[row, rank + col]] = solved[[row, col]];
250 }
251 }
252 }
253 Ok(apply_col_permutation(&result, lu.col_permutation()))
254}
255
256fn factors_from_rrlu<T>(lu: &RrLU<T>) -> Result<MatrixLuciFactors<T>>
257where
258 T: Scalar + crate::MatrixLuciScalar,
259{
260 let left = if lu.is_left_orthogonal() {
261 rrlu_cols_times_pivot_solve(lu)?
262 } else {
263 rrlu_colmatrix(lu)?
264 };
265 let right = if lu.is_left_orthogonal() {
266 rrlu_rowmatrix(lu)?
267 } else {
268 rrlu_pivot_solve_times_rows(lu)?
269 };
270
271 Ok(MatrixLuciFactors {
272 row_indices: lu.row_indices(),
273 col_indices: lu.col_indices(),
274 pivot_errors: lu.pivot_errors(),
275 rank: lu.npivots(),
276 left,
277 right,
278 })
279}
280
281pub(crate) fn dense_matrix_luci_factors_from_matrix<T>(
282 a: &Matrix<T>,
283 options: RrLUOptions,
284) -> Result<MatrixLuciFactors<T>>
285where
286 T: Scalar + crate::MatrixLuciScalar,
287{
288 let lu = rrlu(a, Some(options))?;
289 factors_from_rrlu(&lu)
290}
291
292pub(crate) fn dense_matrix_luci_factors_from_matrix_owned<T>(
293 mut a: Matrix<T>,
294 options: RrLUOptions,
295) -> Result<MatrixLuciFactors<T>>
296where
297 T: Scalar + crate::MatrixLuciScalar,
298{
299 let lu = rrlu_mut(&mut a, Some(options))?;
300 factors_from_rrlu(&lu)
301}
302
303pub(crate) fn lazy_matrix_luci_factors_from_blocks<T, F>(
304 nrows: usize,
305 ncols: usize,
306 fill_block: F,
307 options: RrLUOptions,
308) -> Result<MatrixLuciFactors<T>>
309where
310 T: Scalar + crate::MatrixLuciScalar,
311 F: Fn(&[usize], &[usize], &mut [T]),
312 LazyBlockRookKernel: PivotKernel<T>,
313{
314 let source = LazyMatrixSource::new(nrows, ncols, fill_block);
315 let kernel_options = PivotKernelOptions {
316 max_bond_dim: options.max_bond_dim,
317 rel_tol: options.rel_tol,
318 abs_tol: options.abs_tol,
319 left_orthogonal: options.left_orthogonal,
320 };
321
322 let selection = LazyBlockRookKernel
323 .factorize(&source, &kernel_options)
324 .map_err(map_backend_error)?;
325 let factors = CrossFactors::from_source(&source, &selection).map_err(map_backend_error)?;
326 factors_to_public(selection, factors, options.left_orthogonal)
327}
328
329pub fn matrix_luci_factors_from_matrix<T>(
367 a: &Matrix<T>,
368 options: Option<RrLUOptions>,
369) -> Result<MatrixLuciFactors<T>>
370where
371 T: Scalar + crate::MatrixLuciScalar,
372{
373 <T as crate::MatrixLuciScalar>::matrix_luci_factors_from_matrix(a, options.unwrap_or_default())
374}
375
376pub fn matrix_luci_factors_from_matrix_owned<T>(
386 a: Matrix<T>,
387 options: Option<RrLUOptions>,
388) -> Result<MatrixLuciFactors<T>>
389where
390 T: Scalar + crate::MatrixLuciScalar,
391{
392 dense_matrix_luci_factors_from_matrix_owned(a, options.unwrap_or_default())
393}
394
395pub fn matrix_luci_factors_from_blocks<T, F>(
441 nrows: usize,
442 ncols: usize,
443 fill_block: F,
444 options: RrLUOptions,
445) -> Result<MatrixLuciFactors<T>>
446where
447 T: Scalar + crate::MatrixLuciScalar,
448 F: Fn(&[usize], &[usize], &mut [T]),
449{
450 <T as crate::MatrixLuciScalar>::matrix_luci_factors_from_blocks(
451 nrows, ncols, fill_block, options,
452 )
453}
454
455impl<T> MatrixLUCI<T>
456where
457 T: Scalar + crate::MatrixLuciScalar,
458{
459 pub fn from_matrix(a: &Matrix<T>, options: Option<RrLUOptions>) -> Result<Self> {
480 let factors = matrix_luci_factors_from_matrix(a, options)?;
481
482 Ok(Self {
483 nrows: a.nrows(),
484 ncols: a.ncols(),
485 row_indices: factors.row_indices,
486 col_indices: factors.col_indices,
487 left: factors.left,
488 right: factors.right,
489 pivot_errors: factors.pivot_errors,
490 })
491 }
492
493 pub fn left(&self) -> Matrix<T> {
516 self.left.clone()
517 }
518
519 pub fn right(&self) -> Matrix<T> {
543 self.right.clone()
544 }
545
546 pub fn pivot_errors(&self) -> Vec<f64> {
564 self.pivot_errors.clone()
565 }
566
567 pub fn last_pivot_error(&self) -> f64 {
581 self.pivot_errors.last().copied().unwrap_or(0.0)
582 }
583}
584
585impl<T> AbstractMatrixCI<T> for MatrixLUCI<T>
586where
587 T: Scalar + crate::MatrixLuciScalar,
588{
589 fn nrows(&self) -> usize {
590 self.nrows
591 }
592
593 fn ncols(&self) -> usize {
594 self.ncols
595 }
596
597 fn rank(&self) -> usize {
598 self.row_indices.len()
599 }
600
601 fn row_indices(&self) -> &[usize] {
602 &self.row_indices
603 }
604
605 fn col_indices(&self) -> &[usize] {
606 &self.col_indices
607 }
608
609 fn evaluate(&self, i: usize, j: usize) -> T {
610 let mut sum = T::zero();
611 for k in 0..self.rank() {
612 sum = sum + self.left[[i, k]] * self.right[[k, j]];
613 }
614 sum
615 }
616
617 fn submatrix(&self, rows: &[usize], cols: &[usize]) -> Matrix<T> {
618 let r = self.rank();
619 let mut result = Matrix::zeros(rows.len(), cols.len());
620 for (j_out, &col) in cols.iter().enumerate() {
621 for (i_out, &row) in rows.iter().enumerate() {
622 let mut sum = T::zero();
623 for k in 0..r {
624 sum = sum + self.left[[row, k]] * self.right[[k, col]];
625 }
626 result[[i_out, j_out]] = sum;
627 }
628 }
629 result
630 }
631}
632
633#[cfg(test)]
634mod tests;