1use anyhow::{anyhow, Result};
7use num_complex::{Complex32, Complex64};
8use tenferro::{DType, Tensor, TensorScalar, TensorSessionOpsExt, TypedTensor};
9use tenferro_linalg::TensorLinalgExt;
10
11use crate::context::with_default_session;
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 crate::require_invariant(tensor.duplicate(), "linalg result duplication failed")
51}
52
53impl<T: TensorScalar> SvdResult<T> {
54 pub fn u(&self) -> &TypedTensor<T> {
56 &self.u
57 }
58
59 pub fn s(&self) -> &TypedTensor<T::Real> {
61 &self.s
62 }
63
64 pub fn vt(&self) -> &TypedTensor<T> {
66 &self.vt
67 }
68
69 pub fn into_parts(self) -> (TypedTensor<T>, TypedTensor<T::Real>, TypedTensor<T>) {
71 (self.u, self.s, self.vt)
72 }
73}
74
75impl<T: TensorScalar> FullPivLuResult<T> {
76 pub fn p(&self) -> &TypedTensor<T> {
78 &self.p
79 }
80
81 pub fn l(&self) -> &TypedTensor<T> {
83 &self.l
84 }
85
86 pub fn u(&self) -> &TypedTensor<T> {
88 &self.u
89 }
90
91 pub fn q(&self) -> &TypedTensor<T> {
93 &self.q
94 }
95
96 pub fn into_parts(
98 self,
99 ) -> (
100 TypedTensor<T>,
101 TypedTensor<T>,
102 TypedTensor<T>,
103 TypedTensor<T>,
104 ) {
105 (self.p, self.l, self.u, self.q)
106 }
107}
108
109impl<T: TensorScalar> Clone for SvdResult<T> {
110 fn clone(&self) -> Self {
111 Self {
112 u: clone_linalg_tensor(&self.u),
113 s: clone_linalg_tensor(&self.s),
114 vt: clone_linalg_tensor(&self.vt),
115 }
116 }
117}
118
119impl<T: TensorScalar> Clone for FullPivLuResult<T> {
120 fn clone(&self) -> Self {
121 Self {
122 p: clone_linalg_tensor(&self.p),
123 l: clone_linalg_tensor(&self.l),
124 u: clone_linalg_tensor(&self.u),
125 q: clone_linalg_tensor(&self.q),
126 }
127 }
128}
129
130#[derive(Debug, Clone)]
143pub struct FullPivLuMatrixResult<T> {
144 pub p: Matrix<T>,
146 pub l: Matrix<T>,
148 pub u: Matrix<T>,
150 pub q: Matrix<T>,
152}
153
154pub trait BackendLinalgScalar: TensorScalar {}
156
157pub trait FullPivLuScalar: BackendLinalgScalar {
172 fn solve_right_full_piv_lu(
181 lhs_values: &[Self],
182 lhs_rows: usize,
183 lhs_cols: usize,
184 pivot_values: &[Self],
185 pivot_rows: usize,
186 pivot_cols: usize,
187 ) -> std::result::Result<Vec<Self>, BackendLinalgError>;
188}
189
190macro_rules! impl_full_piv_lu_scalar {
191 ($t:ty) => {
192 impl FullPivLuScalar for $t {
193 fn solve_right_full_piv_lu(
194 lhs_values: &[Self],
195 lhs_rows: usize,
196 lhs_cols: usize,
197 pivot_values: &[Self],
198 pivot_rows: usize,
199 pivot_cols: usize,
200 ) -> std::result::Result<Vec<Self>, BackendLinalgError> {
201 if pivot_rows != pivot_cols {
202 return Err(BackendLinalgError::from(anyhow::anyhow!(
203 "full-pivot solve requires a square pivot matrix, got {}x{}",
204 pivot_rows,
205 pivot_cols
206 )));
207 }
208 if lhs_cols != pivot_rows {
209 return Err(BackendLinalgError::from(anyhow::anyhow!(
210 "cannot solve T * P = Pi1 with Pi1 shape {}x{} and P shape {}x{}",
211 lhs_rows,
212 lhs_cols,
213 pivot_rows,
214 pivot_cols
215 )));
216 }
217
218 let lhs_t = transpose_column_major(lhs_values, lhs_rows, lhs_cols);
219 let pivot_t = transpose_column_major(pivot_values, pivot_rows, pivot_cols);
220 let pivot_tensor = tenferro_tensor::Tensor::from_vec_col_major(
221 vec![pivot_cols, pivot_rows],
222 pivot_t,
223 )
224 .map_err(|e| BackendLinalgError::from(anyhow::Error::new(e)))?;
225 let lhs_tensor =
226 tenferro_tensor::Tensor::from_vec_col_major(vec![lhs_cols, lhs_rows], lhs_t)
227 .map_err(|e| BackendLinalgError::from(anyhow::Error::new(e)))?;
228 let solved_t = with_default_session(|session| {
229 pivot_tensor.full_piv_lu_solve(&lhs_tensor, session)
230 })
231 .map_err(|e| {
232 BackendLinalgError::from(anyhow::anyhow!("full_piv_lu_solve failed: {e}"))
233 })?;
234
235 let solved_t_values = solved_t.as_slice::<Self>().map_err(|e| {
236 BackendLinalgError::from(anyhow::anyhow!(
237 "full_piv_lu_solve returned unexpected dtype: {e}"
238 ))
239 })?;
240 Ok(transpose_column_major(solved_t_values, lhs_cols, lhs_rows))
241 }
242 }
243 };
244}
245
246impl_full_piv_lu_scalar!(f32);
247impl_full_piv_lu_scalar!(f64);
248impl_full_piv_lu_scalar!(num_complex::Complex32);
249impl_full_piv_lu_scalar!(num_complex::Complex64);
250
251fn transpose_column_major<T: Copy + num_traits::Zero>(
253 values: &[T],
254 nrows: usize,
255 ncols: usize,
256) -> Vec<T> {
257 let mut out = vec![T::zero(); nrows * ncols];
258 for col in 0..ncols {
259 for row in 0..nrows {
260 out[col + ncols * row] = values[row + nrows * col];
261 }
262 }
263 out
264}
265
266impl<T: TensorScalar> BackendLinalgScalar for T {}
267
268pub trait MatrixSolveScalar: BackendLinalgScalar + crate::matrix::MatrixScalar {
282 #[doc(hidden)]
283 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>>;
284
285 #[doc(hidden)]
286 fn solve_matrix_owned_impl(a: Matrix<Self>, b: Matrix<Self>) -> Result<Matrix<Self>> {
287 Self::solve_matrix_impl(&a, &b)
288 }
289}
290
291#[derive(Debug, thiserror::Error)]
303#[error("backend linear algebra failed: {source}")]
304pub struct BackendLinalgError {
305 #[source]
307 pub source: anyhow::Error,
308}
309
310impl From<anyhow::Error> for BackendLinalgError {
311 fn from(source: anyhow::Error) -> Self {
312 Self { source }
313 }
314}
315
316pub trait MatrixTriangularSolveScalar: BackendLinalgScalar + crate::matrix::MatrixScalar {
331 #[doc(hidden)]
332 fn triangular_solve_matrix_impl(
333 a: &Matrix<Self>,
334 b: &Matrix<Self>,
335 left_side: bool,
336 lower: bool,
337 transpose_a: bool,
338 unit_diagonal: bool,
339 ) -> Result<Matrix<Self>>;
340
341 #[doc(hidden)]
342 fn triangular_solve_matrix_owned_impl(
343 a: Matrix<Self>,
344 b: Matrix<Self>,
345 left_side: bool,
346 lower: bool,
347 transpose_a: bool,
348 unit_diagonal: bool,
349 ) -> Result<Matrix<Self>> {
350 Self::triangular_solve_matrix_impl(&a, &b, left_side, lower, transpose_a, unit_diagonal)
351 }
352}
353
354fn solve_matrix_direct<T>(a: &Matrix<T>, b: &Matrix<T>) -> Result<Matrix<T>>
355where
356 T: BackendLinalgScalar + Copy,
357 Tensor: From<TypedTensor<T>>,
358{
359 solve_matrix_direct_owned(a.clone(), b.clone())
360}
361
362fn solve_matrix_direct_owned<T>(a: Matrix<T>, b: Matrix<T>) -> Result<Matrix<T>>
363where
364 T: BackendLinalgScalar + Copy,
365 Tensor: From<TypedTensor<T>>,
366{
367 let a_tensor: Tensor = a.into_typed_tensor().into();
368 let b_tensor: Tensor = b.into_typed_tensor().into();
369 let result = with_default_session(|session| a_tensor.solve(&b_tensor, session))
370 .map_err(|e| anyhow!("linear solve failed via tenferro-tensor: {e}"))?;
371 let x = try_into_typed_result::<T>("solve", result)?;
372 typed_tensor_to_matrix("solve", x)
373}
374
375fn triangular_solve_matrix_direct<T>(
376 a: &Matrix<T>,
377 b: &Matrix<T>,
378 left_side: bool,
379 lower: bool,
380 transpose_a: bool,
381 unit_diagonal: bool,
382) -> Result<Matrix<T>>
383where
384 T: BackendLinalgScalar + Copy,
385 Tensor: From<TypedTensor<T>>,
386{
387 triangular_solve_matrix_direct_owned(
388 a.clone(),
389 b.clone(),
390 left_side,
391 lower,
392 transpose_a,
393 unit_diagonal,
394 )
395}
396
397fn triangular_solve_matrix_direct_owned<T>(
398 a: Matrix<T>,
399 b: Matrix<T>,
400 left_side: bool,
401 lower: bool,
402 transpose_a: bool,
403 unit_diagonal: bool,
404) -> Result<Matrix<T>>
405where
406 T: BackendLinalgScalar + Copy,
407 Tensor: From<TypedTensor<T>>,
408{
409 let a_tensor: Tensor = a.into_typed_tensor().into();
410 let b_tensor: Tensor = b.into_typed_tensor().into();
411 let result = with_default_session(|session| {
412 a_tensor.triangular_solve(
413 &b_tensor,
414 left_side,
415 lower,
416 transpose_a,
417 unit_diagonal,
418 session,
419 )
420 })
421 .map_err(|e| anyhow!("triangular solve failed via tenferro-tensor: {e}"))?;
422 let x = try_into_typed_result::<T>("triangular_solve", result)?;
423 typed_tensor_to_matrix("triangular_solve", x)
424}
425
426impl MatrixSolveScalar for f64 {
427 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
428 solve_matrix_direct(a, b)
429 }
430
431 fn solve_matrix_owned_impl(a: Matrix<Self>, b: Matrix<Self>) -> Result<Matrix<Self>> {
432 solve_matrix_direct_owned(a, b)
433 }
434}
435
436impl MatrixTriangularSolveScalar for f64 {
437 fn triangular_solve_matrix_impl(
438 a: &Matrix<Self>,
439 b: &Matrix<Self>,
440 left_side: bool,
441 lower: bool,
442 transpose_a: bool,
443 unit_diagonal: bool,
444 ) -> Result<Matrix<Self>> {
445 triangular_solve_matrix_direct(a, b, left_side, lower, transpose_a, unit_diagonal)
446 }
447
448 fn triangular_solve_matrix_owned_impl(
449 a: Matrix<Self>,
450 b: Matrix<Self>,
451 left_side: bool,
452 lower: bool,
453 transpose_a: bool,
454 unit_diagonal: bool,
455 ) -> Result<Matrix<Self>> {
456 triangular_solve_matrix_direct_owned(a, b, left_side, lower, transpose_a, unit_diagonal)
457 }
458}
459
460impl MatrixSolveScalar for Complex64 {
461 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
462 solve_matrix_direct(a, b)
463 }
464
465 fn solve_matrix_owned_impl(a: Matrix<Self>, b: Matrix<Self>) -> Result<Matrix<Self>> {
466 solve_matrix_direct_owned(a, b)
467 }
468}
469
470impl MatrixTriangularSolveScalar for Complex64 {
471 fn triangular_solve_matrix_impl(
472 a: &Matrix<Self>,
473 b: &Matrix<Self>,
474 left_side: bool,
475 lower: bool,
476 transpose_a: bool,
477 unit_diagonal: bool,
478 ) -> Result<Matrix<Self>> {
479 triangular_solve_matrix_direct(a, b, left_side, lower, transpose_a, unit_diagonal)
480 }
481
482 fn triangular_solve_matrix_owned_impl(
483 a: Matrix<Self>,
484 b: Matrix<Self>,
485 left_side: bool,
486 lower: bool,
487 transpose_a: bool,
488 unit_diagonal: bool,
489 ) -> Result<Matrix<Self>> {
490 triangular_solve_matrix_direct_owned(a, b, left_side, lower, transpose_a, unit_diagonal)
491 }
492}
493
494impl MatrixSolveScalar for f32 {
495 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
496 let a64 = Matrix::from_col_major_vec(
497 a.nrows(),
498 a.ncols(),
499 a.as_col_major_slice()
500 .iter()
501 .map(|&value| value as f64)
502 .collect(),
503 );
504 let b64 = Matrix::from_col_major_vec(
505 b.nrows(),
506 b.ncols(),
507 b.as_col_major_slice()
508 .iter()
509 .map(|&value| value as f64)
510 .collect(),
511 );
512 let x64 = solve_matrix_direct(&a64, &b64)?;
513 Ok(Matrix::from_col_major_vec(
514 x64.nrows(),
515 x64.ncols(),
516 x64.as_col_major_slice()
517 .iter()
518 .map(|&value| value as f32)
519 .collect(),
520 ))
521 }
522}
523
524impl MatrixTriangularSolveScalar for f32 {
525 fn triangular_solve_matrix_impl(
526 a: &Matrix<Self>,
527 b: &Matrix<Self>,
528 left_side: bool,
529 lower: bool,
530 transpose_a: bool,
531 unit_diagonal: bool,
532 ) -> Result<Matrix<Self>> {
533 let a64 = Matrix::from_col_major_vec(
534 a.nrows(),
535 a.ncols(),
536 a.as_col_major_slice()
537 .iter()
538 .map(|&value| value as f64)
539 .collect(),
540 );
541 let b64 = Matrix::from_col_major_vec(
542 b.nrows(),
543 b.ncols(),
544 b.as_col_major_slice()
545 .iter()
546 .map(|&value| value as f64)
547 .collect(),
548 );
549 let x64 = triangular_solve_matrix_direct(
550 &a64,
551 &b64,
552 left_side,
553 lower,
554 transpose_a,
555 unit_diagonal,
556 )?;
557 Ok(Matrix::from_col_major_vec(
558 x64.nrows(),
559 x64.ncols(),
560 x64.as_col_major_slice()
561 .iter()
562 .map(|&value| value as f32)
563 .collect(),
564 ))
565 }
566}
567
568impl MatrixSolveScalar for Complex32 {
569 fn solve_matrix_impl(a: &Matrix<Self>, b: &Matrix<Self>) -> Result<Matrix<Self>> {
570 let a64 = Matrix::from_col_major_vec(
571 a.nrows(),
572 a.ncols(),
573 a.as_col_major_slice()
574 .iter()
575 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
576 .collect(),
577 );
578 let b64 = Matrix::from_col_major_vec(
579 b.nrows(),
580 b.ncols(),
581 b.as_col_major_slice()
582 .iter()
583 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
584 .collect(),
585 );
586 let x64 = solve_matrix_direct(&a64, &b64)?;
587 Ok(Matrix::from_col_major_vec(
588 x64.nrows(),
589 x64.ncols(),
590 x64.as_col_major_slice()
591 .iter()
592 .map(|&value| Complex32::new(value.re as f32, value.im as f32))
593 .collect(),
594 ))
595 }
596}
597
598impl MatrixTriangularSolveScalar for Complex32 {
599 fn triangular_solve_matrix_impl(
600 a: &Matrix<Self>,
601 b: &Matrix<Self>,
602 left_side: bool,
603 lower: bool,
604 transpose_a: bool,
605 unit_diagonal: bool,
606 ) -> Result<Matrix<Self>> {
607 let a64 = Matrix::from_col_major_vec(
608 a.nrows(),
609 a.ncols(),
610 a.as_col_major_slice()
611 .iter()
612 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
613 .collect(),
614 );
615 let b64 = Matrix::from_col_major_vec(
616 b.nrows(),
617 b.ncols(),
618 b.as_col_major_slice()
619 .iter()
620 .map(|&value| Complex64::new(value.re as f64, value.im as f64))
621 .collect(),
622 );
623 let x64 = triangular_solve_matrix_direct(
624 &a64,
625 &b64,
626 left_side,
627 lower,
628 transpose_a,
629 unit_diagonal,
630 )?;
631 Ok(Matrix::from_col_major_vec(
632 x64.nrows(),
633 x64.ncols(),
634 x64.as_col_major_slice()
635 .iter()
636 .map(|&value| Complex32::new(value.re as f32, value.im as f32))
637 .collect(),
638 ))
639 }
640}
641
642fn tensor_scalar_dtype<T: TensorScalar>() -> DType {
643 T::dtype()
644}
645
646fn try_into_typed_result<T: TensorScalar>(
647 op: &'static str,
648 tensor: Tensor,
649) -> Result<TypedTensor<T>> {
650 let actual = tensor.dtype();
651 T::into_typed(tensor).map_err(|source| {
652 anyhow!(
653 "{op}: dtype mismatch lhs={actual:?} rhs={:?}: {source}",
654 tensor_scalar_dtype::<T>()
655 )
656 })
657}
658
659fn convert_for_typed<T: TensorScalar>(op: &'static str, tensor: Tensor) -> Result<TypedTensor<T>> {
660 let expected = tensor_scalar_dtype::<T>();
661 let tensor = if tensor.dtype() == expected {
662 tensor
663 } else {
664 with_default_session(|session| tensor.convert(expected, session))
665 .map_err(|e| anyhow!("{op}: dtype conversion to {expected:?} failed: {e}"))?
666 };
667 try_into_typed_result::<T>(op, tensor)
668}
669
670fn matrix_to_typed_tensor<T>(matrix: &Matrix<T>) -> TypedTensor<T>
671where
672 T: TensorScalar + Copy,
673{
674 crate::require_invariant(
675 TypedTensor::from_vec_col_major(
676 vec![matrix.nrows(), matrix.ncols()],
677 matrix.as_col_major_slice().to_vec(),
678 ),
679 "validated matrix rejected by tenferro",
680 )
681}
682
683fn typed_tensor_to_matrix<T>(op: &'static str, tensor: TypedTensor<T>) -> Result<Matrix<T>>
684where
685 T: TensorScalar + Copy,
686{
687 Matrix::try_from_typed_tensor(tensor).map_err(|err| anyhow!("{op}: {err}"))
688}
689
690fn require_host_linalg_tensor<T: TensorScalar>(
691 op: &'static str,
692 tensor: TypedTensor<T>,
693) -> Result<TypedTensor<T>> {
694 tensor
695 .host_data()
696 .map_err(|error| anyhow!("{op}: result must be host-backed: {error}"))?;
697 Ok(tensor)
698}
699
700pub fn svd_backend<T>(a: &TypedTensor<T>) -> std::result::Result<SvdResult<T>, BackendLinalgError>
707where
708 T: BackendLinalgScalar,
709{
710 let tensor = T::into_tensor(
711 a.shape().to_vec(),
712 a.host_data()
713 .map_err(|e| anyhow!("SVD input host access failed: {e}"))?
714 .to_vec(),
715 )
716 .map_err(|e| anyhow!("SVD input tensor construction failed: {e}"))?;
717 let (u, s, vt) = with_default_session(|session| tensor.svd(session))
718 .map_err(|e| anyhow!("SVD computation failed via tenferro-tensor: {e}"))?;
719 Ok(SvdResult {
720 u: require_host_linalg_tensor("svd", convert_for_typed::<T>("svd", u)?)?,
721 s: require_host_linalg_tensor("svd", convert_for_typed::<T::Real>("svd", s)?)?,
722 vt: require_host_linalg_tensor("svd", convert_for_typed::<T>("svd", vt)?)?,
723 })
724}
725
726pub fn qr_backend<T>(
733 a: &TypedTensor<T>,
734) -> std::result::Result<(TypedTensor<T>, TypedTensor<T>), BackendLinalgError>
735where
736 T: BackendLinalgScalar,
737{
738 let tensor = T::into_tensor(
739 a.shape().to_vec(),
740 a.host_data()
741 .map_err(|e| anyhow!("QR input host access failed: {e}"))?
742 .to_vec(),
743 )
744 .map_err(|e| anyhow!("QR input tensor construction failed: {e}"))?;
745 let (q, r) = with_default_session(|session| tensor.qr(session))
746 .map_err(|e| anyhow!("QR computation failed via tenferro-tensor: {e}"))?;
747 Ok((
748 convert_for_typed::<T>("qr", q)?,
749 convert_for_typed::<T>("qr", r)?,
750 ))
751}
752
753pub fn solve_backend<T>(
759 a: &TypedTensor<T>,
760 b: &TypedTensor<T>,
761) -> std::result::Result<TypedTensor<T>, BackendLinalgError>
762where
763 T: BackendLinalgScalar,
764{
765 let a_tensor = T::into_tensor(
766 a.shape().to_vec(),
767 a.host_data()
768 .map_err(|e| anyhow!("solve input host access failed: {e}"))?
769 .to_vec(),
770 )
771 .map_err(|e| anyhow!("solve lhs tensor construction failed: {e}"))?;
772 let b_tensor = T::into_tensor(
773 b.shape().to_vec(),
774 b.host_data()
775 .map_err(|e| anyhow!("solve rhs host access failed: {e}"))?
776 .to_vec(),
777 )
778 .map_err(|e| anyhow!("solve rhs tensor construction failed: {e}"))?;
779 let result = with_default_session(|session| a_tensor.solve(&b_tensor, session))
780 .map_err(|e| anyhow!("linear solve failed via tenferro-tensor: {e}"))?;
781 try_into_typed_result::<T>("solve", result).map_err(BackendLinalgError::from)
782}
783
784pub fn triangular_solve_backend<T>(
794 a: &TypedTensor<T>,
795 b: &TypedTensor<T>,
796 left_side: bool,
797 lower: bool,
798 transpose_a: bool,
799 unit_diagonal: bool,
800) -> std::result::Result<TypedTensor<T>, BackendLinalgError>
801where
802 T: BackendLinalgScalar,
803{
804 let a_tensor = T::into_tensor(
805 a.shape().to_vec(),
806 a.host_data()
807 .map_err(|e| anyhow!("triangular solve input host access failed: {e}"))?
808 .to_vec(),
809 )
810 .map_err(|e| anyhow!("triangular solve lhs tensor construction failed: {e}"))?;
811 let b_tensor = T::into_tensor(
812 b.shape().to_vec(),
813 b.host_data()
814 .map_err(|e| anyhow!("triangular solve rhs host access failed: {e}"))?
815 .to_vec(),
816 )
817 .map_err(|e| anyhow!("triangular solve rhs tensor construction failed: {e}"))?;
818 let result = with_default_session(|session| {
819 a_tensor.triangular_solve(
820 &b_tensor,
821 left_side,
822 lower,
823 transpose_a,
824 unit_diagonal,
825 session,
826 )
827 })
828 .map_err(|e| anyhow!("triangular solve failed via tenferro-tensor: {e}"))?;
829 try_into_typed_result::<T>("triangular_solve", result).map_err(BackendLinalgError::from)
830}
831
832pub fn solve_matrix<T>(
851 a: &Matrix<T>,
852 b: &Matrix<T>,
853) -> std::result::Result<Matrix<T>, BackendLinalgError>
854where
855 T: MatrixSolveScalar,
856{
857 T::solve_matrix_impl(a, b).map_err(BackendLinalgError::from)
858}
859
860pub fn solve_matrix_owned<T>(
880 a: Matrix<T>,
881 b: Matrix<T>,
882) -> std::result::Result<Matrix<T>, BackendLinalgError>
883where
884 T: MatrixSolveScalar,
885{
886 T::solve_matrix_owned_impl(a, b).map_err(BackendLinalgError::from)
887}
888
889pub fn triangular_solve_matrix<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) -> std::result::Result<Matrix<T>, BackendLinalgError>
917where
918 T: MatrixTriangularSolveScalar,
919{
920 T::triangular_solve_matrix_impl(a, b, left_side, lower, transpose_a, unit_diagonal)
921 .map_err(BackendLinalgError::from)
922}
923
924pub fn triangular_solve_matrix_owned<T>(
945 a: Matrix<T>,
946 b: Matrix<T>,
947 left_side: bool,
948 lower: bool,
949 transpose_a: bool,
950 unit_diagonal: bool,
951) -> std::result::Result<Matrix<T>, BackendLinalgError>
952where
953 T: MatrixTriangularSolveScalar,
954{
955 T::triangular_solve_matrix_owned_impl(a, b, left_side, lower, transpose_a, unit_diagonal)
956 .map_err(BackendLinalgError::from)
957}
958
959pub fn full_piv_lu_backend<T>(
966 a: &TypedTensor<T>,
967) -> std::result::Result<FullPivLuResult<T>, BackendLinalgError>
968where
969 T: BackendLinalgScalar,
970{
971 let tensor = T::into_tensor(
972 a.shape().to_vec(),
973 a.host_data()
974 .map_err(|e| anyhow!("LU input host access failed: {e}"))?
975 .to_vec(),
976 )
977 .map_err(|e| anyhow!("LU input tensor construction failed: {e}"))?;
978 let (p, l, u, q, _parity) = with_default_session(|session| tensor.full_piv_lu(session))
979 .map_err(|e| anyhow!("complete-pivoting LU failed via tenferro-tensor: {e}"))?;
980 Ok(FullPivLuResult {
981 p: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", p)?)?,
982 l: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", l)?)?,
983 u: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", u)?)?,
984 q: require_host_linalg_tensor("full_piv_lu", convert_for_typed::<T>("full_piv_lu", q)?)?,
985 })
986}
987
988pub fn full_piv_lu_matrix<T>(
1006 a: &Matrix<T>,
1007) -> std::result::Result<FullPivLuMatrixResult<T>, BackendLinalgError>
1008where
1009 T: BackendLinalgScalar + Copy,
1010{
1011 let tensor = matrix_to_typed_tensor(a);
1012 let decomp = full_piv_lu_backend(&tensor)?;
1013 Ok(FullPivLuMatrixResult {
1014 p: typed_tensor_to_matrix("full_piv_lu", decomp.p)?,
1015 l: typed_tensor_to_matrix("full_piv_lu", decomp.l)?,
1016 u: typed_tensor_to_matrix("full_piv_lu", decomp.u)?,
1017 q: typed_tensor_to_matrix("full_piv_lu", decomp.q)?,
1018 })
1019}
1020
1021#[cfg(test)]
1022mod tests;