1use anyhow::{anyhow, Result};
7use num_complex::{Complex32, Complex64, ComplexFloat};
8use tenferro::{DType, Tensor, TensorScalar, TensorSessionOpsExt, TypedTensor};
9use tenferro_linalg::TensorLinalgExt;
10
11use crate::context::{with_default_session, CpuExecutionContext};
12use crate::matrix::Matrix;
13
14#[derive(Debug)]
31pub struct SvdResult<T: TensorScalar> {
32 u: TypedTensor<T>,
33 s: TypedTensor<T::Real>,
34 vt: TypedTensor<T>,
35}
36
37#[derive(Debug)]
42pub struct FullPivLuResult<T: TensorScalar> {
43 p: TypedTensor<T>,
44 l: TypedTensor<T>,
45 u: TypedTensor<T>,
46 q: TypedTensor<T>,
47}
48
49fn clone_linalg_tensor<T: TensorScalar>(tensor: &TypedTensor<T>) -> TypedTensor<T> {
50 tensor.duplicate().unwrap_or_else(|error| {
51 crate::invariant_violated("linalg result duplication failed", error)
52 })
53}
54
55impl<T: TensorScalar> SvdResult<T> {
56 pub fn u(&self) -> &TypedTensor<T> {
58 &self.u
59 }
60
61 pub fn s(&self) -> &TypedTensor<T::Real> {
63 &self.s
64 }
65
66 pub fn vt(&self) -> &TypedTensor<T> {
68 &self.vt
69 }
70
71 pub fn into_parts(self) -> (TypedTensor<T>, TypedTensor<T::Real>, TypedTensor<T>) {
73 (self.u, self.s, self.vt)
74 }
75}
76
77impl<T: TensorScalar> FullPivLuResult<T> {
78 pub fn p(&self) -> &TypedTensor<T> {
80 &self.p
81 }
82
83 pub fn l(&self) -> &TypedTensor<T> {
85 &self.l
86 }
87
88 pub fn u(&self) -> &TypedTensor<T> {
90 &self.u
91 }
92
93 pub fn q(&self) -> &TypedTensor<T> {
95 &self.q
96 }
97
98 pub fn into_parts(
100 self,
101 ) -> (
102 TypedTensor<T>,
103 TypedTensor<T>,
104 TypedTensor<T>,
105 TypedTensor<T>,
106 ) {
107 (self.p, self.l, self.u, self.q)
108 }
109}
110
111impl<T: TensorScalar> Clone for SvdResult<T> {
112 fn clone(&self) -> Self {
113 Self {
114 u: clone_linalg_tensor(&self.u),
115 s: clone_linalg_tensor(&self.s),
116 vt: clone_linalg_tensor(&self.vt),
117 }
118 }
119}
120
121impl<T: TensorScalar> Clone for FullPivLuResult<T> {
122 fn clone(&self) -> Self {
123 Self {
124 p: clone_linalg_tensor(&self.p),
125 l: clone_linalg_tensor(&self.l),
126 u: clone_linalg_tensor(&self.u),
127 q: clone_linalg_tensor(&self.q),
128 }
129 }
130}
131
132#[derive(Debug, Clone)]
145pub struct FullPivLuMatrixResult<T> {
146 pub p: Matrix<T>,
148 pub l: Matrix<T>,
150 pub u: Matrix<T>,
152 pub q: Matrix<T>,
154}
155
156pub trait BackendLinalgScalar: TensorScalar {}
158
159pub trait FullPivLuScalar: BackendLinalgScalar {
174 fn solve_right_full_piv_lu(
183 lhs_values: &[Self],
184 lhs_rows: usize,
185 lhs_cols: usize,
186 pivot_values: &[Self],
187 pivot_rows: usize,
188 pivot_cols: usize,
189 ) -> std::result::Result<Vec<Self>, BackendLinalgError>;
190
191 fn solve_right_full_piv_lu_in(
210 context: &CpuExecutionContext,
211 lhs_values: &[Self],
212 lhs_rows: usize,
213 lhs_cols: usize,
214 pivot_values: &[Self],
215 pivot_rows: usize,
216 pivot_cols: usize,
217 ) -> std::result::Result<Vec<Self>, BackendLinalgError>;
218
219 fn solve_right_full_piv_lu_on_session(
233 session: &mut dyn tenferro_tensor::Engine,
234 lhs_values: &[Self],
235 lhs_rows: usize,
236 lhs_cols: usize,
237 pivot_values: &[Self],
238 pivot_rows: usize,
239 pivot_cols: usize,
240 ) -> std::result::Result<Vec<Self>, BackendLinalgError>;
241}
242
243macro_rules! impl_full_piv_lu_scalar {
244 ($t:ty) => {
245 impl FullPivLuScalar for $t {
246 fn solve_right_full_piv_lu(
247 lhs_values: &[Self],
248 lhs_rows: usize,
249 lhs_cols: usize,
250 pivot_values: &[Self],
251 pivot_rows: usize,
252 pivot_cols: usize,
253 ) -> std::result::Result<Vec<Self>, BackendLinalgError> {
254 let inputs = FullPivLuSolveInputs::new(
255 lhs_values,
256 lhs_rows,
257 lhs_cols,
258 pivot_values,
259 pivot_rows,
260 pivot_cols,
261 )?;
262 let solved_t =
263 with_default_session(|session| inputs.solve(session)).map_err(|e| {
264 BackendLinalgError::from(anyhow::anyhow!("full_piv_lu_solve failed: {e}"))
265 })?;
266 inputs.finish::<Self>(solved_t)
267 }
268
269 fn solve_right_full_piv_lu_in(
270 context: &CpuExecutionContext,
271 lhs_values: &[Self],
272 lhs_rows: usize,
273 lhs_cols: usize,
274 pivot_values: &[Self],
275 pivot_rows: usize,
276 pivot_cols: usize,
277 ) -> std::result::Result<Vec<Self>, BackendLinalgError> {
278 let inputs = FullPivLuSolveInputs::new(
279 lhs_values,
280 lhs_rows,
281 lhs_cols,
282 pivot_values,
283 pivot_rows,
284 pivot_cols,
285 )?;
286 let solved_t = context
292 .with_concrete_session(|session| inputs.solve(session.engine()))
293 .map_err(|error| BackendLinalgError::from(anyhow::Error::new(error)))?
294 .map_err(|error| {
295 BackendLinalgError::from(anyhow::anyhow!(
296 "full_piv_lu_solve failed: {error}"
297 ))
298 })?;
299 inputs.finish::<Self>(solved_t)
300 }
301
302 fn solve_right_full_piv_lu_on_session(
303 session: &mut dyn tenferro_tensor::Engine,
304 lhs_values: &[Self],
305 lhs_rows: usize,
306 lhs_cols: usize,
307 pivot_values: &[Self],
308 pivot_rows: usize,
309 pivot_cols: usize,
310 ) -> std::result::Result<Vec<Self>, BackendLinalgError> {
311 let inputs = FullPivLuSolveInputs::new(
312 lhs_values,
313 lhs_rows,
314 lhs_cols,
315 pivot_values,
316 pivot_rows,
317 pivot_cols,
318 )?;
319 let solved_t = inputs.solve(session).map_err(|error| {
320 BackendLinalgError::from(anyhow::anyhow!("full_piv_lu_solve failed: {error}"))
321 })?;
322 inputs.finish::<Self>(solved_t)
323 }
324 }
325 };
326}
327
328struct FullPivLuSolveInputs {
333 pivot_tensor: tenferro_tensor::Tensor,
334 lhs_tensor: tenferro_tensor::Tensor,
335 lhs_cols: usize,
336 lhs_rows: usize,
337}
338
339impl FullPivLuSolveInputs {
340 fn new<T: BackendLinalgScalar + num_traits::Zero>(
341 lhs_values: &[T],
342 lhs_rows: usize,
343 lhs_cols: usize,
344 pivot_values: &[T],
345 pivot_rows: usize,
346 pivot_cols: usize,
347 ) -> std::result::Result<Self, BackendLinalgError> {
348 if pivot_rows != pivot_cols {
349 return Err(BackendLinalgError::from(anyhow::anyhow!(
350 "full-pivot solve requires a square pivot matrix, got {}x{}",
351 pivot_rows,
352 pivot_cols
353 )));
354 }
355 if lhs_cols != pivot_rows {
356 return Err(BackendLinalgError::from(anyhow::anyhow!(
357 "cannot solve T * P = Pi1 with Pi1 shape {}x{} and P shape {}x{}",
358 lhs_rows,
359 lhs_cols,
360 pivot_rows,
361 pivot_cols
362 )));
363 }
364
365 let lhs_t = transpose_column_major(lhs_values, lhs_rows, lhs_cols);
366 let pivot_t = transpose_column_major(pivot_values, pivot_rows, pivot_cols);
367 let pivot_tensor =
368 tenferro_tensor::Tensor::from_vec_col_major(vec![pivot_cols, pivot_rows], pivot_t)
369 .map_err(|e| BackendLinalgError::from(anyhow::Error::new(e)))?;
370 let lhs_tensor =
371 tenferro_tensor::Tensor::from_vec_col_major(vec![lhs_cols, lhs_rows], lhs_t)
372 .map_err(|e| BackendLinalgError::from(anyhow::Error::new(e)))?;
373 Ok(Self {
374 pivot_tensor,
375 lhs_tensor,
376 lhs_cols,
377 lhs_rows,
378 })
379 }
380
381 fn solve(
382 &self,
383 session: &mut dyn tenferro_tensor::Engine,
384 ) -> tenferro_tensor::Result<tenferro_tensor::Tensor> {
385 self.pivot_tensor
386 .full_piv_lu_solve(&self.lhs_tensor, session)
387 }
388
389 fn finish<T: BackendLinalgScalar + num_traits::Zero>(
390 &self,
391 solved_t: tenferro_tensor::Tensor,
392 ) -> std::result::Result<Vec<T>, BackendLinalgError> {
393 let solved_t_values = solved_t.as_slice::<T>().map_err(|e| {
394 BackendLinalgError::from(anyhow::anyhow!(
395 "full_piv_lu_solve returned unexpected dtype: {e}"
396 ))
397 })?;
398 Ok(transpose_column_major(
399 solved_t_values,
400 self.lhs_cols,
401 self.lhs_rows,
402 ))
403 }
404}
405
406impl_full_piv_lu_scalar!(f32);
407impl_full_piv_lu_scalar!(f64);
408impl_full_piv_lu_scalar!(num_complex::Complex32);
409impl_full_piv_lu_scalar!(num_complex::Complex64);
410
411fn transpose_column_major<T: Copy + num_traits::Zero>(
413 values: &[T],
414 nrows: usize,
415 ncols: usize,
416) -> Vec<T> {
417 let mut out = vec![T::zero(); nrows * ncols];
418 for col in 0..ncols {
419 for row in 0..nrows {
420 out[col + ncols * row] = values[row + nrows * col];
421 }
422 }
423 out
424}
425
426impl<T: TensorScalar> BackendLinalgScalar for T {}
427
428pub trait MatrixSolveScalar: BackendLinalgScalar + crate::matrix::MatrixScalar {
442 #[doc(hidden)]
443 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>>;
444
445 #[doc(hidden)]
446 fn solve_matrix_owned_impl(a: Matrix<Self>, b: Matrix<Self>) -> Result<Matrix<Self>> {
447 Self::solve_matrix_impl(&a, &b)
448 }
449
450 #[doc(hidden)]
462 fn solve_matrix_in_impl(
463 session: &mut dyn tenferro_tensor::Engine,
464 a: &Matrix<Self>,
465 b: &Matrix<Self>,
466 ) -> Result<Matrix<Self>>;
467}
468
469#[derive(Debug, thiserror::Error)]
481#[error("backend linear algebra failed: {source}")]
482pub struct BackendLinalgError {
483 #[source]
485 pub source: anyhow::Error,
486}
487
488impl From<anyhow::Error> for BackendLinalgError {
489 fn from(source: anyhow::Error) -> Self {
490 Self { source }
491 }
492}
493
494pub trait MatrixTriangularSolveScalar: BackendLinalgScalar + crate::matrix::MatrixScalar {
509 #[doc(hidden)]
510 fn triangular_solve_matrix_impl(
511 a: &Matrix<Self>,
512 b: &Matrix<Self>,
513 left_side: bool,
514 lower: bool,
515 transpose_a: bool,
516 unit_diagonal: bool,
517 ) -> Result<Matrix<Self>>;
518
519 #[doc(hidden)]
520 fn triangular_solve_matrix_owned_impl(
521 a: Matrix<Self>,
522 b: Matrix<Self>,
523 left_side: bool,
524 lower: bool,
525 transpose_a: bool,
526 unit_diagonal: bool,
527 ) -> Result<Matrix<Self>> {
528 Self::triangular_solve_matrix_impl(&a, &b, left_side, lower, transpose_a, unit_diagonal)
529 }
530}
531
532#[derive(Debug, Clone, Copy, PartialEq)]
550pub struct SrcErrorEstimate {
551 pub error: f64,
553 pub norm: f64,
555}
556
557pub fn src_error_estimate<T>(
589 r: &Matrix<T>,
590) -> std::result::Result<SrcErrorEstimate, BackendLinalgError>
591where
592 T: MatrixTriangularSolveScalar + ComplexFloat,
593{
594 let inverse_adjoint = src_inverse_adjoint(r)?;
595 src_error_estimate_from_inverse_adjoint(r, &inverse_adjoint)
596}
597
598pub fn src_error_estimate_general<T>(
620 factor: &Matrix<T>,
621) -> std::result::Result<SrcErrorEstimate, BackendLinalgError>
622where
623 T: MatrixSolveScalar + ComplexFloat,
624{
625 let nrows = factor.nrows();
626 let ncols = factor.ncols();
627 if nrows != ncols {
628 return Err(BackendLinalgError::from(anyhow!(
629 "SRC estimator requires a square factor, got {nrows}x{ncols}"
630 )));
631 }
632 if nrows == 0 {
633 return Err(BackendLinalgError::from(anyhow!(
634 "SRC estimator requires a non-empty factor"
635 )));
636 }
637 if factor
638 .as_col_major_slice()
639 .iter()
640 .any(|value| !value.matrix_abs_sq().is_finite())
641 {
642 return Err(BackendLinalgError::from(anyhow!(
643 "SRC estimator requires finite entries in its factor"
644 )));
645 }
646 let mut adjoint = Matrix::zeros(nrows, ncols);
647 let mut identity = Matrix::zeros(nrows, ncols);
648 for col in 0..ncols {
649 for row in 0..nrows {
650 adjoint[[row, col]] = factor[[col, row]].conj();
651 }
652 identity[[col, col]] = T::one();
653 }
654 let inverse_adjoint = solve_matrix(&adjoint, &identity).map_err(|error| {
655 BackendLinalgError::from(anyhow!("SRC inverse-adjoint general solve failed: {error}"))
656 })?;
657 src_error_estimate_from_inverse_adjoint(factor, &inverse_adjoint)
658}
659
660pub(crate) fn src_inverse_adjoint<T>(
667 r: &Matrix<T>,
668) -> std::result::Result<Matrix<T>, BackendLinalgError>
669where
670 T: MatrixTriangularSolveScalar + ComplexFloat,
671{
672 let nrows = r.nrows();
673 let ncols = r.ncols();
674 if nrows != ncols {
675 return Err(BackendLinalgError::from(anyhow!(
676 "SRC estimator requires a square R, got {nrows}x{ncols}"
677 )));
678 }
679 if nrows == 0 {
680 return Err(BackendLinalgError::from(anyhow!(
681 "SRC estimator requires a non-empty R"
682 )));
683 }
684
685 for col in 0..ncols {
692 let diagonal_sq = r[[col, col]].matrix_abs_sq();
693 if !diagonal_sq.is_finite() || diagonal_sq == 0.0 {
694 return Err(BackendLinalgError::from(anyhow!(
695 "SRC estimator requires a finite, nonzero diagonal in R at ({col}, {col})"
696 )));
697 }
698 for row in col + 1..nrows {
699 if r[[row, col]].matrix_abs_sq() != 0.0 {
700 return Err(BackendLinalgError::from(anyhow!(
701 "SRC triangular estimator requires upper-triangular R; entry ({row}, {col}) is nonzero"
702 )));
703 }
704 }
705 }
706
707 let mut adjoint = Matrix::zeros(nrows, ncols);
708 for col in 0..ncols {
709 for row in 0..nrows {
710 adjoint[[row, col]] = r[[col, row]].conj();
711 }
712 }
713 let mut identity = Matrix::zeros(nrows, ncols);
714 for diagonal in 0..nrows {
715 identity[[diagonal, diagonal]] = T::one();
716 }
717
718 let inverse_adjoint = triangular_solve_matrix(&adjoint, &identity, true, true, false, false)
719 .map_err(|error| {
720 BackendLinalgError::from(anyhow!(
721 "SRC inverse-adjoint triangular solve failed: {error}"
722 ))
723 })?;
724 Ok(inverse_adjoint)
725}
726
727pub(crate) fn src_error_estimate_from_inverse_adjoint<T>(
733 r: &Matrix<T>,
734 inverse_adjoint: &Matrix<T>,
735) -> std::result::Result<SrcErrorEstimate, BackendLinalgError>
736where
737 T: crate::matrix::MatrixScalar + ComplexFloat,
738{
739 let nrows = r.nrows();
740 let ncols = r.ncols();
741 if nrows != ncols || inverse_adjoint.nrows() != nrows || inverse_adjoint.ncols() != ncols {
742 return Err(BackendLinalgError::from(anyhow!(
743 "SRC estimator requires matching square R and inverse-adjoint factors"
744 )));
745 }
746 if nrows == 0 {
747 return Err(BackendLinalgError::from(anyhow!(
748 "SRC estimator requires a non-empty R"
749 )));
750 }
751
752 let mut norm_sq = 0.0_f64;
753 for value in r.as_col_major_slice() {
754 norm_sq += value.matrix_abs_sq();
755 }
756 if !norm_sq.is_finite() {
757 return Err(BackendLinalgError::from(anyhow!(
758 "SRC estimator requires finite entries in R"
759 )));
760 }
761 let mut inverse_column_error_sq = 0.0_f64;
762 for col in 0..ncols {
763 let column_norm_sq = (0..nrows)
764 .map(|row| inverse_adjoint[[row, col]].matrix_abs_sq())
765 .sum::<f64>();
766 if !column_norm_sq.is_finite() || column_norm_sq == 0.0 {
767 return Err(BackendLinalgError::from(anyhow!(
768 "SRC inverse-adjoint solve returned an invalid column norm at column {col}"
769 )));
770 }
771 inverse_column_error_sq += 1.0 / column_norm_sq;
772 }
773
774 let sketch_width = ncols as f64;
775 let error_sq = inverse_column_error_sq / sketch_width;
776 let norm_estimate_sq = norm_sq / sketch_width;
777 if !error_sq.is_finite() || !norm_estimate_sq.is_finite() {
778 return Err(BackendLinalgError::from(anyhow!(
779 "SRC estimator produced a non-finite estimate"
780 )));
781 }
782 Ok(SrcErrorEstimate {
783 error: error_sq.sqrt(),
784 norm: norm_estimate_sq.sqrt(),
785 })
786}
787
788fn solve_matrix_direct<T>(a: &Matrix<T>, b: &Matrix<T>) -> Result<Matrix<T>>
789where
790 T: BackendLinalgScalar + Copy,
791 Tensor: From<TypedTensor<T>>,
792{
793 with_default_session(|session| Ok(solve_matrix_direct_in(session, a, b)))
794 .map_err(|e| anyhow!("linear solve failed via tenferro-tensor: {e}"))?
795}
796
797fn solve_matrix_direct_in<T>(
798 session: &mut dyn tenferro_tensor::Engine,
799 a: &Matrix<T>,
800 b: &Matrix<T>,
801) -> Result<Matrix<T>>
802where
803 T: BackendLinalgScalar + Copy,
804 Tensor: From<TypedTensor<T>>,
805{
806 solve_matrix_direct_owned_in(session, a.clone(), b.clone())
807}
808
809fn solve_matrix_direct_owned<T>(a: Matrix<T>, b: Matrix<T>) -> Result<Matrix<T>>
810where
811 T: BackendLinalgScalar + Copy,
812 Tensor: From<TypedTensor<T>>,
813{
814 with_default_session(|session| Ok(solve_matrix_direct_owned_in(session, a, b)))
815 .map_err(|e| anyhow!("linear solve failed via tenferro-tensor: {e}"))?
816}
817
818fn solve_matrix_direct_owned_in<T>(
819 session: &mut dyn tenferro_tensor::Engine,
820 a: Matrix<T>,
821 b: Matrix<T>,
822) -> Result<Matrix<T>>
823where
824 T: BackendLinalgScalar + Copy,
825 Tensor: From<TypedTensor<T>>,
826{
827 use tenferro_linalg::TensorLinalgExt;
828
829 let a_tensor: Tensor = a.into_typed_tensor().into();
830 let b_tensor: Tensor = b.into_typed_tensor().into();
831 let result = a_tensor
832 .solve(&b_tensor, session)
833 .map_err(|e| anyhow!("linear solve failed via tenferro-tensor: {e}"))?;
834 let x = try_into_typed_result::<T>("solve", result)?;
835 typed_tensor_to_matrix("solve", x)
836}
837
838pub fn solve_matrix_in<T>(
847 session: &mut dyn tenferro_tensor::Engine,
848 a: &Matrix<T>,
849 b: &Matrix<T>,
850) -> std::result::Result<Matrix<T>, BackendLinalgError>
851where
852 T: BackendLinalgScalar + Copy,
853 Tensor: From<TypedTensor<T>>,
854{
855 solve_matrix_direct_in(session, a, b).map_err(BackendLinalgError::from)
856}
857
858fn triangular_solve_matrix_direct<T>(
859 a: &Matrix<T>,
860 b: &Matrix<T>,
861 left_side: bool,
862 lower: bool,
863 transpose_a: bool,
864 unit_diagonal: bool,
865) -> Result<Matrix<T>>
866where
867 T: BackendLinalgScalar + Copy,
868 Tensor: From<TypedTensor<T>>,
869{
870 with_default_session(|session| {
871 Ok(triangular_solve_matrix_direct_in(
872 session,
873 a,
874 b,
875 left_side,
876 lower,
877 transpose_a,
878 unit_diagonal,
879 ))
880 })
881 .map_err(|e| anyhow!("triangular solve failed via tenferro-tensor: {e}"))?
882}
883
884#[allow(clippy::too_many_arguments)]
885fn triangular_solve_matrix_direct_in<T>(
886 session: &mut dyn tenferro_tensor::Engine,
887 a: &Matrix<T>,
888 b: &Matrix<T>,
889 left_side: bool,
890 lower: bool,
891 transpose_a: bool,
892 unit_diagonal: bool,
893) -> Result<Matrix<T>>
894where
895 T: BackendLinalgScalar + Copy,
896 Tensor: From<TypedTensor<T>>,
897{
898 triangular_solve_matrix_direct_owned_in(
899 session,
900 a.clone(),
901 b.clone(),
902 left_side,
903 lower,
904 transpose_a,
905 unit_diagonal,
906 )
907}
908
909fn triangular_solve_matrix_direct_owned<T>(
910 a: Matrix<T>,
911 b: Matrix<T>,
912 left_side: bool,
913 lower: bool,
914 transpose_a: bool,
915 unit_diagonal: bool,
916) -> Result<Matrix<T>>
917where
918 T: BackendLinalgScalar + Copy,
919 Tensor: From<TypedTensor<T>>,
920{
921 with_default_session(|session| {
922 Ok(triangular_solve_matrix_direct_owned_in(
923 session,
924 a,
925 b,
926 left_side,
927 lower,
928 transpose_a,
929 unit_diagonal,
930 ))
931 })
932 .map_err(|e| anyhow!("triangular solve failed via tenferro-tensor: {e}"))?
933}
934
935#[allow(clippy::too_many_arguments)]
936fn triangular_solve_matrix_direct_owned_in<T>(
937 session: &mut dyn tenferro_tensor::Engine,
938 a: Matrix<T>,
939 b: Matrix<T>,
940 left_side: bool,
941 lower: bool,
942 transpose_a: bool,
943 unit_diagonal: bool,
944) -> Result<Matrix<T>>
945where
946 T: BackendLinalgScalar + Copy,
947 Tensor: From<TypedTensor<T>>,
948{
949 use tenferro_linalg::TensorLinalgExt;
950
951 let a_tensor: Tensor = a.into_typed_tensor().into();
952 let b_tensor: Tensor = b.into_typed_tensor().into();
953 let result = a_tensor
954 .triangular_solve(
955 &b_tensor,
956 left_side,
957 lower,
958 transpose_a,
959 unit_diagonal,
960 session,
961 )
962 .map_err(|e| anyhow!("triangular solve failed via tenferro-tensor: {e}"))?;
963 let x = try_into_typed_result::<T>("triangular_solve", result)?;
964 typed_tensor_to_matrix("triangular_solve", x)
965}
966
967#[allow(clippy::too_many_arguments)]
977pub fn triangular_solve_matrix_in<T>(
978 session: &mut dyn tenferro_tensor::Engine,
979 a: &Matrix<T>,
980 b: &Matrix<T>,
981 left_side: bool,
982 lower: bool,
983 transpose_a: bool,
984 unit_diagonal: bool,
985) -> std::result::Result<Matrix<T>, BackendLinalgError>
986where
987 T: BackendLinalgScalar + Copy,
988 Tensor: From<TypedTensor<T>>,
989{
990 triangular_solve_matrix_direct_in(session, a, b, left_side, lower, transpose_a, unit_diagonal)
991 .map_err(BackendLinalgError::from)
992}
993
994impl MatrixSolveScalar for f64 {
995 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
996 solve_matrix_direct(a, b)
997 }
998
999 fn solve_matrix_in_impl(
1000 session: &mut dyn tenferro_tensor::Engine,
1001 a: &Matrix<Self>,
1002 b: &Matrix<Self>,
1003 ) -> Result<Matrix<Self>> {
1004 solve_matrix_direct_in(session, a, b)
1005 }
1006
1007 fn solve_matrix_owned_impl(a: Matrix<Self>, b: Matrix<Self>) -> Result<Matrix<Self>> {
1008 solve_matrix_direct_owned(a, b)
1009 }
1010}
1011
1012impl MatrixTriangularSolveScalar for f64 {
1013 fn triangular_solve_matrix_impl(
1014 a: &Matrix<Self>,
1015 b: &Matrix<Self>,
1016 left_side: bool,
1017 lower: bool,
1018 transpose_a: bool,
1019 unit_diagonal: bool,
1020 ) -> Result<Matrix<Self>> {
1021 triangular_solve_matrix_direct(a, b, left_side, lower, transpose_a, unit_diagonal)
1022 }
1023
1024 fn triangular_solve_matrix_owned_impl(
1025 a: Matrix<Self>,
1026 b: Matrix<Self>,
1027 left_side: bool,
1028 lower: bool,
1029 transpose_a: bool,
1030 unit_diagonal: bool,
1031 ) -> Result<Matrix<Self>> {
1032 triangular_solve_matrix_direct_owned(a, b, left_side, lower, transpose_a, unit_diagonal)
1033 }
1034}
1035
1036impl MatrixSolveScalar for Complex64 {
1037 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
1038 solve_matrix_direct(a, b)
1039 }
1040
1041 fn solve_matrix_in_impl(
1042 session: &mut dyn tenferro_tensor::Engine,
1043 a: &Matrix<Self>,
1044 b: &Matrix<Self>,
1045 ) -> Result<Matrix<Self>> {
1046 solve_matrix_direct_in(session, a, b)
1047 }
1048
1049 fn solve_matrix_owned_impl(a: Matrix<Self>, b: Matrix<Self>) -> Result<Matrix<Self>> {
1050 solve_matrix_direct_owned(a, b)
1051 }
1052}
1053
1054impl MatrixTriangularSolveScalar for Complex64 {
1055 fn triangular_solve_matrix_impl(
1056 a: &Matrix<Self>,
1057 b: &Matrix<Self>,
1058 left_side: bool,
1059 lower: bool,
1060 transpose_a: bool,
1061 unit_diagonal: bool,
1062 ) -> Result<Matrix<Self>> {
1063 triangular_solve_matrix_direct(a, b, left_side, lower, transpose_a, unit_diagonal)
1064 }
1065
1066 fn triangular_solve_matrix_owned_impl(
1067 a: Matrix<Self>,
1068 b: Matrix<Self>,
1069 left_side: bool,
1070 lower: bool,
1071 transpose_a: bool,
1072 unit_diagonal: bool,
1073 ) -> Result<Matrix<Self>> {
1074 triangular_solve_matrix_direct_owned(a, b, left_side, lower, transpose_a, unit_diagonal)
1075 }
1076}
1077
1078impl MatrixSolveScalar for f32 {
1079 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
1080 let a64 = Matrix::from_col_major_vec(
1081 a.nrows(),
1082 a.ncols(),
1083 a.as_col_major_slice()
1084 .iter()
1085 .map(|&value| value as f64)
1086 .collect(),
1087 );
1088 let b64 = Matrix::from_col_major_vec(
1089 b.nrows(),
1090 b.ncols(),
1091 b.as_col_major_slice()
1092 .iter()
1093 .map(|&value| value as f64)
1094 .collect(),
1095 );
1096 let x64 = solve_matrix_direct(&a64, &b64)?;
1097 Ok(Matrix::from_col_major_vec(
1098 x64.nrows(),
1099 x64.ncols(),
1100 x64.as_col_major_slice()
1101 .iter()
1102 .map(|&value| value as f32)
1103 .collect(),
1104 ))
1105 }
1106
1107 fn solve_matrix_in_impl(
1108 session: &mut dyn tenferro_tensor::Engine,
1109 a: &Matrix<Self>,
1110 b: &Matrix<Self>,
1111 ) -> Result<Matrix<Self>> {
1112 let a64 = Matrix::from_col_major_vec(
1115 a.nrows(),
1116 a.ncols(),
1117 a.as_col_major_slice()
1118 .iter()
1119 .map(|&value| value as f64)
1120 .collect(),
1121 );
1122 let b64 = Matrix::from_col_major_vec(
1123 b.nrows(),
1124 b.ncols(),
1125 b.as_col_major_slice()
1126 .iter()
1127 .map(|&value| value as f64)
1128 .collect(),
1129 );
1130 let x64 = solve_matrix_direct_in(session, &a64, &b64)?;
1131 Ok(Matrix::from_col_major_vec(
1132 x64.nrows(),
1133 x64.ncols(),
1134 x64.as_col_major_slice()
1135 .iter()
1136 .map(|&value| value as f32)
1137 .collect(),
1138 ))
1139 }
1140}
1141
1142impl MatrixTriangularSolveScalar for f32 {
1143 fn triangular_solve_matrix_impl(
1144 a: &Matrix<Self>,
1145 b: &Matrix<Self>,
1146 left_side: bool,
1147 lower: bool,
1148 transpose_a: bool,
1149 unit_diagonal: bool,
1150 ) -> Result<Matrix<Self>> {
1151 let a64 = Matrix::from_col_major_vec(
1152 a.nrows(),
1153 a.ncols(),
1154 a.as_col_major_slice()
1155 .iter()
1156 .map(|&value| value as f64)
1157 .collect(),
1158 );
1159 let b64 = Matrix::from_col_major_vec(
1160 b.nrows(),
1161 b.ncols(),
1162 b.as_col_major_slice()
1163 .iter()
1164 .map(|&value| value as f64)
1165 .collect(),
1166 );
1167 let x64 = triangular_solve_matrix_direct(
1168 &a64,
1169 &b64,
1170 left_side,
1171 lower,
1172 transpose_a,
1173 unit_diagonal,
1174 )?;
1175 Ok(Matrix::from_col_major_vec(
1176 x64.nrows(),
1177 x64.ncols(),
1178 x64.as_col_major_slice()
1179 .iter()
1180 .map(|&value| value as f32)
1181 .collect(),
1182 ))
1183 }
1184}
1185
1186impl MatrixSolveScalar for Complex32 {
1187 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
1188 let a64 = Matrix::from_col_major_vec(
1189 a.nrows(),
1190 a.ncols(),
1191 a.as_col_major_slice()
1192 .iter()
1193 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
1194 .collect(),
1195 );
1196 let b64 = Matrix::from_col_major_vec(
1197 b.nrows(),
1198 b.ncols(),
1199 b.as_col_major_slice()
1200 .iter()
1201 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
1202 .collect(),
1203 );
1204 let x64 = solve_matrix_direct(&a64, &b64)?;
1205 Ok(Matrix::from_col_major_vec(
1206 x64.nrows(),
1207 x64.ncols(),
1208 x64.as_col_major_slice()
1209 .iter()
1210 .map(|&value| Complex32::new(value.re as f32, value.im as f32))
1211 .collect(),
1212 ))
1213 }
1214
1215 fn solve_matrix_in_impl(
1216 session: &mut dyn tenferro_tensor::Engine,
1217 a: &Matrix<Self>,
1218 b: &Matrix<Self>,
1219 ) -> Result<Matrix<Self>> {
1220 let a64 = Matrix::from_col_major_vec(
1223 a.nrows(),
1224 a.ncols(),
1225 a.as_col_major_slice()
1226 .iter()
1227 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
1228 .collect(),
1229 );
1230 let b64 = Matrix::from_col_major_vec(
1231 b.nrows(),
1232 b.ncols(),
1233 b.as_col_major_slice()
1234 .iter()
1235 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
1236 .collect(),
1237 );
1238 let x64 = solve_matrix_direct_in(session, &a64, &b64)?;
1239 Ok(Matrix::from_col_major_vec(
1240 x64.nrows(),
1241 x64.ncols(),
1242 x64.as_col_major_slice()
1243 .iter()
1244 .map(|&value| Complex32::new(value.re as f32, value.im as f32))
1245 .collect(),
1246 ))
1247 }
1248}
1249
1250impl MatrixTriangularSolveScalar for Complex32 {
1251 fn triangular_solve_matrix_impl(
1252 a: &Matrix<Self>,
1253 b: &Matrix<Self>,
1254 left_side: bool,
1255 lower: bool,
1256 transpose_a: bool,
1257 unit_diagonal: bool,
1258 ) -> Result<Matrix<Self>> {
1259 let a64 = Matrix::from_col_major_vec(
1260 a.nrows(),
1261 a.ncols(),
1262 a.as_col_major_slice()
1263 .iter()
1264 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
1265 .collect(),
1266 );
1267 let b64 = Matrix::from_col_major_vec(
1268 b.nrows(),
1269 b.ncols(),
1270 b.as_col_major_slice()
1271 .iter()
1272 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
1273 .collect(),
1274 );
1275 let x64 = triangular_solve_matrix_direct(
1276 &a64,
1277 &b64,
1278 left_side,
1279 lower,
1280 transpose_a,
1281 unit_diagonal,
1282 )?;
1283 Ok(Matrix::from_col_major_vec(
1284 x64.nrows(),
1285 x64.ncols(),
1286 x64.as_col_major_slice()
1287 .iter()
1288 .map(|&value| Complex32::new(value.re as f32, value.im as f32))
1289 .collect(),
1290 ))
1291 }
1292}
1293
1294fn tensor_scalar_dtype<T: TensorScalar>() -> DType {
1295 T::dtype()
1296}
1297
1298fn try_into_typed_result<T: TensorScalar>(
1299 op: &'static str,
1300 tensor: Tensor,
1301) -> Result<TypedTensor<T>> {
1302 let actual = tensor.dtype();
1303 T::into_typed(tensor).map_err(|source| {
1304 anyhow!(
1305 "{op}: dtype mismatch lhs={actual:?} rhs={:?}: {source}",
1306 tensor_scalar_dtype::<T>()
1307 )
1308 })
1309}
1310
1311fn convert_for_typed<T: TensorScalar>(op: &'static str, tensor: Tensor) -> Result<TypedTensor<T>> {
1312 let expected = tensor_scalar_dtype::<T>();
1313 let tensor = if tensor.dtype() == expected {
1314 tensor
1315 } else {
1316 with_default_session(|session| tensor.convert(expected, session))
1317 .map_err(|e| anyhow!("{op}: dtype conversion to {expected:?} failed: {e}"))?
1318 };
1319 try_into_typed_result::<T>(op, tensor)
1320}
1321
1322fn matrix_to_typed_tensor<T>(matrix: &Matrix<T>) -> TypedTensor<T>
1323where
1324 T: TensorScalar + Copy,
1325{
1326 TypedTensor::from_vec_col_major(
1327 vec![matrix.nrows(), matrix.ncols()],
1328 matrix.as_col_major_slice().to_vec(),
1329 )
1330 .unwrap_or_else(|error| {
1331 crate::invariant_violated("validated matrix rejected by tenferro", error)
1332 })
1333}
1334
1335fn typed_tensor_to_matrix<T>(op: &'static str, tensor: TypedTensor<T>) -> Result<Matrix<T>>
1336where
1337 T: TensorScalar + Copy,
1338{
1339 Matrix::try_from_typed_tensor(tensor).map_err(|err| anyhow!("{op}: {err}"))
1340}
1341
1342fn require_host_linalg_tensor<T: TensorScalar>(
1343 op: &'static str,
1344 tensor: TypedTensor<T>,
1345) -> Result<TypedTensor<T>> {
1346 tensor
1347 .host_data()
1348 .map_err(|error| anyhow!("{op}: result must be host-backed: {error}"))?;
1349 Ok(tensor)
1350}
1351
1352pub fn svd_backend<T>(a: &TypedTensor<T>) -> std::result::Result<SvdResult<T>, BackendLinalgError>
1359where
1360 T: BackendLinalgScalar,
1361{
1362 let tensor = T::into_tensor(
1363 a.shape().to_vec(),
1364 a.host_data()
1365 .map_err(|e| anyhow!("SVD input host access failed: {e}"))?
1366 .to_vec(),
1367 )
1368 .map_err(|e| anyhow!("SVD input tensor construction failed: {e}"))?;
1369 with_default_session(|session| Ok(svd_backend_in(session, tensor)))
1370 .map_err(|e| anyhow!("SVD computation failed via tenferro-tensor: {e}"))?
1371}
1372
1373pub fn svd_backend_in<T>(
1379 session: &mut dyn tenferro_tensor::Engine,
1380 tensor: Tensor,
1381) -> std::result::Result<SvdResult<T>, BackendLinalgError>
1382where
1383 T: BackendLinalgScalar,
1384{
1385 use tenferro_linalg::TensorLinalgExt;
1386
1387 let (u, s, vt) = tensor
1388 .svd(session)
1389 .map_err(|e| anyhow!("SVD computation failed via tenferro-tensor: {e}"))?;
1390 Ok(SvdResult {
1391 u: require_host_linalg_tensor("svd", convert_for_typed::<T>("svd", u)?)?,
1392 s: require_host_linalg_tensor("svd", convert_for_typed::<T::Real>("svd", s)?)?,
1393 vt: require_host_linalg_tensor("svd", convert_for_typed::<T>("svd", vt)?)?,
1394 })
1395}
1396
1397pub fn qr_backend<T>(
1405 a: TypedTensor<T>,
1406) -> std::result::Result<(TypedTensor<T>, TypedTensor<T>), BackendLinalgError>
1407where
1408 T: BackendLinalgScalar,
1409{
1410 let (shape, data) = a
1411 .into_vec_col_major()
1412 .map_err(|e| anyhow!("QR input host access failed: {e}"))?;
1413 let tensor = T::into_tensor(shape, data)
1414 .map_err(|e| anyhow!("QR input tensor construction failed: {e}"))?;
1415 with_default_session(|session| Ok(qr_backend_in(session, tensor)))
1416 .map_err(|e| anyhow!("QR computation failed via tenferro-tensor: {e}"))?
1417}
1418
1419pub fn qr_backend_in<T>(
1425 session: &mut dyn tenferro_tensor::Engine,
1426 tensor: Tensor,
1427) -> std::result::Result<(TypedTensor<T>, TypedTensor<T>), BackendLinalgError>
1428where
1429 T: BackendLinalgScalar,
1430{
1431 use tenferro_linalg::TensorLinalgExt;
1432
1433 let (q, r) = tensor
1434 .qr(session)
1435 .map_err(|e| anyhow!("QR computation failed via tenferro-tensor: {e}"))?;
1436 Ok((
1437 convert_for_typed::<T>("qr", q)?,
1438 convert_for_typed::<T>("qr", r)?,
1439 ))
1440}
1441
1442pub fn solve_backend<T>(
1448 a: &TypedTensor<T>,
1449 b: &TypedTensor<T>,
1450) -> std::result::Result<TypedTensor<T>, BackendLinalgError>
1451where
1452 T: BackendLinalgScalar,
1453{
1454 let a_tensor = T::into_tensor(
1455 a.shape().to_vec(),
1456 a.host_data()
1457 .map_err(|e| anyhow!("solve input host access failed: {e}"))?
1458 .to_vec(),
1459 )
1460 .map_err(|e| anyhow!("solve lhs tensor construction failed: {e}"))?;
1461 let b_tensor = T::into_tensor(
1462 b.shape().to_vec(),
1463 b.host_data()
1464 .map_err(|e| anyhow!("solve rhs host access failed: {e}"))?
1465 .to_vec(),
1466 )
1467 .map_err(|e| anyhow!("solve rhs tensor construction failed: {e}"))?;
1468 let result = with_default_session(|session| a_tensor.solve(&b_tensor, session))
1469 .map_err(|e| anyhow!("linear solve failed via tenferro-tensor: {e}"))?;
1470 try_into_typed_result::<T>("solve", result).map_err(BackendLinalgError::from)
1471}
1472
1473pub fn triangular_solve_backend<T>(
1484 a: &TypedTensor<T>,
1485 b: &TypedTensor<T>,
1486 left_side: bool,
1487 lower: bool,
1488 transpose_a: bool,
1489 unit_diagonal: bool,
1490) -> std::result::Result<TypedTensor<T>, BackendLinalgError>
1491where
1492 T: BackendLinalgScalar,
1493{
1494 let a_tensor = T::into_tensor(
1495 a.shape().to_vec(),
1496 a.host_data()
1497 .map_err(|e| anyhow!("triangular solve input host access failed: {e}"))?
1498 .to_vec(),
1499 )
1500 .map_err(|e| anyhow!("triangular solve lhs tensor construction failed: {e}"))?;
1501 let b_tensor = T::into_tensor(
1502 b.shape().to_vec(),
1503 b.host_data()
1504 .map_err(|e| anyhow!("triangular solve rhs host access failed: {e}"))?
1505 .to_vec(),
1506 )
1507 .map_err(|e| anyhow!("triangular solve rhs tensor construction failed: {e}"))?;
1508 let result = with_default_session(|session| {
1509 a_tensor.triangular_solve(
1510 &b_tensor,
1511 left_side,
1512 lower,
1513 transpose_a,
1514 unit_diagonal,
1515 session,
1516 )
1517 })
1518 .map_err(|e| anyhow!("triangular solve failed via tenferro-tensor: {e}"))?;
1519 try_into_typed_result::<T>("triangular_solve", result).map_err(BackendLinalgError::from)
1520}
1521
1522pub fn solve_matrix<T>(
1540 a: &Matrix<T>,
1541 b: &Matrix<T>,
1542) -> std::result::Result<Matrix<T>, BackendLinalgError>
1543where
1544 T: MatrixSolveScalar,
1545{
1546 T::solve_matrix_impl(a, b).map_err(BackendLinalgError::from)
1547}
1548
1549pub fn solve_matrix_owned<T>(
1568 a: Matrix<T>,
1569 b: Matrix<T>,
1570) -> std::result::Result<Matrix<T>, BackendLinalgError>
1571where
1572 T: MatrixSolveScalar,
1573{
1574 T::solve_matrix_owned_impl(a, b).map_err(BackendLinalgError::from)
1575}
1576
1577pub fn triangular_solve_matrix<T>(
1597 a: &Matrix<T>,
1598 b: &Matrix<T>,
1599 left_side: bool,
1600 lower: bool,
1601 transpose_a: bool,
1602 unit_diagonal: bool,
1603) -> std::result::Result<Matrix<T>, BackendLinalgError>
1604where
1605 T: MatrixTriangularSolveScalar,
1606{
1607 T::triangular_solve_matrix_impl(a, b, left_side, lower, transpose_a, unit_diagonal)
1608 .map_err(BackendLinalgError::from)
1609}
1610
1611pub fn triangular_solve_matrix_owned<T>(
1631 a: Matrix<T>,
1632 b: Matrix<T>,
1633 left_side: bool,
1634 lower: bool,
1635 transpose_a: bool,
1636 unit_diagonal: bool,
1637) -> std::result::Result<Matrix<T>, BackendLinalgError>
1638where
1639 T: MatrixTriangularSolveScalar,
1640{
1641 T::triangular_solve_matrix_owned_impl(a, b, left_side, lower, transpose_a, unit_diagonal)
1642 .map_err(BackendLinalgError::from)
1643}
1644
1645fn full_piv_lu_tensor<T>(
1646 tensor: Tensor,
1647) -> std::result::Result<FullPivLuResult<T>, BackendLinalgError>
1648where
1649 T: BackendLinalgScalar,
1650{
1651 with_default_session(|session| Ok(full_piv_lu_tensor_in(session, tensor)))
1652 .map_err(|e| anyhow!("complete-pivoting LU failed via tenferro-tensor: {e}"))?
1653}
1654
1655fn full_piv_lu_tensor_in<T>(
1657 session: &mut dyn tenferro_tensor::Engine,
1658 tensor: Tensor,
1659) -> std::result::Result<FullPivLuResult<T>, BackendLinalgError>
1660where
1661 T: BackendLinalgScalar,
1662{
1663 use tenferro_linalg::TensorLinalgExt;
1664
1665 let (p, l, u, q, _parity) = tensor
1666 .full_piv_lu(session)
1667 .map_err(|e| anyhow!("complete-pivoting LU failed via tenferro-tensor: {e}"))?;
1668 Ok(FullPivLuResult {
1669 p: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", p)?)?,
1670 l: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", l)?)?,
1671 u: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", u)?)?,
1672 q: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", q)?)?,
1673 })
1674}
1675
1676pub fn full_piv_lu_backend<T>(
1683 a: &TypedTensor<T>,
1684) -> std::result::Result<FullPivLuResult<T>, BackendLinalgError>
1685where
1686 T: BackendLinalgScalar,
1687{
1688 let tensor = T::into_tensor(
1689 a.shape().to_vec(),
1690 a.host_data()
1691 .map_err(|e| anyhow!("LU input host access failed: {e}"))?
1692 .to_vec(),
1693 )
1694 .map_err(|e| anyhow!("LU input tensor construction failed: {e}"))?;
1695 full_piv_lu_tensor(tensor)
1696}
1697
1698pub fn full_piv_lu_backend_in<T>(
1708 session: &mut dyn tenferro_tensor::Engine,
1709 a: &TypedTensor<T>,
1710) -> std::result::Result<FullPivLuResult<T>, BackendLinalgError>
1711where
1712 T: BackendLinalgScalar,
1713{
1714 let tensor = T::into_tensor(
1715 a.shape().to_vec(),
1716 a.host_data()
1717 .map_err(|e| anyhow!("LU input host access failed: {e}"))?
1718 .to_vec(),
1719 )
1720 .map_err(|e| anyhow!("LU input tensor construction failed: {e}"))?;
1721 full_piv_lu_tensor_in(session, tensor)
1722}
1723
1724pub fn full_piv_lu_matrix<T>(
1741 a: &Matrix<T>,
1742) -> std::result::Result<FullPivLuMatrixResult<T>, BackendLinalgError>
1743where
1744 T: BackendLinalgScalar + Copy,
1745{
1746 let tensor = matrix_to_typed_tensor(a);
1747 let decomp = full_piv_lu_backend(&tensor)?;
1748 Ok(FullPivLuMatrixResult {
1749 p: typed_tensor_to_matrix("full_piv_lu", decomp.p)?,
1750 l: typed_tensor_to_matrix("full_piv_lu", decomp.l)?,
1751 u: typed_tensor_to_matrix("full_piv_lu", decomp.u)?,
1752 q: typed_tensor_to_matrix("full_piv_lu", decomp.q)?,
1753 })
1754}
1755
1756pub fn full_piv_lu_matrix_in<T>(
1766 session: &mut dyn tenferro_tensor::Engine,
1767 a: &Matrix<T>,
1768) -> std::result::Result<FullPivLuMatrixResult<T>, BackendLinalgError>
1769where
1770 T: BackendLinalgScalar + Copy,
1771{
1772 let tensor = matrix_to_typed_tensor(a);
1773 let decomp = full_piv_lu_backend_in(session, &tensor)?;
1774 Ok(FullPivLuMatrixResult {
1775 p: typed_tensor_to_matrix("full_piv_lu", decomp.p)?,
1776 l: typed_tensor_to_matrix("full_piv_lu", decomp.l)?,
1777 u: typed_tensor_to_matrix("full_piv_lu", decomp.u)?,
1778 q: typed_tensor_to_matrix("full_piv_lu", decomp.q)?,
1779 })
1780}
1781
1782pub fn full_piv_lu_matrix_owned<T>(
1834 a: Matrix<T>,
1835) -> std::result::Result<FullPivLuMatrixResult<T>, BackendLinalgError>
1836where
1837 T: BackendLinalgScalar + Copy,
1838{
1839 let tensor = T::typed_tensor_into_tensor(a.into_typed_tensor());
1840 let decomp = full_piv_lu_tensor(tensor)?;
1841 Ok(FullPivLuMatrixResult {
1842 p: typed_tensor_to_matrix("full_piv_lu", decomp.p)?,
1843 l: typed_tensor_to_matrix("full_piv_lu", decomp.l)?,
1844 u: typed_tensor_to_matrix("full_piv_lu", decomp.u)?,
1845 q: typed_tensor_to_matrix("full_piv_lu", decomp.q)?,
1846 })
1847}
1848
1849#[cfg(test)]
1850mod tests;