Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
dense_subspace_unused.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace uni20::krylov
6{
7
11
12namespace detail
13{
14
19template <typename Scalar> using ColumnMajorLapackMatrix = uni20::DenseMatrix<Scalar, uni20::ColumnMajor>;
20
21} // namespace detail
22
23template <uni20::LapackReal Scalar> struct RealSymmetricEigensystem
24{
25 std::vector<Scalar> eigenvalues;
27};
28
31template <uni20::LapackComplexReal Real> struct ComplexHermitianEigensystem
32{
33 std::vector<Real> eigenvalues;
35};
36
45
48template <uni20::LapackReal Scalar> struct RealLuFactorization
49{
51 std::vector<std::size_t> pivot_rows;
52};
53
60template <uni20::LapackReal Scalar> struct RealGeneralBandMatrix
61{
63 std::size_t order = 0;
64 std::size_t lower_bandwidth = 0;
65 std::size_t upper_bandwidth = 0;
66};
67
70template <uni20::LapackReal Scalar> struct RealGeneralBandFactorization
71{
73 std::vector<std::size_t> pivot_rows;
74};
75
80template <uni20::LapackReal Scalar> struct RealGeneralTridiagonalMatrix
81{
82 std::vector<Scalar> lower_diagonal;
83 std::vector<Scalar> diagonal;
84 std::vector<Scalar> upper_diagonal;
85
86 [[nodiscard]] std::size_t order() const noexcept { return diagonal.size(); }
87};
88
94template <uni20::LapackReal Scalar> struct RealGeneralTridiagonalFactorization
95{
97 std::vector<Scalar> second_upper_diagonal;
98 std::vector<std::size_t> pivot_rows;
99};
100
112
126
140
143template <uni20::LapackReal Scalar> struct RealRefinedLinearSolve
144{
146 std::vector<Scalar> forward_error_bounds;
147 std::vector<Scalar> backward_error_bounds;
148};
149
152template <uni20::LapackReal Scalar> struct RealEquilibration
153{
154 std::vector<Scalar> row_scale;
155 std::vector<Scalar> column_scale;
159};
160
174
177template <uni20::LapackReal Scalar> struct RealSymmetricPositiveDefiniteEquilibration
178{
179 std::vector<Scalar> scale;
182};
183
186template <uni20::LapackReal Scalar> struct RealSymmetricPositiveDefiniteFactorization
187{
189 MatrixFill triangle = MatrixFill::Upper;
190};
191
198template <uni20::LapackReal Scalar> struct RealSymmetricPositiveDefiniteBandMatrix
199{
201 std::size_t order = 0;
202 std::size_t bandwidth = 0;
203 MatrixFill triangle = MatrixFill::Upper;
204};
205
212
226
232template <uni20::LapackReal Scalar> struct RealSymmetricPositiveDefiniteTridiagonalMatrix
233{
234 std::vector<Scalar> diagonal;
235 std::vector<Scalar> offdiagonal;
236
237 [[nodiscard]] std::size_t order() const noexcept { return diagonal.size(); }
238};
239
249
261
264template <uni20::LapackReal Scalar> struct RealPivotedCholeskyFactorization
265{
267 std::vector<std::size_t> pivot_order;
268 std::size_t rank = 0;
269 MatrixFill triangle = MatrixFill::Upper;
270 bool rank_deficient = false;
271};
272
275template <uni20::LapackReal Scalar> struct RealSymmetricIndefiniteFactorization
276{
278 std::vector<int> pivot_blocks;
279 MatrixFill triangle = MatrixFill::Upper;
280};
281
294
297template <uni20::LapackReal Scalar> struct RealRankRevealingLeastSquares
298{
300 std::size_t rank = 0;
301 std::vector<std::size_t> pivot_columns;
302};
303
306template <uni20::LapackReal Scalar> struct RealSvdLeastSquares
307{
309 std::size_t rank = 0;
310 std::vector<Scalar> singular_values;
311};
312
315template <uni20::LapackReal Scalar> struct RealSylvesterSolution
316{
320};
321
329
337
345
353
356template <uni20::LapackReal Scalar> struct RealCompactQrFactorization
357{
359 std::vector<Scalar> tau;
360 std::size_t rank = 0;
361};
362
365template <uni20::LapackReal Scalar> struct RealCompactLqFactorization
366{
368 std::vector<Scalar> tau;
369 std::size_t rank = 0;
370};
371
374template <uni20::LapackReal Scalar> struct RealCompactQlFactorization
375{
377 std::vector<Scalar> tau;
378 std::size_t rank = 0;
379};
380
383template <uni20::LapackReal Scalar> struct RealCompactRqFactorization
384{
386 std::vector<Scalar> tau;
387 std::size_t rank = 0;
388};
389
392template <uni20::LapackReal Scalar> struct RealBidiagonalReduction
393{
396 std::vector<Scalar> diagonal;
397 std::vector<Scalar> offdiagonal;
398 std::vector<Scalar> tauq;
399 std::vector<Scalar> taup;
400 bool upper = true;
401};
402
411
414template <uni20::LapackReal Scalar> struct RealHessenbergReduction
415{
418 std::vector<Scalar> tau;
419 std::size_t first = 0;
420 std::size_t last_exclusive = 0;
421};
422
425template <uni20::LapackReal Scalar> struct RealSymmetricTridiagonalReduction
426{
429 std::vector<Scalar> diagonal;
430 std::vector<Scalar> offdiagonal;
431 std::vector<Scalar> tau;
432 MatrixFill triangle = MatrixFill::Upper;
433};
434
443
446
458
461{
462 None,
463 Permute,
464 Scale,
465 Both
466};
467
470template <uni20::LapackReal Real> struct RealNonsymmetricBalance
471{
473 std::vector<Real> scale;
474 std::size_t balanced_first = 0;
475 std::size_t balanced_last_exclusive = 0;
476};
477
480template <uni20::LapackReal Real> struct RealGeneralizedNonsymmetricEigensystem
481{
482 std::vector<uni20::complex<Real>> alpha;
483 std::vector<Real> beta;
484 std::vector<uni20::complex<Real>> eigenvalues;
486};
487
490template <uni20::LapackReal Real> struct RealGeneralizedNonsymmetricExpertEigensystem
491{
492 std::vector<uni20::complex<Real>> alpha;
493 std::vector<Real> beta;
494 std::vector<uni20::complex<Real>> eigenvalues;
498 std::vector<Real> left_balance_scale;
499 std::vector<Real> right_balance_scale;
502 std::size_t balanced_first = 0;
503 std::size_t balanced_last_exclusive = 0;
504};
505
517
529
532
545
557
565
568template <uni20::LapackReal Real> struct RealGeneralizedSchurConditionEstimates
569{
572 std::size_t computed_estimates = 0;
573};
574
577
585
593
596template <uni20::LapackReal Scalar> struct RealSchurConditionEstimates
597{
600 std::size_t computed_estimates = 0;
601};
602
603namespace detail
604{
605inline char lapack_uplo(MatrixFill fill)
606{
607 switch (fill)
608 {
609 case MatrixFill::Upper:
610 return 'U';
611 case MatrixFill::Lower:
612 return 'L';
613 case MatrixFill::All:
614 throw std::invalid_argument("dense symmetric LAPACK operation requires an upper or lower triangle selector");
615 }
616 throw std::invalid_argument("dense symmetric LAPACK operation received an unknown triangle selector");
617}
618
619template <uni20::LapackReal Scalar>
620Scalar symmetric_matrix_one_norm(detail::ColumnMajorLapackMatrix<Scalar> const& matrix, MatrixFill triangle)
621{
622 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
623 {
624 throw std::invalid_argument("symmetric_matrix_one_norm requires a square matrix");
625 }
626 if (triangle == MatrixFill::All)
627 {
628 throw std::invalid_argument("symmetric_matrix_one_norm requires an upper or lower triangle selector");
629 }
630
631 Scalar norm{};
632 for (uni20::index_type col = 0; col < matrix.cols(); ++col)
633 {
634 Scalar column_sum{};
635 for (uni20::index_type row = 0; row < matrix.rows(); ++row)
636 {
637 if (triangle == MatrixFill::Upper)
638 {
639 column_sum += row <= col ? uni20::math::abs(matrix[row, col]) : uni20::math::abs(matrix[col, row]);
640 }
641 else
642 {
643 column_sum += row >= col ? uni20::math::abs(matrix[row, col]) : uni20::math::abs(matrix[col, row]);
644 }
645 }
646 norm = std::max(norm, column_sum);
647 }
648 return norm;
649}
650
651template <typename Scalar>
652void mirror_selected_symmetric_triangle(detail::ColumnMajorLapackMatrix<Scalar>& matrix, MatrixFill triangle)
653{
654 if (triangle == MatrixFill::All)
655 {
656 throw std::invalid_argument("symmetric triangle mirroring requires an upper or lower triangle selector");
657 }
658 for (uni20::index_type col = 0; col < matrix.cols(); ++col)
659 {
660 for (uni20::index_type row = 0; row < matrix.rows(); ++row)
661 {
662 if (triangle == MatrixFill::Upper && row > col)
663 {
664 matrix[row, col] = matrix[col, row];
665 }
666 if (triangle == MatrixFill::Lower && row < col)
667 {
668 matrix[row, col] = matrix[col, row];
669 }
670 }
671 }
672}
673
674template <uni20::LapackReal Scalar>
675std::vector<blas_int>
676checked_symmetric_indefinite_pivots(RealSymmetricIndefiniteFactorization<Scalar> const& factorization)
677{
678 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
679 std::vector<blas_int> pivots(n);
680 for (std::size_t index = 0; index < n; ++index)
681 {
682 int const pivot = factorization.pivot_blocks[index];
683 std::size_t const pivot_magnitude =
684 pivot < 0 ? static_cast<std::size_t>(-(pivot + 1)) + std::size_t{1} : static_cast<std::size_t>(pivot);
685 if (pivot_magnitude == 0 || pivot_magnitude > n)
686 {
687 throw std::invalid_argument("symmetric-indefinite factorization has an out-of-range pivot block");
688 }
689 pivots[index] = static_cast<blas_int>(pivot);
690 }
691 return pivots;
692}
693
694inline char lapack_transpose(MatrixTranspose transpose)
695{
696 switch (transpose)
697 {
699 return 'N';
701 return 'T';
703 return 'C';
704 }
705 throw std::invalid_argument("dense real LAPACK operation received an unknown transpose selector");
706}
707
708inline char lapack_side(MatrixSide side)
709{
710 switch (side)
711 {
712 case MatrixSide::Left:
713 return 'L';
715 return 'R';
716 }
717 throw std::invalid_argument("dense real LAPACK operation received an unknown side selector");
718}
719
720inline char lapack_balance_job(RealNonsymmetricBalanceJob job)
721{
722 switch (job)
723 {
725 return 'N';
727 return 'P';
729 return 'S';
731 return 'B';
732 }
733 throw std::invalid_argument("dense real LAPACK operation received an unknown balance job");
734}
735} // namespace detail
736
737template <uni20::LapackReal Scalar>
739{
740 if (matrix.rows() == 0 || matrix.cols() == 0)
741 {
742 return Scalar{};
743 }
744
745 blas_int const rows = detail::checked_blas_int(matrix.rows());
746 blas_int const cols = detail::checked_blas_int(matrix.cols());
747 blas_int const lda = std::max<blas_int>(1, rows);
748 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, rows)), Scalar{});
749 return uni20::lapack::lange(detail::lapack_norm(norm), rows, cols, matrix.data(), lda, work.data());
750}
751
759template <uni20::LapackReal Scalar>
761 MatrixFill triangle = MatrixFill::Upper)
762{
763 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
764 {
765 throw std::invalid_argument("real_symmetric_matrix_norm requires a square matrix");
766 }
767 if (matrix.rows() == 0)
768 {
769 return Scalar{};
770 }
771
772 blas_int const order = detail::checked_blas_int(matrix.rows());
773 blas_int const lda = std::max<blas_int>(1, order);
774 char const uplo = detail::lapack_uplo(triangle);
775 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, order)), Scalar{});
776 return uni20::lapack::lansy(detail::lapack_norm(norm), uplo, order, matrix.data(), lda, work.data());
777}
778
789template <uni20::LapackReal Scalar>
791 MatrixFill triangle = MatrixFill::Upper, bool unit_diagonal = false)
792{
793 if (matrix.rows() == 0 || matrix.cols() == 0)
794 {
795 return Scalar{};
796 }
797
798 blas_int const rows = detail::checked_blas_int(matrix.rows());
799 blas_int const cols = detail::checked_blas_int(matrix.cols());
800 blas_int const lda = std::max<blas_int>(1, rows);
801 char const uplo = detail::lapack_uplo(triangle);
802 char const diag = unit_diagonal ? 'U' : 'N';
803 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, rows)), Scalar{});
804 return uni20::lapack::lantr(detail::lapack_norm(norm), uplo, diag, rows, cols, matrix.data(), lda, work.data());
805}
806
816template <uni20::LapackReal Scalar>
818 std::size_t lower_bandwidth, std::size_t upper_bandwidth)
819{
820 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
821 {
822 throw std::invalid_argument("real_general_band_from_dense requires a square matrix");
823 }
824
825 std::size_t const n = static_cast<std::size_t>(matrix.rows());
827 result.order = n;
828 result.lower_bandwidth = lower_bandwidth;
829 result.upper_bandwidth = upper_bandwidth;
830 result.storage = detail::ColumnMajorLapackMatrix<Scalar>(2 * lower_bandwidth + upper_bandwidth + 1, n);
831
832 for (std::size_t col = 0; col < n; ++col)
833 {
834 std::size_t const first_row = col > upper_bandwidth ? col - upper_bandwidth : 0;
835 std::size_t const last_row = std::min(n - 1, col + lower_bandwidth);
836 for (std::size_t row = first_row; row <= last_row; ++row)
837 {
838 std::size_t const band_row = lower_bandwidth + upper_bandwidth + row - col;
839 result.storage[band_row, col] = matrix[row, col];
840 }
841 }
842 return result;
843}
844
853template <uni20::LapackReal Scalar>
855{
856 if (band_matrix.order == 0)
857 {
858 return Scalar{};
859 }
860 if (!std::cmp_equal(band_matrix.storage.cols(), band_matrix.order) ||
861 std::cmp_less(band_matrix.storage.rows(), 2 * band_matrix.lower_bandwidth + band_matrix.upper_bandwidth + 1))
862 {
863 throw std::invalid_argument("real_general_band_matrix_norm received inconsistent band storage");
864 }
865
866 blas_int const n = detail::checked_blas_int(band_matrix.order);
867 blas_int const kl = detail::checked_blas_int(band_matrix.lower_bandwidth);
868 blas_int const ku = detail::checked_blas_int(band_matrix.upper_bandwidth);
869 blas_int const ldab = detail::checked_blas_int(band_matrix.storage.rows());
870 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, n)), Scalar{});
871 Scalar* compact_band = band_matrix.storage.data() + band_matrix.lower_bandwidth;
872 return uni20::lapack::langb(detail::lapack_norm(norm), n, kl, ku, compact_band, ldab, work.data());
873}
874
882template <uni20::LapackReal Scalar>
883detail::ColumnMajorLapackMatrix<Scalar>
886{
887 if (!std::cmp_equal(coefficients.storage.cols(), coefficients.order) ||
888 std::cmp_less(coefficients.storage.rows(), 2 * coefficients.lower_bandwidth + coefficients.upper_bandwidth + 1))
889 {
890 throw std::invalid_argument("real_general_band_solve received inconsistent band storage");
891 }
892 if (!std::cmp_equal(right_hand_sides.rows(), coefficients.order))
893 {
894 throw std::invalid_argument("real_general_band_solve received incompatible right-hand sides");
895 }
896 if (coefficients.order == 0 || right_hand_sides.cols() == 0)
897 {
898 return right_hand_sides;
899 }
900
901 blas_int const n = detail::checked_blas_int(coefficients.order);
902 blas_int const kl = detail::checked_blas_int(coefficients.lower_bandwidth);
903 blas_int const ku = detail::checked_blas_int(coefficients.upper_bandwidth);
904 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
905 blas_int const ldab = detail::checked_blas_int(coefficients.storage.rows());
906 std::vector<blas_int> pivots(coefficients.order);
907 uni20::lapack::gbsv(n, kl, ku, nrhs, coefficients.storage.data(), ldab, pivots.data(), right_hand_sides.data(), n);
908 return right_hand_sides;
909}
910
915template <uni20::LapackReal Scalar>
917{
918 if (!std::cmp_equal(matrix.storage.cols(), matrix.order) ||
919 std::cmp_less(matrix.storage.rows(), 2 * matrix.lower_bandwidth + matrix.upper_bandwidth + 1))
920 {
921 throw std::invalid_argument("real_general_band_factorization received inconsistent band storage");
922 }
923
925 result.factors = std::move(matrix);
926 result.pivot_rows.resize(result.factors.order);
927 std::iota(result.pivot_rows.begin(), result.pivot_rows.end(), std::size_t{0});
928 if (result.factors.order == 0)
929 {
930 return result;
931 }
932
933 blas_int const n = detail::checked_blas_int(result.factors.order);
934 blas_int const kl = detail::checked_blas_int(result.factors.lower_bandwidth);
935 blas_int const ku = detail::checked_blas_int(result.factors.upper_bandwidth);
936 blas_int const ldab = detail::checked_blas_int(result.factors.storage.rows());
937 std::vector<blas_int> pivots(result.factors.order);
938 uni20::lapack::gbtrf(n, n, kl, ku, result.factors.storage.data(), ldab, pivots.data());
939 for (std::size_t index = 0; index < result.factors.order; ++index)
940 {
941 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > result.factors.order)
942 {
943 throw std::runtime_error("LAPACK gbtrf returned an invalid row pivot");
944 }
945 result.pivot_rows[index] = static_cast<std::size_t>(pivots[index] - 1);
946 }
947 return result;
948}
949
956template <uni20::LapackReal Scalar>
957detail::ColumnMajorLapackMatrix<Scalar>
961{
962 if (!std::cmp_equal(factorization.factors.storage.cols(), factorization.factors.order) ||
963 std::cmp_less(factorization.factors.storage.rows(),
964 2 * factorization.factors.lower_bandwidth + factorization.factors.upper_bandwidth + 1))
965 {
966 throw std::invalid_argument("real_general_band_solve received inconsistent band factors");
967 }
968 if (factorization.pivot_rows.size() != factorization.factors.order)
969 {
970 throw std::invalid_argument("real_general_band_solve received inconsistent pivot metadata");
971 }
972 if (!std::cmp_equal(right_hand_sides.rows(), factorization.factors.order))
973 {
974 throw std::invalid_argument("real_general_band_solve received incompatible right-hand sides");
975 }
976 if (factorization.factors.order == 0 || right_hand_sides.cols() == 0)
977 {
978 return right_hand_sides;
979 }
980
981 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors.storage;
982 std::vector<blas_int> pivots(factorization.factors.order);
983 for (std::size_t index = 0; index < factorization.factors.order; ++index)
984 {
985 if (factorization.pivot_rows[index] >= factorization.factors.order)
986 {
987 throw std::invalid_argument("real_general_band_solve received an out-of-range row pivot");
988 }
989 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
990 }
991
992 blas_int const n = detail::checked_blas_int(factorization.factors.order);
993 blas_int const kl = detail::checked_blas_int(factorization.factors.lower_bandwidth);
994 blas_int const ku = detail::checked_blas_int(factorization.factors.upper_bandwidth);
995 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
996 blas_int const ldab = detail::checked_blas_int(factors.rows());
997 char const trans = detail::lapack_transpose(transpose);
998 uni20::lapack::gbtrs(trans, n, kl, ku, nrhs, factors.data(), ldab, pivots.data(), right_hand_sides.data(), n);
999 return right_hand_sides;
1000}
1001
1008template <uni20::LapackReal Scalar>
1009RealGeneralTridiagonalMatrix<Scalar>
1011{
1012 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
1013 {
1014 throw std::invalid_argument("real_general_tridiagonal_from_dense requires a square matrix");
1015 }
1016
1018 result.diagonal.resize(matrix.rows());
1019 if (matrix.rows() > 0)
1020 {
1021 result.lower_diagonal.resize(matrix.rows() - 1);
1022 result.upper_diagonal.resize(matrix.rows() - 1);
1023 }
1024 for (uni20::index_type index = 0; index < matrix.rows(); ++index)
1025 {
1026 result.diagonal[index] = matrix[index, index];
1027 if (index + 1 < matrix.rows())
1028 {
1029 result.lower_diagonal[index] = matrix[index + 1, index];
1030 result.upper_diagonal[index] = matrix[index, index + 1];
1031 }
1032 }
1033 return result;
1034}
1035
1042template <uni20::LapackReal Scalar>
1043detail::ColumnMajorLapackMatrix<Scalar>
1046{
1047 std::size_t const order = coefficients.order();
1048 if (coefficients.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1049 coefficients.upper_diagonal.size() != (order == 0 ? 0 : order - 1))
1050 {
1051 throw std::invalid_argument("real_general_tridiagonal_solve received inconsistent tridiagonal storage");
1052 }
1053 if (!std::cmp_equal(right_hand_sides.rows(), order))
1054 {
1055 throw std::invalid_argument("real_general_tridiagonal_solve received incompatible right-hand sides");
1056 }
1057 if (order == 0 || right_hand_sides.cols() == 0)
1058 {
1059 return right_hand_sides;
1060 }
1061
1062 blas_int const n = detail::checked_blas_int(order);
1063 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
1064 Scalar* lower_diagonal =
1065 coefficients.lower_diagonal.empty() ? coefficients.diagonal.data() : coefficients.lower_diagonal.data();
1066 Scalar* upper_diagonal =
1067 coefficients.upper_diagonal.empty() ? coefficients.diagonal.data() : coefficients.upper_diagonal.data();
1068 uni20::lapack::gtsv(n, nrhs, lower_diagonal, coefficients.diagonal.data(), upper_diagonal, right_hand_sides.data(),
1069 n);
1070 return right_hand_sides;
1071}
1072
1077template <uni20::LapackReal Scalar>
1078RealGeneralTridiagonalFactorization<Scalar>
1080{
1081 std::size_t const order = matrix.order();
1082 if (matrix.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1083 matrix.upper_diagonal.size() != (order == 0 ? 0 : order - 1))
1084 {
1085 throw std::invalid_argument("real_general_tridiagonal_factorization received inconsistent tridiagonal storage");
1086 }
1087
1089 result.factors = std::move(matrix);
1090 result.second_upper_diagonal.resize(order > 2 ? order - 2 : 0);
1091 result.pivot_rows.resize(order);
1092 std::iota(result.pivot_rows.begin(), result.pivot_rows.end(), std::size_t{0});
1093 if (order == 0)
1094 {
1095 return result;
1096 }
1097
1098 blas_int const n = detail::checked_blas_int(order);
1099 std::vector<blas_int> pivots(order);
1100 Scalar* lower_diagonal =
1101 result.factors.lower_diagonal.empty() ? result.factors.diagonal.data() : result.factors.lower_diagonal.data();
1102 Scalar* upper_diagonal =
1103 result.factors.upper_diagonal.empty() ? result.factors.diagonal.data() : result.factors.upper_diagonal.data();
1104 Scalar* second_upper_diagonal =
1105 result.second_upper_diagonal.empty() ? result.factors.diagonal.data() : result.second_upper_diagonal.data();
1106 uni20::lapack::gttrf(n, lower_diagonal, result.factors.diagonal.data(), upper_diagonal, second_upper_diagonal,
1107 pivots.data());
1108 for (std::size_t index = 0; index < order; ++index)
1109 {
1110 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > order)
1111 {
1112 throw std::runtime_error("LAPACK gttrf returned an invalid row pivot");
1113 }
1114 result.pivot_rows[index] = static_cast<std::size_t>(pivots[index] - 1);
1115 }
1116 return result;
1117}
1118
1125template <uni20::LapackReal Scalar>
1126detail::ColumnMajorLapackMatrix<Scalar>
1130{
1131 std::size_t const order = factorization.factors.order();
1132 if (factorization.factors.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1133 factorization.factors.upper_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1134 factorization.second_upper_diagonal.size() != (order > 2 ? order - 2 : 0))
1135 {
1136 throw std::invalid_argument("real_general_tridiagonal_solve received inconsistent tridiagonal factors");
1137 }
1138 if (factorization.pivot_rows.size() != order)
1139 {
1140 throw std::invalid_argument("real_general_tridiagonal_solve received inconsistent pivot metadata");
1141 }
1142 if (!std::cmp_equal(right_hand_sides.rows(), order))
1143 {
1144 throw std::invalid_argument("real_general_tridiagonal_solve received incompatible right-hand sides");
1145 }
1146 if (order == 0 || right_hand_sides.cols() == 0)
1147 {
1148 return right_hand_sides;
1149 }
1150
1151 auto lower_diagonal_values = factorization.factors.lower_diagonal;
1152 auto diagonal_values = factorization.factors.diagonal;
1153 auto upper_diagonal_values = factorization.factors.upper_diagonal;
1154 auto second_upper_diagonal_values = factorization.second_upper_diagonal;
1155 std::vector<blas_int> pivots(order);
1156 for (std::size_t index = 0; index < order; ++index)
1157 {
1158 if (factorization.pivot_rows[index] >= order)
1159 {
1160 throw std::invalid_argument("real_general_tridiagonal_solve received an out-of-range row pivot");
1161 }
1162 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
1163 }
1164
1165 blas_int const n = detail::checked_blas_int(order);
1166 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
1167 char const trans = detail::lapack_transpose(transpose);
1168 Scalar* lower_diagonal = lower_diagonal_values.empty() ? diagonal_values.data() : lower_diagonal_values.data();
1169 Scalar* upper_diagonal = upper_diagonal_values.empty() ? diagonal_values.data() : upper_diagonal_values.data();
1170 Scalar* second_upper_diagonal =
1171 second_upper_diagonal_values.empty() ? diagonal_values.data() : second_upper_diagonal_values.data();
1172 uni20::lapack::gttrs(trans, n, nrhs, lower_diagonal, diagonal_values.data(), upper_diagonal, second_upper_diagonal,
1173 pivots.data(), right_hand_sides.data(), n);
1174 return right_hand_sides;
1175}
1176
1181template <uni20::LapackReal Scalar>
1183{
1184 std::size_t const order = matrix.order();
1185 if (matrix.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1186 matrix.upper_diagonal.size() != (order == 0 ? 0 : order - 1))
1187 {
1188 throw std::invalid_argument("real_general_tridiagonal_one_norm received inconsistent tridiagonal storage");
1189 }
1190
1191 Scalar norm{};
1192 for (std::size_t col = 0; col < order; ++col)
1193 {
1194 Scalar column_sum = uni20::math::abs(matrix.diagonal[col]);
1195 if (col > 0)
1196 {
1197 column_sum += uni20::math::abs(matrix.upper_diagonal[col - 1]);
1198 }
1199 if (col + 1 < order)
1200 {
1201 column_sum += uni20::math::abs(matrix.lower_diagonal[col]);
1202 }
1203 norm = std::max(norm, column_sum);
1204 }
1205 return norm;
1206}
1207
1216template <uni20::LapackReal Scalar>
1218 RealGeneralTridiagonalFactorization<Scalar> const& factorization, Scalar original_one_norm)
1219{
1220 std::size_t const order = factorization.factors.order();
1221 if (factorization.factors.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1222 factorization.factors.upper_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1223 factorization.second_upper_diagonal.size() != (order > 2 ? order - 2 : 0))
1224 {
1225 throw std::invalid_argument(
1226 "real_general_tridiagonal_one_norm_reciprocal_condition_number received inconsistent factors");
1227 }
1228 if (factorization.pivot_rows.size() != order)
1229 {
1230 throw std::invalid_argument(
1231 "real_general_tridiagonal_one_norm_reciprocal_condition_number received inconsistent pivots");
1232 }
1233 if (original_one_norm < Scalar{})
1234 {
1235 throw std::invalid_argument(
1236 "real_general_tridiagonal_one_norm_reciprocal_condition_number requires a nonnegative norm");
1237 }
1238 if (order == 0)
1239 {
1240 return Scalar{1};
1241 }
1242
1243 auto lower_diagonal_values = factorization.factors.lower_diagonal;
1244 auto diagonal_values = factorization.factors.diagonal;
1245 auto upper_diagonal_values = factorization.factors.upper_diagonal;
1246 auto second_upper_diagonal_values = factorization.second_upper_diagonal;
1247 std::vector<blas_int> pivots(order);
1248 for (std::size_t index = 0; index < order; ++index)
1249 {
1250 if (factorization.pivot_rows[index] >= order)
1251 {
1252 throw std::invalid_argument(
1253 "real_general_tridiagonal_one_norm_reciprocal_condition_number received an out-of-range pivot");
1254 }
1255 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
1256 }
1257
1258 blas_int const n = detail::checked_blas_int(order);
1259 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 2 * n)), Scalar{});
1260 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
1261 Scalar* lower_diagonal = lower_diagonal_values.empty() ? diagonal_values.data() : lower_diagonal_values.data();
1262 Scalar* upper_diagonal = upper_diagonal_values.empty() ? diagonal_values.data() : upper_diagonal_values.data();
1263 Scalar* second_upper_diagonal =
1264 second_upper_diagonal_values.empty() ? diagonal_values.data() : second_upper_diagonal_values.data();
1265 return uni20::lapack::gtcon('1', n, lower_diagonal, diagonal_values.data(), upper_diagonal, second_upper_diagonal,
1266 pivots.data(), original_one_norm, work.data(), iwork.data());
1267}
1268
1273template <uni20::LapackReal Scalar>
1280
1289template <uni20::LapackReal Scalar>
1290RealRefinedLinearSolve<Scalar>
1294{
1295 std::size_t const order = coefficients.order();
1296 if (coefficients.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1297 coefficients.upper_diagonal.size() != (order == 0 ? 0 : order - 1))
1298 {
1299 throw std::invalid_argument("real_general_tridiagonal_refined_solve received inconsistent tridiagonal storage");
1300 }
1301 if (!std::cmp_equal(right_hand_sides.rows(), order))
1302 {
1303 throw std::invalid_argument("real_general_tridiagonal_refined_solve received incompatible right-hand sides");
1304 }
1305
1306 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
1308 result.solution = right_hand_sides;
1309 result.forward_error_bounds.resize(rhs_count);
1310 result.backward_error_bounds.resize(rhs_count);
1311 if (order == 0 || rhs_count == 0)
1312 {
1313 return result;
1314 }
1315
1316 auto factorization = real_general_tridiagonal_factorization(coefficients);
1317 result.solution = real_general_tridiagonal_solve(factorization, result.solution, transpose);
1318
1319 std::vector<blas_int> pivots(order);
1320 for (std::size_t index = 0; index < order; ++index)
1321 {
1322 if (factorization.pivot_rows[index] >= order)
1323 {
1324 throw std::invalid_argument("real_general_tridiagonal_refined_solve received an out-of-range row pivot");
1325 }
1326 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
1327 }
1328
1329 blas_int const n = detail::checked_blas_int(order);
1330 blas_int const nrhs = detail::checked_blas_int(rhs_count);
1331 char const trans = detail::lapack_transpose(transpose);
1332 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * n)), Scalar{});
1333 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
1334 Scalar* lower_diagonal =
1335 coefficients.lower_diagonal.empty() ? coefficients.diagonal.data() : coefficients.lower_diagonal.data();
1336 Scalar* upper_diagonal =
1337 coefficients.upper_diagonal.empty() ? coefficients.diagonal.data() : coefficients.upper_diagonal.data();
1338 Scalar* factor_lower_diagonal = factorization.factors.lower_diagonal.empty()
1339 ? factorization.factors.diagonal.data()
1340 : factorization.factors.lower_diagonal.data();
1341 Scalar* factor_upper_diagonal = factorization.factors.upper_diagonal.empty()
1342 ? factorization.factors.diagonal.data()
1343 : factorization.factors.upper_diagonal.data();
1344 Scalar* factor_second_upper_diagonal = factorization.second_upper_diagonal.empty()
1345 ? factorization.factors.diagonal.data()
1346 : factorization.second_upper_diagonal.data();
1347 uni20::lapack::gtrfs(trans, n, nrhs, lower_diagonal, coefficients.diagonal.data(), upper_diagonal,
1348 factor_lower_diagonal, factorization.factors.diagonal.data(), factor_upper_diagonal,
1349 factor_second_upper_diagonal, pivots.data(), right_hand_sides.data(), n, result.solution.data(),
1350 n, result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(),
1351 iwork.data());
1352 return result;
1353}
1354
1366template <uni20::LapackReal Scalar>
1367RealGeneralTridiagonalExpertLinearSolve<Scalar>
1371{
1372 std::size_t const order = coefficients.order();
1373 if (coefficients.lower_diagonal.size() != (order == 0 ? 0 : order - 1) ||
1374 coefficients.upper_diagonal.size() != (order == 0 ? 0 : order - 1))
1375 {
1376 throw std::invalid_argument(
1377 "real_general_tridiagonal_expert_linear_solve received inconsistent tridiagonal storage");
1378 }
1379 if (!std::cmp_equal(right_hand_sides.rows(), order))
1380 {
1381 throw std::invalid_argument("real_general_tridiagonal_expert_linear_solve received incompatible right-hand sides");
1382 }
1383
1384 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
1386 result.solution = detail::ColumnMajorLapackMatrix<Scalar>(right_hand_sides.rows(), rhs_count);
1387 result.factorization.factors.diagonal.resize(order);
1388 result.factorization.factors.lower_diagonal.resize(order == 0 ? 0 : order - 1);
1389 result.factorization.factors.upper_diagonal.resize(order == 0 ? 0 : order - 1);
1390 result.factorization.second_upper_diagonal.resize(order > 2 ? order - 2 : 0);
1391 result.factorization.pivot_rows.resize(order);
1392 std::iota(result.factorization.pivot_rows.begin(), result.factorization.pivot_rows.end(), std::size_t{0});
1393 result.forward_error_bounds.resize(rhs_count);
1394 result.backward_error_bounds.resize(rhs_count);
1395 if (order == 0 || rhs_count == 0)
1396 {
1397 return result;
1398 }
1399
1400 std::vector<blas_int> pivots(order);
1401 blas_int const n = detail::checked_blas_int(order);
1402 blas_int const nrhs = detail::checked_blas_int(rhs_count);
1403 char const trans = detail::lapack_transpose(transpose);
1404 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * n)), Scalar{});
1405 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
1406 Scalar* lower_diagonal =
1407 coefficients.lower_diagonal.empty() ? coefficients.diagonal.data() : coefficients.lower_diagonal.data();
1408 Scalar* upper_diagonal =
1409 coefficients.upper_diagonal.empty() ? coefficients.diagonal.data() : coefficients.upper_diagonal.data();
1410 Scalar* factor_lower_diagonal = result.factorization.factors.lower_diagonal.empty()
1411 ? result.factorization.factors.diagonal.data()
1412 : result.factorization.factors.lower_diagonal.data();
1413 Scalar* factor_upper_diagonal = result.factorization.factors.upper_diagonal.empty()
1414 ? result.factorization.factors.diagonal.data()
1415 : result.factorization.factors.upper_diagonal.data();
1416 Scalar* factor_second_upper_diagonal = result.factorization.second_upper_diagonal.empty()
1417 ? result.factorization.factors.diagonal.data()
1418 : result.factorization.second_upper_diagonal.data();
1420 'N', trans, n, nrhs, lower_diagonal, coefficients.diagonal.data(), upper_diagonal, factor_lower_diagonal,
1421 result.factorization.factors.diagonal.data(), factor_upper_diagonal, factor_second_upper_diagonal, pivots.data(),
1422 right_hand_sides.data(), n, result.solution.data(), n, result.reciprocal_condition,
1423 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(), iwork.data());
1424 for (std::size_t index = 0; index < order; ++index)
1425 {
1426 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > order)
1427 {
1428 throw std::runtime_error("LAPACK gtsvx returned an invalid row pivot");
1429 }
1430 result.factorization.pivot_rows[index] = static_cast<std::size_t>(pivots[index] - 1);
1431 }
1432 return result;
1433}
1434
1442template <uni20::LapackReal Scalar>
1444 Scalar original_one_norm)
1445{
1446 if (!std::cmp_equal(factorization.factors.storage.cols(), factorization.factors.order) ||
1447 std::cmp_less(factorization.factors.storage.rows(),
1448 2 * factorization.factors.lower_bandwidth + factorization.factors.upper_bandwidth + 1))
1449 {
1450 throw std::invalid_argument("real_general_band_one_norm_reciprocal_condition_number received inconsistent factors");
1451 }
1452 if (factorization.pivot_rows.size() != factorization.factors.order)
1453 {
1454 throw std::invalid_argument("real_general_band_one_norm_reciprocal_condition_number received inconsistent pivots");
1455 }
1456 if (original_one_norm < Scalar{})
1457 {
1458 throw std::invalid_argument("real_general_band_one_norm_reciprocal_condition_number requires a nonnegative norm");
1459 }
1460 if (factorization.factors.order == 0)
1461 {
1462 return Scalar{1};
1463 }
1464
1465 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors.storage;
1466 std::vector<blas_int> pivots(factorization.factors.order);
1467 for (std::size_t index = 0; index < factorization.factors.order; ++index)
1468 {
1469 if (factorization.pivot_rows[index] >= factorization.factors.order)
1470 {
1471 throw std::invalid_argument(
1472 "real_general_band_one_norm_reciprocal_condition_number received an out-of-range pivot");
1473 }
1474 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
1475 }
1476
1477 blas_int const n = detail::checked_blas_int(factorization.factors.order);
1478 blas_int const kl = detail::checked_blas_int(factorization.factors.lower_bandwidth);
1479 blas_int const ku = detail::checked_blas_int(factorization.factors.upper_bandwidth);
1480 blas_int const ldab = detail::checked_blas_int(factors.rows());
1481 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * n)), Scalar{});
1482 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
1483 return uni20::lapack::gbcon('1', n, kl, ku, factors.data(), ldab, pivots.data(), original_one_norm, work.data(),
1484 iwork.data());
1485}
1486
1494template <uni20::LapackReal Scalar>
1501
1512template <uni20::LapackReal Scalar>
1516{
1517 if (!std::cmp_equal(coefficients.storage.cols(), coefficients.order) ||
1518 std::cmp_less(coefficients.storage.rows(), 2 * coefficients.lower_bandwidth + coefficients.upper_bandwidth + 1))
1519 {
1520 throw std::invalid_argument("real_general_band_refined_solve received inconsistent band storage");
1521 }
1522 if (!std::cmp_equal(right_hand_sides.rows(), coefficients.order))
1523 {
1524 throw std::invalid_argument("real_general_band_refined_solve received incompatible right-hand sides");
1525 }
1526
1527 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
1529 result.solution = right_hand_sides;
1530 result.forward_error_bounds.resize(rhs_count);
1531 result.backward_error_bounds.resize(rhs_count);
1532 if (coefficients.order == 0 || rhs_count == 0)
1533 {
1534 return result;
1535 }
1536
1537 auto factorization = real_general_band_factorization(coefficients);
1538 std::vector<blas_int> pivots(factorization.factors.order);
1539 for (std::size_t index = 0; index < factorization.factors.order; ++index)
1540 {
1541 if (factorization.pivot_rows[index] >= factorization.factors.order)
1542 {
1543 throw std::invalid_argument("real_general_band_refined_solve received an out-of-range row pivot");
1544 }
1545 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
1546 }
1547
1548 blas_int const n = detail::checked_blas_int(coefficients.order);
1549 blas_int const kl = detail::checked_blas_int(coefficients.lower_bandwidth);
1550 blas_int const ku = detail::checked_blas_int(coefficients.upper_bandwidth);
1551 blas_int const nrhs = detail::checked_blas_int(rhs_count);
1552 blas_int const ldab = detail::checked_blas_int(coefficients.storage.rows());
1553 blas_int const ldafb = detail::checked_blas_int(factorization.factors.storage.rows());
1554 char const trans = detail::lapack_transpose(transpose);
1555
1556 uni20::lapack::gbtrs(trans, n, kl, ku, nrhs, factorization.factors.storage.data(), ldafb, pivots.data(),
1557 result.solution.data(), n);
1558
1559 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * n)), Scalar{});
1560 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
1561 Scalar* compact_band = coefficients.storage.data() + coefficients.lower_bandwidth;
1562 uni20::lapack::gbrfs(trans, n, kl, ku, nrhs, compact_band, ldab, factorization.factors.storage.data(), ldafb,
1563 pivots.data(), right_hand_sides.data(), n, result.solution.data(), n,
1564 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(),
1565 iwork.data());
1566 return result;
1567}
1568
1580template <uni20::LapackReal Scalar>
1581RealGeneralBandExpertLinearSolve<Scalar>
1585{
1586 if (!std::cmp_equal(coefficients.storage.cols(), coefficients.order) ||
1587 std::cmp_less(coefficients.storage.rows(), 2 * coefficients.lower_bandwidth + coefficients.upper_bandwidth + 1))
1588 {
1589 throw std::invalid_argument("real_general_band_expert_linear_solve received inconsistent band storage");
1590 }
1591 if (!std::cmp_equal(right_hand_sides.rows(), coefficients.order))
1592 {
1593 throw std::invalid_argument("real_general_band_expert_linear_solve received incompatible right-hand sides");
1594 }
1595
1596 std::size_t const n = coefficients.order;
1597 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
1600 result.factors.order = n;
1601 result.factors.lower_bandwidth = coefficients.lower_bandwidth;
1602 result.factors.upper_bandwidth = coefficients.upper_bandwidth;
1603 result.factors.storage =
1604 detail::ColumnMajorLapackMatrix<Scalar>(coefficients.storage.rows(), coefficients.storage.cols());
1605 result.pivot_rows.resize(n);
1606 std::iota(result.pivot_rows.begin(), result.pivot_rows.end(), std::size_t{0});
1607 result.forward_error_bounds.resize(rhs_count);
1608 result.backward_error_bounds.resize(rhs_count);
1609 if (n == 0 || rhs_count == 0)
1610 {
1611 return result;
1612 }
1613
1614 blas_int const order = detail::checked_blas_int(n);
1615 blas_int const kl = detail::checked_blas_int(coefficients.lower_bandwidth);
1616 blas_int const ku = detail::checked_blas_int(coefficients.upper_bandwidth);
1617 blas_int const nrhs = detail::checked_blas_int(rhs_count);
1618 blas_int const ldab = detail::checked_blas_int(coefficients.storage.rows());
1619 blas_int const ldafb = detail::checked_blas_int(result.factors.storage.rows());
1620 std::vector<blas_int> pivots(n, 0);
1621 std::vector<Scalar> row_scale(n, Scalar{});
1622 std::vector<Scalar> column_scale(n, Scalar{});
1623 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
1624 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
1625 char const trans = detail::lapack_transpose(transpose);
1626 char equed = 'N';
1627 Scalar rcond{};
1628 Scalar* compact_band = coefficients.storage.data() + coefficients.lower_bandwidth;
1629
1631 'N', trans, order, kl, ku, nrhs, compact_band, ldab, result.factors.storage.data(), ldafb, pivots.data(), equed,
1632 row_scale.data(), column_scale.data(), right_hand_sides.data(), order, result.solution.data(), order, rcond,
1633 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(), iwork.data());
1634 result.equilibration = equed;
1635 result.reciprocal_condition = rcond;
1636 for (std::size_t index = 0; index < n; ++index)
1637 {
1638 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > n)
1639 {
1640 throw std::runtime_error("LAPACK gbsvx returned an invalid row pivot");
1641 }
1642 result.pivot_rows[index] = static_cast<std::size_t>(pivots[index] - 1);
1643 }
1644 return result;
1645}
1646
1656template <uni20::LapackReal Scalar>
1658{
1659 if (!std::cmp_equal(band_matrix.storage.cols(), band_matrix.order) ||
1660 std::cmp_less(band_matrix.storage.rows(), 2 * band_matrix.lower_bandwidth + band_matrix.upper_bandwidth + 1))
1661 {
1662 throw std::invalid_argument("real_general_band_equilibration received inconsistent band storage");
1663 }
1664
1666 result.row_scale.assign(band_matrix.order, Scalar{1});
1667 result.column_scale.assign(band_matrix.order, Scalar{1});
1668 if (band_matrix.order == 0)
1669 {
1670 return result;
1671 }
1672
1673 blas_int const n = detail::checked_blas_int(band_matrix.order);
1674 blas_int const kl = detail::checked_blas_int(band_matrix.lower_bandwidth);
1675 blas_int const ku = detail::checked_blas_int(band_matrix.upper_bandwidth);
1676 blas_int const ldab = detail::checked_blas_int(band_matrix.storage.rows());
1677 Scalar* compact_band = band_matrix.storage.data() + band_matrix.lower_bandwidth;
1678 uni20::lapack::gbequ(false, n, n, kl, ku, compact_band, ldab, result.row_scale.data(), result.column_scale.data(),
1679 result.row_condition, result.column_condition, result.max_abs);
1680 return result;
1681}
1682
1690template <uni20::LapackReal Scalar>
1692{
1693 if (!std::cmp_equal(band_matrix.storage.cols(), band_matrix.order) ||
1694 std::cmp_less(band_matrix.storage.rows(), 2 * band_matrix.lower_bandwidth + band_matrix.upper_bandwidth + 1))
1695 {
1696 throw std::invalid_argument("real_general_band_power_of_two_equilibration received inconsistent band storage");
1697 }
1698
1700 result.row_scale.assign(band_matrix.order, Scalar{1});
1701 result.column_scale.assign(band_matrix.order, Scalar{1});
1702 if (band_matrix.order == 0)
1703 {
1704 return result;
1705 }
1706
1707 blas_int const n = detail::checked_blas_int(band_matrix.order);
1708 blas_int const kl = detail::checked_blas_int(band_matrix.lower_bandwidth);
1709 blas_int const ku = detail::checked_blas_int(band_matrix.upper_bandwidth);
1710 blas_int const ldab = detail::checked_blas_int(band_matrix.storage.rows());
1711 Scalar* compact_band = band_matrix.storage.data() + band_matrix.lower_bandwidth;
1712 uni20::lapack::gbequ(true, n, n, kl, ku, compact_band, ldab, result.row_scale.data(), result.column_scale.data(),
1713 result.row_condition, result.column_condition, result.max_abs);
1714 return result;
1715}
1716
1724template <uni20::LapackReal Scalar>
1726{
1728 result.row_scale.assign(matrix.rows(), Scalar{1});
1729 result.column_scale.assign(matrix.cols(), Scalar{1});
1730 if (matrix.rows() == 0 || matrix.cols() == 0)
1731 {
1732 return result;
1733 }
1734
1735 blas_int const rows = detail::checked_blas_int(matrix.rows());
1736 blas_int const cols = detail::checked_blas_int(matrix.cols());
1737 uni20::lapack::geequ(rows, cols, matrix.data(), rows, result.row_scale.data(), result.column_scale.data(),
1738 result.row_condition, result.column_condition, result.max_abs);
1739 return result;
1740}
1741
1749template <uni20::LapackReal Scalar>
1750RealSymmetricPositiveDefiniteEquilibration<Scalar>
1752{
1753 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
1754 {
1755 throw std::invalid_argument("real_symmetric_positive_definite_equilibration requires a square matrix");
1756 }
1757
1759 result.scale.assign(matrix.rows(), Scalar{1});
1760 if (matrix.rows() == 0)
1761 {
1762 return result;
1763 }
1764
1765 blas_int const order = detail::checked_blas_int(matrix.rows());
1766 uni20::lapack::poequ(order, matrix.data(), order, result.scale.data(), result.scale_condition, result.max_abs);
1767 return result;
1768}
1769
1777template <uni20::LapackReal Scalar>
1778RealSymmetricPositiveDefiniteFactorization<Scalar>
1780 MatrixFill triangle = MatrixFill::Upper)
1781{
1782 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
1783 {
1784 throw std::invalid_argument("real_symmetric_positive_definite_factorization requires a square matrix");
1785 }
1786
1788 result.factors = std::move(matrix);
1789 result.triangle = triangle;
1790 if (result.factors.rows() == 0)
1791 {
1792 return result;
1793 }
1794
1795 blas_int const order = detail::checked_blas_int(result.factors.rows());
1796 char const uplo = detail::lapack_uplo(triangle);
1797 uni20::lapack::potrf(uplo, order, result.factors.data(), order);
1798 return result;
1799}
1800
1810template <uni20::LapackReal Scalar>
1811RealSymmetricPositiveDefiniteBandMatrix<Scalar>
1813 std::size_t bandwidth, MatrixFill triangle = MatrixFill::Upper)
1814{
1815 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
1816 {
1817 throw std::invalid_argument("real_symmetric_positive_definite_band_from_dense requires a square matrix");
1818 }
1819 if (triangle != MatrixFill::Upper && triangle != MatrixFill::Lower)
1820 {
1821 throw std::invalid_argument("real_symmetric_positive_definite_band_from_dense requires upper or lower storage");
1822 }
1823
1824 std::size_t const n = static_cast<std::size_t>(matrix.rows());
1826 result.order = n;
1827 result.bandwidth = bandwidth;
1828 result.triangle = triangle;
1829 result.storage = detail::ColumnMajorLapackMatrix<Scalar>(bandwidth + 1, n);
1830
1831 for (std::size_t col = 0; col < n; ++col)
1832 {
1833 if (triangle == MatrixFill::Upper)
1834 {
1835 std::size_t const first_row = col > bandwidth ? col - bandwidth : 0;
1836 for (std::size_t row = first_row; row <= col; ++row)
1837 {
1838 result.storage[bandwidth + row - col, col] = matrix[row, col];
1839 }
1840 }
1841 else
1842 {
1843 std::size_t const last_row = std::min(n - 1, col + bandwidth);
1844 for (std::size_t row = col; row <= last_row; ++row)
1845 {
1846 result.storage[row - col, col] = matrix[row, col];
1847 }
1848 }
1849 }
1850 return result;
1851}
1852
1861template <uni20::LapackReal Scalar>
1862detail::ColumnMajorLapackMatrix<Scalar>
1865{
1866 if (!std::cmp_equal(coefficients.storage.cols(), coefficients.order) ||
1867 std::cmp_less(coefficients.storage.rows(), coefficients.bandwidth + 1))
1868 {
1869 throw std::invalid_argument("real_symmetric_positive_definite_band_solve received inconsistent band storage");
1870 }
1871 if (!std::cmp_equal(right_hand_sides.rows(), coefficients.order))
1872 {
1873 throw std::invalid_argument("real_symmetric_positive_definite_band_solve received incompatible right-hand sides");
1874 }
1875 if (coefficients.order == 0 || right_hand_sides.cols() == 0)
1876 {
1877 return right_hand_sides;
1878 }
1879
1880 blas_int const n = detail::checked_blas_int(coefficients.order);
1881 blas_int const kd = detail::checked_blas_int(coefficients.bandwidth);
1882 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
1883 blas_int const ldab = detail::checked_blas_int(coefficients.storage.rows());
1884 char const uplo = detail::lapack_uplo(coefficients.triangle);
1885 uni20::lapack::pbsv(uplo, n, kd, nrhs, coefficients.storage.data(), ldab, right_hand_sides.data(), n);
1886 return right_hand_sides;
1887}
1888
1893template <uni20::LapackReal Scalar>
1894RealSymmetricPositiveDefiniteBandFactorization<Scalar>
1896{
1897 if (!std::cmp_equal(matrix.storage.cols(), matrix.order) ||
1898 std::cmp_less(matrix.storage.rows(), matrix.bandwidth + 1))
1899 {
1900 throw std::invalid_argument(
1901 "real_symmetric_positive_definite_band_factorization received inconsistent band storage");
1902 }
1903 if (matrix.triangle != MatrixFill::Upper && matrix.triangle != MatrixFill::Lower)
1904 {
1905 throw std::invalid_argument("real_symmetric_positive_definite_band_factorization requires upper or lower storage");
1906 }
1907
1909 result.factors = std::move(matrix);
1910 if (result.factors.order == 0)
1911 {
1912 return result;
1913 }
1914
1915 blas_int const n = detail::checked_blas_int(result.factors.order);
1916 blas_int const kd = detail::checked_blas_int(result.factors.bandwidth);
1917 blas_int const ldab = detail::checked_blas_int(result.factors.storage.rows());
1918 char const uplo = detail::lapack_uplo(result.factors.triangle);
1919 uni20::lapack::pbtrf(uplo, n, kd, result.factors.storage.data(), ldab);
1920 return result;
1921}
1922
1928template <uni20::LapackReal Scalar>
1929detail::ColumnMajorLapackMatrix<Scalar>
1932{
1933 if (!std::cmp_equal(factorization.factors.storage.cols(), factorization.factors.order) ||
1934 std::cmp_less(factorization.factors.storage.rows(), factorization.factors.bandwidth + 1))
1935 {
1936 throw std::invalid_argument("real_symmetric_positive_definite_band_solve received inconsistent band factors");
1937 }
1938 if (!std::cmp_equal(right_hand_sides.rows(), factorization.factors.order))
1939 {
1940 throw std::invalid_argument("real_symmetric_positive_definite_band_solve received incompatible right-hand sides");
1941 }
1942 if (factorization.factors.order == 0 || right_hand_sides.cols() == 0)
1943 {
1944 return right_hand_sides;
1945 }
1946
1947 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors.storage;
1948 blas_int const n = detail::checked_blas_int(factorization.factors.order);
1949 blas_int const kd = detail::checked_blas_int(factorization.factors.bandwidth);
1950 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
1951 blas_int const ldab = detail::checked_blas_int(factors.rows());
1952 char const uplo = detail::lapack_uplo(factorization.factors.triangle);
1953 uni20::lapack::pbtrs(uplo, n, kd, nrhs, factors.data(), ldab, right_hand_sides.data(), n);
1954 return right_hand_sides;
1955}
1956
1961template <uni20::LapackReal Scalar>
1963{
1964 if (!std::cmp_equal(matrix.storage.cols(), matrix.order) ||
1965 std::cmp_less(matrix.storage.rows(), matrix.bandwidth + 1))
1966 {
1967 throw std::invalid_argument("real_symmetric_positive_definite_band_one_norm received inconsistent band storage");
1968 }
1969 if (matrix.triangle != MatrixFill::Upper && matrix.triangle != MatrixFill::Lower)
1970 {
1971 throw std::invalid_argument("real_symmetric_positive_definite_band_one_norm requires upper or lower storage");
1972 }
1973
1974 std::vector<Scalar> column_sums(matrix.order, Scalar{});
1975 for (std::size_t col = 0; col < matrix.order; ++col)
1976 {
1977 if (matrix.triangle == MatrixFill::Upper)
1978 {
1979 std::size_t const first_row = col > matrix.bandwidth ? col - matrix.bandwidth : 0;
1980 for (std::size_t row = first_row; row <= col; ++row)
1981 {
1982 Scalar const magnitude = uni20::math::abs(matrix.storage[matrix.bandwidth + row - col, col]);
1983 column_sums[col] += magnitude;
1984 if (row != col)
1985 {
1986 column_sums[row] += magnitude;
1987 }
1988 }
1989 }
1990 else
1991 {
1992 std::size_t const last_row = std::min(matrix.order - 1, col + matrix.bandwidth);
1993 for (std::size_t row = col; row <= last_row; ++row)
1994 {
1995 Scalar const magnitude = uni20::math::abs(matrix.storage[row - col, col]);
1996 column_sums[col] += magnitude;
1997 if (row != col)
1998 {
1999 column_sums[row] += magnitude;
2000 }
2001 }
2002 }
2003 }
2004
2005 Scalar norm{};
2006 for (Scalar const value : column_sums)
2007 {
2008 norm = std::max(norm, value);
2009 }
2010 return norm;
2011}
2012
2021template <uni20::LapackReal Scalar>
2023 RealSymmetricPositiveDefiniteBandFactorization<Scalar> const& factorization, Scalar original_one_norm)
2024{
2025 if (!std::cmp_equal(factorization.factors.storage.cols(), factorization.factors.order) ||
2026 std::cmp_less(factorization.factors.storage.rows(), factorization.factors.bandwidth + 1))
2027 {
2028 throw std::invalid_argument(
2029 "real_symmetric_positive_definite_band_one_norm_reciprocal_condition_number received inconsistent factors");
2030 }
2031 if (factorization.factors.triangle != MatrixFill::Upper && factorization.factors.triangle != MatrixFill::Lower)
2032 {
2033 throw std::invalid_argument(
2034 "real_symmetric_positive_definite_band_one_norm_reciprocal_condition_number requires upper or lower storage");
2035 }
2036 if (original_one_norm < Scalar{})
2037 {
2038 throw std::invalid_argument(
2039 "real_symmetric_positive_definite_band_one_norm_reciprocal_condition_number requires a nonnegative norm");
2040 }
2041 if (factorization.factors.order == 0)
2042 {
2043 return Scalar{1};
2044 }
2045
2046 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors.storage;
2047 blas_int const n = detail::checked_blas_int(factorization.factors.order);
2048 blas_int const kd = detail::checked_blas_int(factorization.factors.bandwidth);
2049 blas_int const ldab = detail::checked_blas_int(factors.rows());
2050 char const uplo = detail::lapack_uplo(factorization.factors.triangle);
2051 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * n)), Scalar{});
2052 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
2053 return uni20::lapack::pbcon(uplo, n, kd, factors.data(), ldab, original_one_norm, work.data(), iwork.data());
2054}
2055
2063template <uni20::LapackReal Scalar>
2071
2080template <uni20::LapackReal Scalar>
2081RealRefinedLinearSolve<Scalar>
2084{
2085 if (!std::cmp_equal(coefficients.storage.cols(), coefficients.order) ||
2086 std::cmp_less(coefficients.storage.rows(), coefficients.bandwidth + 1))
2087 {
2088 throw std::invalid_argument(
2089 "real_symmetric_positive_definite_band_refined_solve received inconsistent band storage");
2090 }
2091 if (!std::cmp_equal(right_hand_sides.rows(), coefficients.order))
2092 {
2093 throw std::invalid_argument(
2094 "real_symmetric_positive_definite_band_refined_solve received incompatible right-hand sides");
2095 }
2096
2097 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2099 result.solution = right_hand_sides;
2100 result.forward_error_bounds.resize(rhs_count);
2101 result.backward_error_bounds.resize(rhs_count);
2102 if (coefficients.order == 0 || rhs_count == 0)
2103 {
2104 return result;
2105 }
2106
2107 auto factorization = real_symmetric_positive_definite_band_factorization(coefficients);
2108 result.solution = real_symmetric_positive_definite_band_solve(factorization, result.solution);
2109
2110 blas_int const n = detail::checked_blas_int(coefficients.order);
2111 blas_int const kd = detail::checked_blas_int(coefficients.bandwidth);
2112 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2113 blas_int const ldab = detail::checked_blas_int(coefficients.storage.rows());
2114 blas_int const ldafb = detail::checked_blas_int(factorization.factors.storage.rows());
2115 char const uplo = detail::lapack_uplo(coefficients.triangle);
2116 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * n)), Scalar{});
2117 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, n)), 0);
2118
2119 uni20::lapack::pbrfs(uplo, n, kd, nrhs, coefficients.storage.data(), ldab, factorization.factors.storage.data(),
2120 ldafb, right_hand_sides.data(), n, result.solution.data(), n, result.forward_error_bounds.data(),
2121 result.backward_error_bounds.data(), work.data(), iwork.data());
2122 return result;
2123}
2124
2136template <uni20::LapackReal Scalar>
2137RealSymmetricPositiveDefiniteBandExpertLinearSolve<Scalar>
2140{
2141 if (!std::cmp_equal(coefficients.storage.cols(), coefficients.order) ||
2142 std::cmp_less(coefficients.storage.rows(), coefficients.bandwidth + 1))
2143 {
2144 throw std::invalid_argument(
2145 "real_symmetric_positive_definite_band_expert_linear_solve received inconsistent band storage");
2146 }
2147 if (!std::cmp_equal(right_hand_sides.rows(), coefficients.order))
2148 {
2149 throw std::invalid_argument(
2150 "real_symmetric_positive_definite_band_expert_linear_solve received incompatible right-hand sides");
2151 }
2152
2153 std::size_t const n = coefficients.order;
2154 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2157 result.factorization.factors.order = n;
2158 result.factorization.factors.bandwidth = coefficients.bandwidth;
2159 result.factorization.factors.triangle = coefficients.triangle;
2160 result.factorization.factors.storage =
2161 detail::ColumnMajorLapackMatrix<Scalar>(coefficients.storage.rows(), coefficients.storage.cols());
2162 result.forward_error_bounds.resize(rhs_count);
2163 result.backward_error_bounds.resize(rhs_count);
2164 result.scale.assign(n, Scalar{1});
2165 if (n == 0 || rhs_count == 0)
2166 {
2167 return result;
2168 }
2169
2170 blas_int const order = detail::checked_blas_int(n);
2171 blas_int const kd = detail::checked_blas_int(coefficients.bandwidth);
2172 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2173 blas_int const ldab = detail::checked_blas_int(coefficients.storage.rows());
2174 blas_int const ldafb = detail::checked_blas_int(result.factorization.factors.storage.rows());
2175 char const uplo = detail::lapack_uplo(coefficients.triangle);
2176 char equed = 'N';
2177 Scalar rcond{};
2178 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
2179 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
2180
2182 'N', uplo, order, kd, nrhs, coefficients.storage.data(), ldab, result.factorization.factors.storage.data(), ldafb,
2183 equed, result.scale.data(), right_hand_sides.data(), order, result.solution.data(), order, rcond,
2184 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(), iwork.data());
2185 result.equilibration = equed;
2186 result.reciprocal_condition = rcond;
2187 return result;
2188}
2189
2196template <uni20::LapackReal Scalar>
2197RealSymmetricPositiveDefiniteTridiagonalMatrix<Scalar>
2199{
2200 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
2201 {
2202 throw std::invalid_argument("real_symmetric_positive_definite_tridiagonal_from_dense requires a square matrix");
2203 }
2204
2206 result.diagonal.resize(matrix.rows());
2207 if (matrix.rows() > 0)
2208 {
2209 result.offdiagonal.resize(matrix.rows() - 1);
2210 }
2211 for (uni20::index_type index = 0; index < matrix.rows(); ++index)
2212 {
2213 result.diagonal[index] = matrix[index, index];
2214 if (index + 1 < matrix.rows())
2215 {
2216 result.offdiagonal[index] = matrix[index + 1, index];
2217 }
2218 }
2219 return result;
2220}
2221
2228template <uni20::LapackReal Scalar>
2229detail::ColumnMajorLapackMatrix<Scalar>
2232{
2233 std::size_t const order = coefficients.order();
2234 if (coefficients.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2235 {
2236 throw std::invalid_argument(
2237 "real_symmetric_positive_definite_tridiagonal_solve received inconsistent tridiagonal storage");
2238 }
2239 if (!std::cmp_equal(right_hand_sides.rows(), order))
2240 {
2241 throw std::invalid_argument(
2242 "real_symmetric_positive_definite_tridiagonal_solve received incompatible right-hand sides");
2243 }
2244 if (order == 0 || right_hand_sides.cols() == 0)
2245 {
2246 return right_hand_sides;
2247 }
2248
2249 blas_int const n = detail::checked_blas_int(order);
2250 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
2251 Scalar* offdiagonal =
2252 coefficients.offdiagonal.empty() ? coefficients.diagonal.data() : coefficients.offdiagonal.data();
2253 uni20::lapack::ptsv(n, nrhs, coefficients.diagonal.data(), offdiagonal, right_hand_sides.data(), n);
2254 return right_hand_sides;
2255}
2256
2261template <uni20::LapackReal Scalar>
2262RealSymmetricPositiveDefiniteTridiagonalFactorization<Scalar>
2265{
2266 std::size_t const order = matrix.order();
2267 if (matrix.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2268 {
2269 throw std::invalid_argument(
2270 "real_symmetric_positive_definite_tridiagonal_factorization received inconsistent tridiagonal storage");
2271 }
2272
2274 result.factors = std::move(matrix);
2275 if (order == 0)
2276 {
2277 return result;
2278 }
2279
2280 blas_int const n = detail::checked_blas_int(order);
2281 Scalar* offdiagonal =
2282 result.factors.offdiagonal.empty() ? result.factors.diagonal.data() : result.factors.offdiagonal.data();
2283 uni20::lapack::pttrf(n, result.factors.diagonal.data(), offdiagonal);
2284 return result;
2285}
2286
2292template <uni20::LapackReal Scalar>
2296{
2297 std::size_t const order = factorization.factors.order();
2298 if (factorization.factors.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2299 {
2300 throw std::invalid_argument(
2301 "real_symmetric_positive_definite_tridiagonal_solve received inconsistent tridiagonal factors");
2302 }
2303 if (!std::cmp_equal(right_hand_sides.rows(), order))
2304 {
2305 throw std::invalid_argument(
2306 "real_symmetric_positive_definite_tridiagonal_solve received incompatible right-hand sides");
2307 }
2308 if (order == 0 || right_hand_sides.cols() == 0)
2309 {
2310 return right_hand_sides;
2311 }
2312
2313 std::vector<Scalar> diagonal = factorization.factors.diagonal;
2314 std::vector<Scalar> offdiagonal = factorization.factors.offdiagonal;
2315 blas_int const n = detail::checked_blas_int(order);
2316 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
2317 Scalar* offdiagonal_data = offdiagonal.empty() ? diagonal.data() : offdiagonal.data();
2318 uni20::lapack::pttrs(n, nrhs, diagonal.data(), offdiagonal_data, right_hand_sides.data(), n);
2319 return right_hand_sides;
2320}
2321
2326template <uni20::LapackReal Scalar>
2329{
2330 std::size_t const order = matrix.order();
2331 if (matrix.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2332 {
2333 throw std::invalid_argument(
2334 "real_symmetric_positive_definite_tridiagonal_one_norm received inconsistent tridiagonal storage");
2335 }
2336
2337 Scalar norm{};
2338 for (std::size_t col = 0; col < order; ++col)
2339 {
2340 Scalar column_sum = uni20::math::abs(matrix.diagonal[col]);
2341 if (col > 0)
2342 {
2343 column_sum += uni20::math::abs(matrix.offdiagonal[col - 1]);
2344 }
2345 if (col + 1 < order)
2346 {
2347 column_sum += uni20::math::abs(matrix.offdiagonal[col]);
2348 }
2349 norm = std::max(norm, column_sum);
2350 }
2351 return norm;
2352}
2353
2362template <uni20::LapackReal Scalar>
2364 RealSymmetricPositiveDefiniteTridiagonalFactorization<Scalar> const& factorization, Scalar original_one_norm)
2365{
2366 std::size_t const order = factorization.factors.order();
2367 if (factorization.factors.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2368 {
2369 throw std::invalid_argument(
2370 "real_symmetric_positive_definite_tridiagonal_one_norm_reciprocal_condition_number received inconsistent "
2371 "factors");
2372 }
2373 if (original_one_norm < Scalar{})
2374 {
2375 throw std::invalid_argument("real_symmetric_positive_definite_tridiagonal_one_norm_reciprocal_condition_number "
2376 "requires a nonnegative norm");
2377 }
2378 if (order == 0)
2379 {
2380 return Scalar{1};
2381 }
2382
2383 std::vector<Scalar> diagonal = factorization.factors.diagonal;
2384 std::vector<Scalar> offdiagonal = factorization.factors.offdiagonal;
2385 blas_int const n = detail::checked_blas_int(order);
2386 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, n)), Scalar{});
2387 Scalar* offdiagonal_data = offdiagonal.empty() ? diagonal.data() : offdiagonal.data();
2388 return uni20::lapack::ptcon(n, diagonal.data(), offdiagonal_data, original_one_norm, work.data());
2389}
2390
2395template <uni20::LapackReal Scalar>
2403
2411template <uni20::LapackReal Scalar>
2415{
2416 std::size_t const order = coefficients.order();
2417 if (coefficients.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2418 {
2419 throw std::invalid_argument(
2420 "real_symmetric_positive_definite_tridiagonal_refined_solve received inconsistent tridiagonal storage");
2421 }
2422 if (!std::cmp_equal(right_hand_sides.rows(), order))
2423 {
2424 throw std::invalid_argument(
2425 "real_symmetric_positive_definite_tridiagonal_refined_solve received incompatible right-hand sides");
2426 }
2427
2428 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2430 result.solution = right_hand_sides;
2431 result.forward_error_bounds.resize(rhs_count);
2432 result.backward_error_bounds.resize(rhs_count);
2433 if (order == 0 || rhs_count == 0)
2434 {
2435 return result;
2436 }
2437
2438 auto factorization = real_symmetric_positive_definite_tridiagonal_factorization(coefficients);
2440
2441 blas_int const n = detail::checked_blas_int(order);
2442 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2443 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 2 * n)), Scalar{});
2444 Scalar* offdiagonal =
2445 coefficients.offdiagonal.empty() ? coefficients.diagonal.data() : coefficients.offdiagonal.data();
2446 Scalar* factor_offdiagonal = factorization.factors.offdiagonal.empty() ? factorization.factors.diagonal.data()
2447 : factorization.factors.offdiagonal.data();
2448 uni20::lapack::ptrfs(n, nrhs, coefficients.diagonal.data(), offdiagonal, factorization.factors.diagonal.data(),
2449 factor_offdiagonal, right_hand_sides.data(), n, result.solution.data(), n,
2450 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data());
2451 return result;
2452}
2453
2464template <uni20::LapackReal Scalar>
2465RealSymmetricPositiveDefiniteTridiagonalExpertLinearSolve<Scalar>
2469{
2470 std::size_t const order = coefficients.order();
2471 if (coefficients.offdiagonal.size() != (order == 0 ? 0 : order - 1))
2472 {
2473 throw std::invalid_argument(
2474 "real_symmetric_positive_definite_tridiagonal_expert_linear_solve received inconsistent tridiagonal storage");
2475 }
2476 if (!std::cmp_equal(right_hand_sides.rows(), order))
2477 {
2478 throw std::invalid_argument(
2479 "real_symmetric_positive_definite_tridiagonal_expert_linear_solve received incompatible right-hand sides");
2480 }
2481
2482 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2484 result.solution = detail::ColumnMajorLapackMatrix<Scalar>(right_hand_sides.rows(), rhs_count);
2485 result.factorization.factors.diagonal.resize(order);
2486 result.factorization.factors.offdiagonal.resize(order == 0 ? 0 : order - 1);
2487 result.forward_error_bounds.resize(rhs_count);
2488 result.backward_error_bounds.resize(rhs_count);
2489 if (order == 0 || rhs_count == 0)
2490 {
2491 return result;
2492 }
2493
2494 blas_int const n = detail::checked_blas_int(order);
2495 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2496 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 2 * n)), Scalar{});
2497 Scalar* offdiagonal =
2498 coefficients.offdiagonal.empty() ? coefficients.diagonal.data() : coefficients.offdiagonal.data();
2499 Scalar* factor_offdiagonal = result.factorization.factors.offdiagonal.empty()
2500 ? result.factorization.factors.diagonal.data()
2501 : result.factorization.factors.offdiagonal.data();
2503 'N', n, nrhs, coefficients.diagonal.data(), offdiagonal, result.factorization.factors.diagonal.data(),
2504 factor_offdiagonal, right_hand_sides.data(), n, result.solution.data(), n, result.reciprocal_condition,
2505 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data());
2506 return result;
2507}
2508
2521template <uni20::LapackReal Scalar>
2522RealPivotedCholeskyFactorization<Scalar>
2524 MatrixFill triangle = MatrixFill::Upper)
2525{
2526 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
2527 {
2528 throw std::invalid_argument("real_pivoted_cholesky_factorization requires a square matrix");
2529 }
2530
2531 std::size_t const n = static_cast<std::size_t>(matrix.rows());
2532 RealPivotedCholeskyFactorization<Scalar> result;
2533 result.factors = std::move(matrix);
2534 result.pivot_order.resize(n);
2535 std::iota(result.pivot_order.begin(), result.pivot_order.end(), std::size_t{0});
2536 result.triangle = triangle;
2537 result.rank = n;
2538 if (n == 0)
2539 {
2540 return result;
2541 }
2542
2543 blas_int const order = detail::checked_blas_int(n);
2544 char const uplo = detail::lapack_uplo(triangle);
2545 std::vector<blas_int> pivots(n, 0);
2546 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order)), Scalar{});
2547 blas_int rank = 0;
2548 result.rank_deficient =
2549 uni20::lapack::pstrf(uplo, order, result.factors.data(), order, pivots.data(), rank, tolerance, work.data());
2550 if (rank < 0 || rank > order)
2551 {
2552 throw std::runtime_error("LAPACK pstrf returned an invalid numerical rank");
2553 }
2554 result.rank = static_cast<std::size_t>(rank);
2555 for (std::size_t index = 0; index < n; ++index)
2556 {
2557 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > n)
2558 {
2559 throw std::runtime_error("LAPACK pstrf returned an invalid pivot order");
2560 }
2561 result.pivot_order[index] = static_cast<std::size_t>(pivots[index] - 1);
2562 }
2563 return result;
2564}
2565
2574template <uni20::LapackReal Scalar>
2575detail::ColumnMajorLapackMatrix<Scalar>
2578{
2579 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
2580 {
2581 throw std::invalid_argument("real_dense_solve_linear_system requires a square coefficient matrix");
2582 }
2583 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
2584 {
2585 throw std::invalid_argument("real_dense_solve_linear_system received incompatible right-hand sides");
2586 }
2587
2588 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
2589 if (n == 0)
2590 {
2591 return right_hand_sides;
2592 }
2593
2594 blas_int const order = detail::checked_blas_int(n);
2595 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
2596 std::vector<blas_int> pivots(n);
2597 uni20::lapack::gesv(order, nrhs, coefficients.data(), order, pivots.data(), right_hand_sides.data(), order);
2598 return right_hand_sides;
2599}
2600
2612template <uni20::LapackReal Scalar>
2615{
2616 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
2617 {
2618 throw std::invalid_argument("real_expert_linear_solve requires a square coefficient matrix");
2619 }
2620 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
2621 {
2622 throw std::invalid_argument("real_expert_linear_solve received incompatible right-hand sides");
2623 }
2624
2625 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
2626 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2630 result.pivot_rows.resize(n);
2631 std::iota(result.pivot_rows.begin(), result.pivot_rows.end(), std::size_t{0});
2632 result.forward_error_bounds.resize(rhs_count);
2633 result.backward_error_bounds.resize(rhs_count);
2634 if (n == 0 || rhs_count == 0)
2635 {
2636 return result;
2637 }
2638
2639 blas_int const order = detail::checked_blas_int(n);
2640 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2641 std::vector<blas_int> pivots(n, 0);
2642 std::vector<Scalar> row_scale(n, Scalar{});
2643 std::vector<Scalar> column_scale(n, Scalar{});
2644 std::vector<Scalar> work(static_cast<std::size_t>(4 * order), Scalar{});
2645 std::vector<blas_int> iwork(n, 0);
2646 char equed = 'N';
2647 Scalar rcond{};
2648
2650 'N', 'N', order, nrhs, coefficients.data(), order, result.factors.data(), order, pivots.data(), equed,
2651 row_scale.data(), column_scale.data(), right_hand_sides.data(), order, result.solution.data(), order, rcond,
2652 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(), iwork.data());
2653 result.equilibration = equed;
2654 result.reciprocal_condition = rcond;
2655 for (std::size_t index = 0; index < n; ++index)
2656 {
2657 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > n)
2658 {
2659 throw std::runtime_error("LAPACK gesvx returned an invalid row pivot");
2660 }
2661 result.pivot_rows[index] = static_cast<std::size_t>(pivots[index] - 1);
2662 }
2663 return result;
2664}
2665
2672template <uni20::LapackReal Scalar>
2674{
2675 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
2676 {
2677 throw std::invalid_argument("real_lu_factorization requires a square matrix");
2678 }
2679
2680 std::size_t const n = static_cast<std::size_t>(matrix.rows());
2682 result.factors = std::move(matrix);
2683 result.pivot_rows.resize(n);
2684 std::iota(result.pivot_rows.begin(), result.pivot_rows.end(), std::size_t{0});
2685 if (n == 0)
2686 {
2687 return result;
2688 }
2689
2690 blas_int const order = detail::checked_blas_int(n);
2691 std::vector<blas_int> pivots(n);
2692 uni20::lapack::getrf(order, order, result.factors.data(), order, pivots.data());
2693 for (std::size_t index = 0; index < n; ++index)
2694 {
2695 if (pivots[index] <= 0 || static_cast<std::size_t>(pivots[index]) > n)
2696 {
2697 throw std::runtime_error("LAPACK getrf returned an invalid row pivot");
2698 }
2699 result.pivot_rows[index] = static_cast<std::size_t>(pivots[index] - 1);
2700 }
2701 return result;
2702}
2703
2712template <uni20::LapackReal Scalar>
2716{
2717 if (!std::cmp_equal(factorization.factors.rows(), factorization.factors.cols()))
2718 {
2719 throw std::invalid_argument("real_lu_solve requires square LU factors");
2720 }
2721 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
2722 if (factorization.pivot_rows.size() != n)
2723 {
2724 throw std::invalid_argument("real_lu_solve received inconsistent pivot metadata");
2725 }
2726 if (!std::cmp_equal(right_hand_sides.rows(), n))
2727 {
2728 throw std::invalid_argument("real_lu_solve received incompatible right-hand sides");
2729 }
2730 if (n == 0 || right_hand_sides.cols() == 0)
2731 {
2732 return right_hand_sides;
2733 }
2734
2735 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors;
2736 std::vector<blas_int> pivots(n);
2737 for (std::size_t index = 0; index < n; ++index)
2738 {
2739 if (factorization.pivot_rows[index] >= n)
2740 {
2741 throw std::invalid_argument("real_lu_solve received an out-of-range row pivot");
2742 }
2743 pivots[index] = detail::checked_blas_int(factorization.pivot_rows[index] + 1);
2744 }
2745
2746 blas_int const order = detail::checked_blas_int(n);
2747 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
2748 char const trans = detail::lapack_transpose(transpose);
2749 uni20::lapack::getrs(trans, order, nrhs, factors.data(), order, pivots.data(), right_hand_sides.data(), order);
2750 return right_hand_sides;
2751}
2752
2763template <uni20::LapackReal Scalar>
2767{
2768 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
2769 {
2770 throw std::invalid_argument("real_refined_linear_solve requires a square coefficient matrix");
2771 }
2772 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
2773 {
2774 throw std::invalid_argument("real_refined_linear_solve received incompatible right-hand sides");
2775 }
2776
2777 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
2778 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2780 result.solution = right_hand_sides;
2781 result.forward_error_bounds.resize(rhs_count);
2782 result.backward_error_bounds.resize(rhs_count);
2783 if (n == 0 || rhs_count == 0)
2784 {
2785 return result;
2786 }
2787
2788 detail::ColumnMajorLapackMatrix<Scalar> factors = coefficients;
2789 blas_int const order = detail::checked_blas_int(n);
2790 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2791 std::vector<blas_int> pivots(n);
2792 uni20::lapack::getrf(order, order, factors.data(), order, pivots.data());
2793
2794 char const trans = detail::lapack_transpose(transpose);
2795 uni20::lapack::getrs(trans, order, nrhs, factors.data(), order, pivots.data(), result.solution.data(), order);
2796
2797 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
2798 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
2799 uni20::lapack::gerfs(trans, order, nrhs, coefficients.data(), order, factors.data(), order, pivots.data(),
2800 right_hand_sides.data(), order, result.solution.data(), order,
2801 result.forward_error_bounds.data(), result.backward_error_bounds.data(), work.data(),
2802 iwork.data());
2803 return result;
2804}
2805
2813template <uni20::LapackReal Scalar>
2815 Scalar original_one_norm)
2816{
2817 if (!std::cmp_equal(factorization.factors.rows(), factorization.factors.cols()))
2818 {
2819 throw std::invalid_argument("real_lu_one_norm_reciprocal_condition_number requires square LU factors");
2820 }
2821 if (original_one_norm < Scalar{})
2822 {
2823 throw std::invalid_argument("real_lu_one_norm_reciprocal_condition_number requires a nonnegative matrix norm");
2824 }
2825
2826 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
2827 if (n == 0)
2828 {
2829 return Scalar{1};
2830 }
2831
2832 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors;
2833 blas_int const order = detail::checked_blas_int(n);
2834 std::vector<Scalar> work(static_cast<std::size_t>(4 * order), Scalar{});
2835 std::vector<blas_int> iwork(n);
2836 return uni20::lapack::gecon('1', order, factors.data(), order, original_one_norm, work.data(), iwork.data());
2837}
2838
2845template <uni20::LapackReal Scalar>
2852
2865template <uni20::LapackReal Scalar>
2866detail::ColumnMajorLapackMatrix<Scalar>
2868 detail::ColumnMajorLapackMatrix<Scalar> right_hand_sides, MatrixFill triangle = MatrixFill::Upper,
2869 MatrixTranspose transpose = MatrixTranspose::None, bool unit_diagonal = false)
2870{
2871 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
2872 {
2873 throw std::invalid_argument("real_triangular_solve requires a square coefficient matrix");
2874 }
2875 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
2876 {
2877 throw std::invalid_argument("real_triangular_solve received incompatible right-hand sides");
2878 }
2879
2880 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
2881 if (n == 0 || right_hand_sides.cols() == 0)
2882 {
2883 return right_hand_sides;
2884 }
2885
2886 blas_int const order = detail::checked_blas_int(n);
2887 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
2888 char const uplo = detail::lapack_uplo(triangle);
2889 char const trans = detail::lapack_transpose(transpose);
2890 char const diag = unit_diagonal ? 'U' : 'N';
2891 uni20::lapack::trtrs(uplo, trans, diag, order, nrhs, coefficients.data(), order, right_hand_sides.data(), order);
2892 return right_hand_sides;
2893}
2894
2906template <uni20::LapackReal Scalar>
2909 MatrixFill triangle = MatrixFill::Upper,
2911 bool unit_diagonal = false)
2912{
2913 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
2914 {
2915 throw std::invalid_argument("real_triangular_refined_solve requires a square coefficient matrix");
2916 }
2917 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
2918 {
2919 throw std::invalid_argument("real_triangular_refined_solve received incompatible right-hand sides");
2920 }
2921
2922 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
2923 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
2925 result.solution = right_hand_sides;
2926 result.forward_error_bounds.resize(rhs_count);
2927 result.backward_error_bounds.resize(rhs_count);
2928 if (n == 0 || rhs_count == 0)
2929 {
2930 return result;
2931 }
2932
2933 blas_int const order = detail::checked_blas_int(n);
2934 blas_int const nrhs = detail::checked_blas_int(rhs_count);
2935 char const uplo = detail::lapack_uplo(triangle);
2936 char const trans = detail::lapack_transpose(transpose);
2937 char const diag = unit_diagonal ? 'U' : 'N';
2938 uni20::lapack::trtrs(uplo, trans, diag, order, nrhs, coefficients.data(), order, result.solution.data(), order);
2939
2940 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
2941 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
2942 uni20::lapack::trrfs(uplo, trans, diag, order, nrhs, coefficients.data(), order, right_hand_sides.data(), order,
2943 result.solution.data(), order, result.forward_error_bounds.data(),
2944 result.backward_error_bounds.data(), work.data(), iwork.data());
2945 return result;
2946}
2947
2957template <uni20::LapackReal Scalar>
2959 MatrixFill triangle = MatrixFill::Upper,
2960 bool unit_diagonal = false)
2961{
2962 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
2963 {
2964 throw std::invalid_argument("real_triangular_inverse requires a square matrix");
2965 }
2966
2967 std::size_t const n = static_cast<std::size_t>(matrix.rows());
2968 if (n == 0)
2969 {
2970 return matrix;
2971 }
2972
2973 blas_int const order = detail::checked_blas_int(n);
2974 char const uplo = detail::lapack_uplo(triangle);
2975 char const diag = unit_diagonal ? 'U' : 'N';
2976 uni20::lapack::trtri(uplo, diag, order, matrix.data(), order);
2977
2978 for (std::size_t row = 0; row < n; ++row)
2979 {
2980 for (std::size_t col = 0; col < n; ++col)
2981 {
2982 bool const selected = triangle == MatrixFill::Upper ? row <= col : row >= col;
2983 if (!selected)
2984 {
2985 matrix[row, col] = Scalar{};
2986 }
2987 }
2988 if (unit_diagonal)
2989 {
2990 matrix[row, row] = Scalar{1};
2991 }
2992 }
2993 return matrix;
2994}
2995
3005template <uni20::LapackReal Scalar>
3007 MatrixFill triangle = MatrixFill::Upper,
3008 bool unit_diagonal = false)
3009{
3010 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
3011 {
3012 throw std::invalid_argument("real_triangular_one_norm_reciprocal_condition_number requires a square matrix");
3013 }
3014
3015 std::size_t const n = static_cast<std::size_t>(matrix.rows());
3016 if (n == 0)
3017 {
3018 return Scalar{1};
3019 }
3020
3021 blas_int const order = detail::checked_blas_int(n);
3022 char const uplo = detail::lapack_uplo(triangle);
3023 char const diag = unit_diagonal ? 'U' : 'N';
3024 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
3025 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3026 return uni20::lapack::trcon('1', uplo, diag, order, matrix.data(), order, work.data(), iwork.data());
3027}
3028
3044template <uni20::LapackReal Scalar>
3048 MatrixTranspose transpose_left = MatrixTranspose::None,
3049 MatrixTranspose transpose_right = MatrixTranspose::None)
3050{
3051 if (!std::cmp_equal(left.rows(), left.cols()))
3052 {
3053 throw std::invalid_argument("real_sylvester_solve requires a square left matrix");
3054 }
3055 if (!std::cmp_equal(right.rows(), right.cols()))
3056 {
3057 throw std::invalid_argument("real_sylvester_solve requires a square right matrix");
3058 }
3059 if (!std::cmp_equal(rhs.rows(), left.rows()) || !std::cmp_equal(rhs.cols(), right.rows()))
3060 {
3061 throw std::invalid_argument("real_sylvester_solve received incompatible right-hand side dimensions");
3062 }
3063 if (sign != 1 && sign != -1)
3064 {
3065 throw std::invalid_argument("real_sylvester_solve sign must be +1 or -1");
3066 }
3067
3069 result.solution = std::move(rhs);
3070 if (left.rows() == 0 || right.rows() == 0)
3071 {
3072 return result;
3073 }
3074
3075 blas_int const m = detail::checked_blas_int(left.rows());
3076 blas_int const n = detail::checked_blas_int(right.rows());
3077 char const trans_a = detail::lapack_transpose(transpose_left);
3078 char const trans_b = detail::lapack_transpose(transpose_right);
3079 Scalar scale = Scalar{1};
3080 result.separation_perturbed = uni20::lapack::trsyl(trans_a, trans_b, static_cast<blas_int>(sign), m, n, left.data(),
3081 m, right.data(), n, result.solution.data(), m, scale);
3082 result.scale = scale;
3083 return result;
3084}
3085
3092template <uni20::LapackReal Scalar>
3094{
3095 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
3096 {
3097 throw std::invalid_argument("real_dense_inverse requires a square matrix");
3098 }
3099
3100 std::size_t const n = static_cast<std::size_t>(matrix.rows());
3101 if (n == 0)
3102 {
3103 return matrix;
3104 }
3105
3106 blas_int const order = detail::checked_blas_int(n);
3107 std::vector<blas_int> pivots(n);
3108 uni20::lapack::getrf(order, order, matrix.data(), order, pivots.data());
3109
3110 Scalar work_query{};
3111 blas_int const query_lwork = -1;
3112 uni20::lapack::getri(order, matrix.data(), order, pivots.data(), &work_query, query_lwork);
3113
3114 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3115 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3116 uni20::lapack::getri(order, matrix.data(), order, pivots.data(), work.data(), lwork);
3117
3118 return matrix;
3119}
3120
3129template <uni20::LapackReal Scalar>
3132{
3133 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3134 {
3135 throw std::invalid_argument("real_least_squares received incompatible right-hand sides");
3136 }
3137
3138 std::size_t const rows = static_cast<std::size_t>(coefficients.rows());
3139 std::size_t const cols = static_cast<std::size_t>(coefficients.cols());
3140 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3141 detail::ColumnMajorLapackMatrix<Scalar> solution(cols, rhs_count);
3142 if (cols == 0 || rhs_count == 0)
3143 {
3144 return solution;
3145 }
3146 if (rows == 0)
3147 {
3148 std::fill_n(solution.data(), solution.size(), Scalar{});
3149 return solution;
3150 }
3151
3152 std::size_t const workspace_rows = std::max(rows, cols);
3153 detail::ColumnMajorLapackMatrix<Scalar> rhs_workspace(workspace_rows, rhs_count);
3154 for (std::size_t row = 0; row < rows; ++row)
3155 {
3156 for (std::size_t col = 0; col < rhs_count; ++col)
3157 {
3158 rhs_workspace[row, col] = right_hand_sides[row, col];
3159 }
3160 }
3161
3162 blas_int const m = detail::checked_blas_int(rows);
3163 blas_int const n = detail::checked_blas_int(cols);
3164 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3165 blas_int const lda = std::max<blas_int>(1, m);
3166 blas_int const ldb = detail::checked_blas_int(workspace_rows);
3167 char const trans = 'N';
3168
3169 Scalar work_query{};
3170 blas_int const query_lwork = -1;
3171 uni20::lapack::gels(trans, m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, &work_query, query_lwork);
3172
3173 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3174 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3175 uni20::lapack::gels(trans, m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, work.data(), lwork);
3176
3177 for (std::size_t row = 0; row < cols; ++row)
3178 {
3179 for (std::size_t col = 0; col < rhs_count; ++col)
3180 {
3181 solution[row, col] = rhs_workspace[row, col];
3182 }
3183 }
3184 return solution;
3185}
3186
3196template <uni20::LapackReal Scalar>
3199 Scalar rcond = Scalar{100} *
3201{
3202 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3203 {
3204 throw std::invalid_argument("real_svd_least_squares received incompatible right-hand sides");
3205 }
3206
3207 std::size_t const rows = static_cast<std::size_t>(coefficients.rows());
3208 std::size_t const cols = static_cast<std::size_t>(coefficients.cols());
3209 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3210 RealSvdLeastSquares<Scalar> result;
3211 result.solution = detail::ColumnMajorLapackMatrix<Scalar>(cols, rhs_count);
3212 result.singular_values.resize(std::min(rows, cols));
3213 if (cols == 0 || rhs_count == 0)
3214 {
3215 return result;
3216 }
3217 if (rows == 0)
3218 {
3219 std::fill_n(result.solution.data(), result.solution.size(), Scalar{});
3220 return result;
3221 }
3222
3223 std::size_t const workspace_rows = std::max(rows, cols);
3224 detail::ColumnMajorLapackMatrix<Scalar> rhs_workspace(workspace_rows, rhs_count);
3225 for (std::size_t row = 0; row < rows; ++row)
3226 {
3227 for (std::size_t col = 0; col < rhs_count; ++col)
3228 {
3229 rhs_workspace[row, col] = right_hand_sides[row, col];
3230 }
3231 }
3232
3233 blas_int const m = detail::checked_blas_int(rows);
3234 blas_int const n = detail::checked_blas_int(cols);
3235 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3236 blas_int const lda = std::max<blas_int>(1, m);
3237 blas_int const ldb = detail::checked_blas_int(workspace_rows);
3238 blas_int rank = 0;
3239
3240 Scalar work_query{};
3241 blas_int const query_lwork = -1;
3242 uni20::lapack::gelss(m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, result.singular_values.data(),
3243 rcond, rank, &work_query, query_lwork);
3244
3245 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3246 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3247 uni20::lapack::gelss(m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, result.singular_values.data(),
3248 rcond, rank, work.data(), lwork);
3249
3250 if (rank < 0)
3251 {
3252 throw std::runtime_error("LAPACK gelss returned a negative rank");
3253 }
3254 result.rank = static_cast<std::size_t>(rank);
3255 for (std::size_t row = 0; row < cols; ++row)
3256 {
3257 for (std::size_t col = 0; col < rhs_count; ++col)
3258 {
3259 result.solution[row, col] = rhs_workspace[row, col];
3260 }
3261 }
3262 return result;
3263}
3264
3275template <uni20::LapackReal Scalar>
3276RealSvdLeastSquares<Scalar>
3280{
3281 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3282 {
3283 throw std::invalid_argument("real_divide_and_conquer_svd_least_squares received incompatible right-hand sides");
3284 }
3285
3286 std::size_t const rows = static_cast<std::size_t>(coefficients.rows());
3287 std::size_t const cols = static_cast<std::size_t>(coefficients.cols());
3288 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3289 RealSvdLeastSquares<Scalar> result;
3290 result.solution = detail::ColumnMajorLapackMatrix<Scalar>(cols, rhs_count);
3291 result.singular_values.resize(std::min(rows, cols));
3292 if (cols == 0 || rhs_count == 0)
3293 {
3294 return result;
3295 }
3296 if (rows == 0)
3297 {
3298 std::fill_n(result.solution.data(), result.solution.size(), Scalar{});
3299 return result;
3300 }
3301
3302 std::size_t const workspace_rows = std::max(rows, cols);
3303 detail::ColumnMajorLapackMatrix<Scalar> rhs_workspace(workspace_rows, rhs_count);
3304 for (std::size_t row = 0; row < rows; ++row)
3305 {
3306 for (std::size_t col = 0; col < rhs_count; ++col)
3307 {
3308 rhs_workspace[row, col] = right_hand_sides[row, col];
3309 }
3310 }
3311
3312 blas_int const m = detail::checked_blas_int(rows);
3313 blas_int const n = detail::checked_blas_int(cols);
3314 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3315 blas_int const lda = std::max<blas_int>(1, m);
3316 blas_int const ldb = detail::checked_blas_int(workspace_rows);
3317 blas_int rank = 0;
3318 blas_int const iwork_size = detail::gelsd_iwork_size(m, n);
3319 std::vector<blas_int> iwork(static_cast<std::size_t>(iwork_size), 0);
3320
3321 Scalar work_query{};
3322 blas_int const query_lwork = -1;
3323 uni20::lapack::gelsd(m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, result.singular_values.data(),
3324 rcond, rank, &work_query, query_lwork, iwork.data());
3325
3326 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3327 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3328 rank = 0;
3329 uni20::lapack::gelsd(m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, result.singular_values.data(),
3330 rcond, rank, work.data(), lwork, iwork.data());
3331
3332 if (rank < 0)
3333 {
3334 throw std::runtime_error("LAPACK gelsd returned a negative rank");
3335 }
3336 result.rank = static_cast<std::size_t>(rank);
3337 for (std::size_t row = 0; row < cols; ++row)
3338 {
3339 for (std::size_t col = 0; col < rhs_count; ++col)
3340 {
3341 result.solution[row, col] = rhs_workspace[row, col];
3342 }
3343 }
3344 return result;
3345}
3346
3356template <uni20::LapackReal Scalar>
3357RealRankRevealingLeastSquares<Scalar>
3361{
3362 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3363 {
3364 throw std::invalid_argument("real_rank_revealing_least_squares received incompatible right-hand sides");
3365 }
3366
3367 std::size_t const rows = static_cast<std::size_t>(coefficients.rows());
3368 std::size_t const cols = static_cast<std::size_t>(coefficients.cols());
3369 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3370 RealRankRevealingLeastSquares<Scalar> result;
3371 result.solution = detail::ColumnMajorLapackMatrix<Scalar>(cols, rhs_count);
3372 result.pivot_columns.resize(cols);
3373 std::iota(result.pivot_columns.begin(), result.pivot_columns.end(), std::size_t{0});
3374 if (cols == 0 || rhs_count == 0)
3375 {
3376 return result;
3377 }
3378 if (rows == 0)
3379 {
3380 std::fill_n(result.solution.data(), result.solution.size(), Scalar{});
3381 return result;
3382 }
3383
3384 std::size_t const workspace_rows = std::max(rows, cols);
3385 detail::ColumnMajorLapackMatrix<Scalar> rhs_workspace(workspace_rows, rhs_count);
3386 for (std::size_t row = 0; row < rows; ++row)
3387 {
3388 for (std::size_t col = 0; col < rhs_count; ++col)
3389 {
3390 rhs_workspace[row, col] = right_hand_sides[row, col];
3391 }
3392 }
3393
3394 blas_int const m = detail::checked_blas_int(rows);
3395 blas_int const n = detail::checked_blas_int(cols);
3396 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3397 blas_int const lda = std::max<blas_int>(1, m);
3398 blas_int const ldb = detail::checked_blas_int(workspace_rows);
3399 std::vector<blas_int> jpvt(cols, 0);
3400 blas_int rank = 0;
3401
3402 Scalar work_query{};
3403 blas_int const query_lwork = -1;
3404 uni20::lapack::gelsy(m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, jpvt.data(), rcond, rank,
3405 &work_query, query_lwork);
3406
3407 std::fill(jpvt.begin(), jpvt.end(), 0);
3408 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3409 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3410 uni20::lapack::gelsy(m, n, nrhs, coefficients.data(), lda, rhs_workspace.data(), ldb, jpvt.data(), rcond, rank,
3411 work.data(), lwork);
3412
3413 if (rank < 0)
3414 {
3415 throw std::runtime_error("LAPACK gelsy returned a negative rank");
3416 }
3417 result.rank = static_cast<std::size_t>(rank);
3418 for (std::size_t col = 0; col < cols; ++col)
3419 {
3420 if (jpvt[col] <= 0 || static_cast<std::size_t>(jpvt[col]) > cols)
3421 {
3422 throw std::runtime_error("LAPACK gelsy returned an invalid column pivot");
3423 }
3424 result.pivot_columns[col] = static_cast<std::size_t>(jpvt[col] - 1);
3425 }
3426
3427 for (std::size_t row = 0; row < cols; ++row)
3428 {
3429 for (std::size_t col = 0; col < rhs_count; ++col)
3430 {
3431 result.solution[row, col] = rhs_workspace[row, col];
3432 }
3433 }
3434 return result;
3435}
3436
3446template <uni20::LapackReal Scalar>
3447detail::ColumnMajorLapackMatrix<Scalar>
3450 MatrixFill triangle = MatrixFill::Upper)
3451{
3452 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
3453 {
3454 throw std::invalid_argument("real_symmetric_positive_definite_solve requires a square coefficient matrix");
3455 }
3456 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3457 {
3458 throw std::invalid_argument("real_symmetric_positive_definite_solve received incompatible right-hand sides");
3459 }
3460
3461 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
3462 if (n == 0 || right_hand_sides.cols() == 0)
3463 {
3464 return right_hand_sides;
3465 }
3466
3467 blas_int const order = detail::checked_blas_int(n);
3468 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
3469 char const uplo = detail::lapack_uplo(triangle);
3470 uni20::lapack::potrf(uplo, order, coefficients.data(), order);
3471 uni20::lapack::potrs(uplo, order, nrhs, coefficients.data(), order, right_hand_sides.data(), order);
3472 return right_hand_sides;
3473}
3474
3480template <uni20::LapackReal Scalar>
3481detail::ColumnMajorLapackMatrix<Scalar>
3484{
3485 if (!std::cmp_equal(factorization.factors.rows(), factorization.factors.cols()))
3486 {
3487 throw std::invalid_argument("real_symmetric_positive_definite_solve requires square Cholesky factors");
3488 }
3489 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
3490 if (!std::cmp_equal(right_hand_sides.rows(), n))
3491 {
3492 throw std::invalid_argument("real_symmetric_positive_definite_solve received incompatible right-hand sides");
3493 }
3494 if (n == 0 || right_hand_sides.cols() == 0)
3495 {
3496 return right_hand_sides;
3497 }
3498
3499 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors;
3500 blas_int const order = detail::checked_blas_int(n);
3501 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
3502 char const uplo = detail::lapack_uplo(factorization.triangle);
3503 uni20::lapack::potrs(uplo, order, nrhs, factors.data(), order, right_hand_sides.data(), order);
3504 return right_hand_sides;
3505}
3506
3517template <uni20::LapackReal Scalar>
3518RealRefinedLinearSolve<Scalar>
3521 MatrixFill triangle = MatrixFill::Upper)
3522{
3523 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
3524 {
3525 throw std::invalid_argument("real_symmetric_positive_definite_refined_solve requires a square coefficient matrix");
3526 }
3527 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3528 {
3529 throw std::invalid_argument(
3530 "real_symmetric_positive_definite_refined_solve received incompatible right-hand sides");
3531 }
3532
3533 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
3534 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3536 result.solution = right_hand_sides;
3537 result.forward_error_bounds.resize(rhs_count);
3538 result.backward_error_bounds.resize(rhs_count);
3539 if (n == 0 || rhs_count == 0)
3540 {
3541 return result;
3542 }
3543
3544 detail::ColumnMajorLapackMatrix<Scalar> factors = coefficients;
3545 blas_int const order = detail::checked_blas_int(n);
3546 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3547 char const uplo = detail::lapack_uplo(triangle);
3548 uni20::lapack::potrf(uplo, order, factors.data(), order);
3549 uni20::lapack::potrs(uplo, order, nrhs, factors.data(), order, result.solution.data(), order);
3550
3551 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
3552 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3553 uni20::lapack::porfs(uplo, order, nrhs, coefficients.data(), order, factors.data(), order, right_hand_sides.data(),
3554 order, result.solution.data(), order, result.forward_error_bounds.data(),
3555 result.backward_error_bounds.data(), work.data(), iwork.data());
3556 return result;
3557}
3558
3571template <uni20::LapackReal Scalar>
3572RealSymmetricPositiveDefiniteExpertLinearSolve<Scalar>
3575 MatrixFill triangle = MatrixFill::Upper)
3576{
3577 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
3578 {
3579 throw std::invalid_argument("real_symmetric_positive_definite_expert_solve requires a square coefficient matrix");
3580 }
3581 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3582 {
3583 throw std::invalid_argument("real_symmetric_positive_definite_expert_solve received incompatible right-hand sides");
3584 }
3585
3586 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
3587 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3591 result.forward_error_bounds.resize(rhs_count);
3592 result.backward_error_bounds.resize(rhs_count);
3593 result.scale.assign(n, Scalar{1});
3594 if (n == 0 || rhs_count == 0)
3595 {
3596 return result;
3597 }
3598
3599 blas_int const order = detail::checked_blas_int(n);
3600 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3601 char const uplo = detail::lapack_uplo(triangle);
3602 char equed = 'N';
3603 Scalar rcond{};
3604 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
3605 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3606
3608 'N', uplo, order, nrhs, coefficients.data(), order, result.factors.data(), order, equed, result.scale.data(),
3609 right_hand_sides.data(), order, result.solution.data(), order, rcond, result.forward_error_bounds.data(),
3610 result.backward_error_bounds.data(), work.data(), iwork.data());
3611 result.equilibration = equed;
3612 result.reciprocal_condition = rcond;
3613 return result;
3614}
3615
3625template <uni20::LapackReal Scalar>
3626Scalar
3628 MatrixFill triangle = MatrixFill::Upper)
3629{
3630 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
3631 {
3632 throw std::invalid_argument(
3633 "real_symmetric_positive_definite_one_norm_reciprocal_condition_number requires a square matrix");
3634 }
3635
3636 std::size_t const n = static_cast<std::size_t>(matrix.rows());
3637 if (n == 0)
3638 {
3639 return Scalar{1};
3640 }
3641
3642 Scalar const original_one_norm = detail::symmetric_matrix_one_norm(matrix, triangle);
3643 blas_int const order = detail::checked_blas_int(n);
3644 char const uplo = detail::lapack_uplo(triangle);
3645 uni20::lapack::potrf(uplo, order, matrix.data(), order);
3646 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
3647 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3648 return uni20::lapack::pocon(uplo, order, matrix.data(), order, original_one_norm, work.data(), iwork.data());
3649}
3650
3658template <uni20::LapackReal Scalar>
3660 RealSymmetricPositiveDefiniteFactorization<Scalar> const& factorization, Scalar original_one_norm)
3661{
3662 if (!std::cmp_equal(factorization.factors.rows(), factorization.factors.cols()))
3663 {
3664 throw std::invalid_argument(
3665 "real_symmetric_positive_definite_one_norm_reciprocal_condition_number requires square Cholesky factors");
3666 }
3667 if (original_one_norm < Scalar{})
3668 {
3669 throw std::invalid_argument(
3670 "real_symmetric_positive_definite_one_norm_reciprocal_condition_number requires a nonnegative matrix norm");
3671 }
3672
3673 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
3674 if (n == 0)
3675 {
3676 return Scalar{1};
3677 }
3678
3679 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors;
3680 blas_int const order = detail::checked_blas_int(n);
3681 char const uplo = detail::lapack_uplo(factorization.triangle);
3682 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
3683 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3684 return uni20::lapack::pocon(uplo, order, factors.data(), order, original_one_norm, work.data(), iwork.data());
3685}
3686
3695template <uni20::LapackReal Scalar>
3696detail::ColumnMajorLapackMatrix<Scalar>
3698 MatrixFill triangle = MatrixFill::Upper)
3699{
3700 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
3701 {
3702 throw std::invalid_argument("real_symmetric_positive_definite_inverse requires a square matrix");
3703 }
3704
3705 std::size_t const n = static_cast<std::size_t>(matrix.rows());
3706 if (n == 0)
3707 {
3708 return matrix;
3709 }
3710
3711 blas_int const order = detail::checked_blas_int(n);
3712 char const uplo = detail::lapack_uplo(triangle);
3713 uni20::lapack::potrf(uplo, order, matrix.data(), order);
3714 uni20::lapack::potri(uplo, order, matrix.data(), order);
3715 detail::mirror_selected_symmetric_triangle(matrix, triangle);
3716 return matrix;
3717}
3718
3727template <uni20::LapackReal Scalar>
3728RealSymmetricIndefiniteFactorization<Scalar>
3730 MatrixFill triangle = MatrixFill::Upper)
3731{
3732 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
3733 {
3734 throw std::invalid_argument("real_symmetric_indefinite_factorization requires a square matrix");
3735 }
3736
3737 std::size_t const n = static_cast<std::size_t>(matrix.rows());
3739 result.factors = std::move(matrix);
3740 result.pivot_blocks.resize(n);
3741 result.triangle = triangle;
3742 if (n == 0)
3743 {
3744 return result;
3745 }
3746
3747 blas_int const order = detail::checked_blas_int(n);
3748 char const uplo = detail::lapack_uplo(triangle);
3749 std::vector<blas_int> pivots(n);
3750
3751 Scalar work_query{};
3752 blas_int const query_lwork = -1;
3753 uni20::lapack::sytrf(uplo, order, result.factors.data(), order, pivots.data(), &work_query, query_lwork);
3754
3755 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3756 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3757 uni20::lapack::sytrf(uplo, order, result.factors.data(), order, pivots.data(), work.data(), lwork);
3758
3759 for (std::size_t index = 0; index < n; ++index)
3760 {
3761 result.pivot_blocks[index] = static_cast<int>(pivots[index]);
3762 }
3763 return result;
3764}
3765
3776template <uni20::LapackReal Scalar>
3777detail::ColumnMajorLapackMatrix<Scalar>
3780 MatrixFill triangle = MatrixFill::Upper)
3781{
3782 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
3783 {
3784 throw std::invalid_argument("real_symmetric_indefinite_solve requires a square coefficient matrix");
3785 }
3786 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3787 {
3788 throw std::invalid_argument("real_symmetric_indefinite_solve received incompatible right-hand sides");
3789 }
3790
3791 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
3792 if (n == 0 || right_hand_sides.cols() == 0)
3793 {
3794 return right_hand_sides;
3795 }
3796
3797 blas_int const order = detail::checked_blas_int(n);
3798 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
3799 char const uplo = detail::lapack_uplo(triangle);
3800 std::vector<blas_int> pivots(n);
3801
3802 Scalar work_query{};
3803 blas_int const query_lwork = -1;
3804 uni20::lapack::sytrf(uplo, order, coefficients.data(), order, pivots.data(), &work_query, query_lwork);
3805
3806 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3807 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
3808 uni20::lapack::sytrf(uplo, order, coefficients.data(), order, pivots.data(), work.data(), lwork);
3809 uni20::lapack::sytrs(uplo, order, nrhs, coefficients.data(), order, pivots.data(), right_hand_sides.data(), order);
3810 return right_hand_sides;
3811}
3812
3818template <uni20::LapackReal Scalar>
3819detail::ColumnMajorLapackMatrix<Scalar>
3822{
3823 if (!std::cmp_equal(factorization.factors.rows(), factorization.factors.cols()))
3824 {
3825 throw std::invalid_argument("real_symmetric_indefinite_solve requires square Bunch-Kaufman factors");
3826 }
3827 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
3828 if (factorization.pivot_blocks.size() != n)
3829 {
3830 throw std::invalid_argument("real_symmetric_indefinite_solve received inconsistent pivot metadata");
3831 }
3832 if (!std::cmp_equal(right_hand_sides.rows(), n))
3833 {
3834 throw std::invalid_argument("real_symmetric_indefinite_solve received incompatible right-hand sides");
3835 }
3836 if (n == 0 || right_hand_sides.cols() == 0)
3837 {
3838 return right_hand_sides;
3839 }
3840
3841 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors;
3842 std::vector<blas_int> pivots = detail::checked_symmetric_indefinite_pivots(factorization);
3843 blas_int const order = detail::checked_blas_int(n);
3844 blas_int const nrhs = detail::checked_blas_int(right_hand_sides.cols());
3845 char const uplo = detail::lapack_uplo(factorization.triangle);
3846 uni20::lapack::sytrs(uplo, order, nrhs, factors.data(), order, pivots.data(), right_hand_sides.data(), order);
3847 return right_hand_sides;
3848}
3849
3860template <uni20::LapackReal Scalar>
3861RealRefinedLinearSolve<Scalar>
3864 MatrixFill triangle = MatrixFill::Upper)
3865{
3866 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
3867 {
3868 throw std::invalid_argument("real_symmetric_indefinite_refined_solve requires a square coefficient matrix");
3869 }
3870 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3871 {
3872 throw std::invalid_argument("real_symmetric_indefinite_refined_solve received incompatible right-hand sides");
3873 }
3874
3875 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
3876 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3878 result.solution = right_hand_sides;
3879 result.forward_error_bounds.resize(rhs_count);
3880 result.backward_error_bounds.resize(rhs_count);
3881 if (n == 0 || rhs_count == 0)
3882 {
3883 return result;
3884 }
3885
3886 detail::ColumnMajorLapackMatrix<Scalar> factors = coefficients;
3887 blas_int const order = detail::checked_blas_int(n);
3888 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3889 char const uplo = detail::lapack_uplo(triangle);
3890 std::vector<blas_int> pivots(n);
3891
3892 Scalar work_query{};
3893 blas_int const query_lwork = -1;
3894 uni20::lapack::sytrf(uplo, order, factors.data(), order, pivots.data(), &work_query, query_lwork);
3895
3896 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3897 std::vector<Scalar> factor_work(static_cast<std::size_t>(lwork), Scalar{});
3898 uni20::lapack::sytrf(uplo, order, factors.data(), order, pivots.data(), factor_work.data(), lwork);
3899 uni20::lapack::sytrs(uplo, order, nrhs, factors.data(), order, pivots.data(), result.solution.data(), order);
3900
3901 std::vector<Scalar> refine_work(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order)), Scalar{});
3902 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3903 uni20::lapack::syrfs(uplo, order, nrhs, coefficients.data(), order, factors.data(), order, pivots.data(),
3904 right_hand_sides.data(), order, result.solution.data(), order,
3905 result.forward_error_bounds.data(), result.backward_error_bounds.data(), refine_work.data(),
3906 iwork.data());
3907 return result;
3908}
3909
3918template <uni20::LapackReal Scalar>
3919detail::ColumnMajorLapackMatrix<Scalar>
3921 MatrixFill triangle = MatrixFill::Upper)
3922{
3923 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
3924 {
3925 throw std::invalid_argument("real_symmetric_indefinite_inverse requires a square matrix");
3926 }
3927
3928 std::size_t const n = static_cast<std::size_t>(matrix.rows());
3929 if (n == 0)
3930 {
3931 return matrix;
3932 }
3933
3934 blas_int const order = detail::checked_blas_int(n);
3935 char const uplo = detail::lapack_uplo(triangle);
3936 std::vector<blas_int> pivots(n);
3937
3938 Scalar work_query{};
3939 blas_int const query_lwork = -1;
3940 uni20::lapack::sytrf(uplo, order, matrix.data(), order, pivots.data(), &work_query, query_lwork);
3941
3942 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
3943 std::vector<Scalar> factor_work(static_cast<std::size_t>(lwork), Scalar{});
3944 uni20::lapack::sytrf(uplo, order, matrix.data(), order, pivots.data(), factor_work.data(), lwork);
3945
3946 std::vector<Scalar> inverse_work(static_cast<std::size_t>(std::max<blas_int>(1, order)), Scalar{});
3947 uni20::lapack::sytri(uplo, order, matrix.data(), order, pivots.data(), inverse_work.data());
3948 detail::mirror_selected_symmetric_triangle(matrix, triangle);
3949 return matrix;
3950}
3951
3962template <uni20::LapackReal Scalar>
3963RealSymmetricIndefiniteExpertLinearSolve<Scalar>
3966 MatrixFill triangle = MatrixFill::Upper)
3967{
3968 if (!std::cmp_equal(coefficients.rows(), coefficients.cols()))
3969 {
3970 throw std::invalid_argument("real_symmetric_indefinite_expert_solve requires a square coefficient matrix");
3971 }
3972 if (!std::cmp_equal(coefficients.rows(), right_hand_sides.rows()))
3973 {
3974 throw std::invalid_argument("real_symmetric_indefinite_expert_solve received incompatible right-hand sides");
3975 }
3976
3977 std::size_t const n = static_cast<std::size_t>(coefficients.rows());
3978 std::size_t const rhs_count = static_cast<std::size_t>(right_hand_sides.cols());
3982 result.pivot_blocks.resize(n);
3983 result.forward_error_bounds.resize(rhs_count);
3984 result.backward_error_bounds.resize(rhs_count);
3985 if (n == 0 || rhs_count == 0)
3986 {
3987 return result;
3988 }
3989
3990 blas_int const order = detail::checked_blas_int(n);
3991 blas_int const nrhs = detail::checked_blas_int(rhs_count);
3992 char const uplo = detail::lapack_uplo(triangle);
3993 std::vector<blas_int> pivots(n, 0);
3994 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
3995 Scalar rcond{};
3996
3997 Scalar work_query{};
3998 blas_int const query_lwork = -1;
3999 uni20::lapack::sysvx('N', uplo, order, nrhs, coefficients.data(), order, result.factors.data(), order, pivots.data(),
4000 right_hand_sides.data(), order, result.solution.data(), order, rcond,
4001 result.forward_error_bounds.data(), result.backward_error_bounds.data(), &work_query,
4002 query_lwork, iwork.data());
4003
4004 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4005 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4007 'N', uplo, order, nrhs, coefficients.data(), order, result.factors.data(), order, pivots.data(),
4008 right_hand_sides.data(), order, result.solution.data(), order, rcond, result.forward_error_bounds.data(),
4009 result.backward_error_bounds.data(), work.data(), lwork, iwork.data());
4010 result.reciprocal_condition = rcond;
4011
4012 for (std::size_t index = 0; index < n; ++index)
4013 {
4014 result.pivot_blocks[index] = static_cast<int>(pivots[index]);
4015 }
4016 return result;
4017}
4018
4028template <uni20::LapackReal Scalar>
4030 MatrixFill triangle = MatrixFill::Upper)
4031{
4032 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4033 {
4034 throw std::invalid_argument(
4035 "real_symmetric_indefinite_one_norm_reciprocal_condition_number requires a square matrix");
4036 }
4037
4038 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4039 if (n == 0)
4040 {
4041 return Scalar{1};
4042 }
4043
4044 Scalar const original_one_norm = detail::symmetric_matrix_one_norm(matrix, triangle);
4045 blas_int const order = detail::checked_blas_int(n);
4046 char const uplo = detail::lapack_uplo(triangle);
4047 std::vector<blas_int> pivots(n);
4048
4049 Scalar work_query{};
4050 blas_int const query_lwork = -1;
4051 uni20::lapack::sytrf(uplo, order, matrix.data(), order, pivots.data(), &work_query, query_lwork);
4052
4053 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4054 std::vector<Scalar> factor_work(static_cast<std::size_t>(lwork), Scalar{});
4055 uni20::lapack::sytrf(uplo, order, matrix.data(), order, pivots.data(), factor_work.data(), lwork);
4056
4057 std::vector<Scalar> condition_work(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order)), Scalar{});
4058 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
4059 return uni20::lapack::sycon(uplo, order, matrix.data(), order, pivots.data(), original_one_norm,
4060 condition_work.data(), iwork.data());
4061}
4062
4070template <uni20::LapackReal Scalar>
4072 RealSymmetricIndefiniteFactorization<Scalar> const& factorization, Scalar original_one_norm)
4073{
4074 if (!std::cmp_equal(factorization.factors.rows(), factorization.factors.cols()))
4075 {
4076 throw std::invalid_argument(
4077 "real_symmetric_indefinite_one_norm_reciprocal_condition_number requires square Bunch-Kaufman factors");
4078 }
4079 if (original_one_norm < Scalar{})
4080 {
4081 throw std::invalid_argument(
4082 "real_symmetric_indefinite_one_norm_reciprocal_condition_number requires a nonnegative matrix norm");
4083 }
4084
4085 std::size_t const n = static_cast<std::size_t>(factorization.factors.rows());
4086 if (factorization.pivot_blocks.size() != n)
4087 {
4088 throw std::invalid_argument(
4089 "real_symmetric_indefinite_one_norm_reciprocal_condition_number received inconsistent pivot metadata");
4090 }
4091 if (n == 0)
4092 {
4093 return Scalar{1};
4094 }
4095
4096 detail::ColumnMajorLapackMatrix<Scalar> factors = factorization.factors;
4097 std::vector<blas_int> pivots = detail::checked_symmetric_indefinite_pivots(factorization);
4098 blas_int const order = detail::checked_blas_int(n);
4099 char const uplo = detail::lapack_uplo(factorization.triangle);
4100 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order)), Scalar{});
4101 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
4102 return uni20::lapack::sycon(uplo, order, factors.data(), order, pivots.data(), original_one_norm, work.data(),
4103 iwork.data());
4104}
4105
4116template <uni20::LapackReal Scalar>
4118 bool compute_vectors,
4119 MatrixFill triangle = MatrixFill::Upper)
4120{
4121 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4122 {
4123 throw std::invalid_argument("real_symmetric_eigensystem requires a square matrix");
4124 }
4125
4126 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4128 result.eigenvalues.resize(n);
4129 if (n == 0)
4130 {
4132 return result;
4133 }
4134
4135 blas_int const order = detail::checked_blas_int(n);
4136 char const jobz = compute_vectors ? 'V' : 'N';
4137 char const uplo = detail::lapack_uplo(triangle);
4138 Scalar work_query{};
4139 blas_int const query_lwork = -1;
4140 uni20::lapack::syev(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), &work_query, query_lwork);
4141
4142 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4143 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4144 uni20::lapack::syev(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), work.data(), lwork);
4145
4146 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
4147 return result;
4148}
4149
4161template <uni20::LapackReal Scalar>
4162RealSymmetricEigensystem<Scalar>
4164 MatrixFill triangle = MatrixFill::Upper)
4165{
4166 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4167 {
4168 throw std::invalid_argument("real_symmetric_eigensystem_divide_and_conquer requires a square matrix");
4169 }
4170
4171 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4173 result.eigenvalues.resize(n);
4174 if (n == 0)
4175 {
4177 return result;
4178 }
4179
4180 blas_int const order = detail::checked_blas_int(n);
4181 char const jobz = compute_vectors ? 'V' : 'N';
4182 char const uplo = detail::lapack_uplo(triangle);
4183 Scalar work_query{};
4184 blas_int iwork_query = 0;
4185 blas_int const query_lwork = -1;
4186 uni20::lapack::syevd(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), &work_query, query_lwork,
4187 &iwork_query, query_lwork);
4188
4189 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4190 blas_int const liwork = std::max<blas_int>(1, iwork_query);
4191 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4192 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
4193 uni20::lapack::syevd(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), work.data(), lwork,
4194 iwork.data(), liwork);
4195
4196 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
4197 return result;
4198}
4199
4213template <uni20::LapackReal Scalar>
4215 std::size_t first_index, std::size_t last_index,
4216 bool compute_vectors,
4217 MatrixFill triangle = MatrixFill::Upper)
4218{
4219 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4220 {
4221 throw std::invalid_argument("real_symmetric_eigensystem_index_range requires a square matrix");
4222 }
4223
4224 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4225 if (n == 0 || first_index > last_index || last_index >= n)
4226 {
4227 throw std::invalid_argument("real_symmetric_eigensystem_index_range received an invalid index range");
4228 }
4229
4230 std::size_t const selected = last_index - first_index + 1;
4232 result.eigenvalues.resize(selected);
4233
4234 blas_int const order = detail::checked_blas_int(n);
4235 blas_int const first = detail::checked_blas_int(first_index + 1);
4236 blas_int const last = detail::checked_blas_int(last_index + 1);
4237 char const jobz = compute_vectors ? 'V' : 'N';
4238 char const range = 'I';
4239 char const uplo = detail::lapack_uplo(triangle);
4240 detail::ColumnMajorLapackMatrix<Scalar> eigenvectors(compute_vectors ? n : 1, compute_vectors ? selected : 1);
4241 blas_int const ldz = compute_vectors ? order : 1;
4242 std::vector<blas_int> support(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order)), 0);
4243 Scalar work_query{};
4244 blas_int iwork_query = 0;
4245 blas_int selected_count = 0;
4246 blas_int const query_lwork = -1;
4247 uni20::lapack::syevr(jobz, range, uplo, order, matrix.data(), order, Scalar{}, Scalar{}, first, last, Scalar{},
4248 selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, support.data(), &work_query,
4249 query_lwork, &iwork_query, query_lwork);
4250
4251 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4252 blas_int const liwork = std::max<blas_int>(1, iwork_query);
4253 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4254 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
4255 selected_count = 0;
4256 uni20::lapack::syevr(jobz, range, uplo, order, matrix.data(), order, Scalar{}, Scalar{}, first, last, Scalar{},
4257 selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, support.data(), work.data(),
4258 lwork, iwork.data(), liwork);
4259
4260 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
4261 {
4262 throw std::runtime_error("LAPACK syevr returned an unexpected number of eigenvalues");
4263 }
4264
4265 result.eigenvectors = compute_vectors ? std::move(eigenvectors) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
4266 return result;
4267}
4268
4281template <uni20::LapackReal Scalar>
4284 bool compute_vectors,
4285 MatrixFill triangle = MatrixFill::Upper)
4286{
4287 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4288 {
4289 throw std::invalid_argument("real_generalized_symmetric_eigensystem requires a square matrix");
4290 }
4291 if (!std::cmp_equal(metric.rows(), metric.cols()))
4292 {
4293 throw std::invalid_argument("real_generalized_symmetric_eigensystem requires a square metric matrix");
4294 }
4295 if (!std::cmp_equal(matrix.rows(), metric.rows()))
4296 {
4297 throw std::invalid_argument("real_generalized_symmetric_eigensystem received incompatible matrix sizes");
4298 }
4299
4300 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4302 result.eigenvalues.resize(n);
4303 if (n == 0)
4304 {
4306 return result;
4307 }
4308
4309 blas_int const order = detail::checked_blas_int(n);
4310 char const jobz = compute_vectors ? 'V' : 'N';
4311 char const uplo = detail::lapack_uplo(triangle);
4312 Scalar work_query{};
4313 blas_int const query_lwork = -1;
4314 uni20::lapack::sygv(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4315 &work_query, query_lwork);
4316
4317 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4318 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4319 uni20::lapack::sygv(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4320 work.data(), lwork);
4321
4322 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
4323 return result;
4324}
4325
4339template <uni20::LapackReal Scalar>
4340RealSymmetricEigensystem<Scalar>
4343 bool compute_vectors, MatrixFill triangle = MatrixFill::Upper)
4344{
4345 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4346 {
4347 throw std::invalid_argument("real_generalized_symmetric_eigensystem_divide_and_conquer requires a square matrix");
4348 }
4349 if (!std::cmp_equal(metric.rows(), metric.cols()))
4350 {
4351 throw std::invalid_argument(
4352 "real_generalized_symmetric_eigensystem_divide_and_conquer requires a square metric matrix");
4353 }
4354 if (!std::cmp_equal(matrix.rows(), metric.rows()))
4355 {
4356 throw std::invalid_argument(
4357 "real_generalized_symmetric_eigensystem_divide_and_conquer received incompatible matrix sizes");
4358 }
4359
4360 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4362 result.eigenvalues.resize(n);
4363 if (n == 0)
4364 {
4366 return result;
4367 }
4368
4369 blas_int const order = detail::checked_blas_int(n);
4370 char const jobz = compute_vectors ? 'V' : 'N';
4371 char const uplo = detail::lapack_uplo(triangle);
4372 Scalar work_query{};
4373 blas_int iwork_query = 0;
4374 blas_int const query_lwork = -1;
4375 uni20::lapack::sygvd(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4376 &work_query, query_lwork, &iwork_query, query_lwork);
4377
4378 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4379 blas_int const liwork = std::max<blas_int>(1, iwork_query);
4380 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4381 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
4382 uni20::lapack::sygvd(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4383 work.data(), lwork, iwork.data(), liwork);
4384
4385 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
4386 return result;
4387}
4388
4404template <uni20::LapackReal Scalar>
4407 std::size_t first_index, std::size_t last_index, bool compute_vectors, MatrixFill triangle = MatrixFill::Upper)
4408{
4409 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4410 {
4411 throw std::invalid_argument("real_generalized_symmetric_eigensystem_index_range requires a square matrix");
4412 }
4413 if (!std::cmp_equal(metric.rows(), metric.cols()))
4414 {
4415 throw std::invalid_argument("real_generalized_symmetric_eigensystem_index_range requires a square metric matrix");
4416 }
4417 if (!std::cmp_equal(matrix.rows(), metric.rows()))
4418 {
4419 throw std::invalid_argument(
4420 "real_generalized_symmetric_eigensystem_index_range received incompatible matrix sizes");
4421 }
4422
4423 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4424 if (n == 0 || first_index > last_index || last_index >= n)
4425 {
4426 throw std::invalid_argument("real_generalized_symmetric_eigensystem_index_range received an invalid index range");
4427 }
4428
4429 std::size_t const selected = last_index - first_index + 1;
4431 result.eigenvalues.resize(n);
4432
4433 blas_int const order = detail::checked_blas_int(n);
4434 blas_int const first = detail::checked_blas_int(first_index + 1);
4435 blas_int const last = detail::checked_blas_int(last_index + 1);
4436 char const jobz = compute_vectors ? 'V' : 'N';
4437 char const range = 'I';
4438 char const uplo = detail::lapack_uplo(triangle);
4439 detail::ColumnMajorLapackMatrix<Scalar> eigenvectors(compute_vectors ? n : 1, compute_vectors ? selected : 1);
4440 blas_int const ldz = compute_vectors ? order : 1;
4441 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, 5 * order)), 0);
4442 std::vector<blas_int> ifail(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
4443 Scalar work_query{};
4444 blas_int selected_count = 0;
4445 blas_int const query_lwork = -1;
4446 uni20::lapack::sygvx(1, jobz, range, uplo, order, matrix.data(), order, metric.data(), order, Scalar{}, Scalar{},
4447 first, last, Scalar{}, selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz,
4448 &work_query, query_lwork, iwork.data(), ifail.data());
4449
4450 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4451 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4452 selected_count = 0;
4453 uni20::lapack::sygvx(1, jobz, range, uplo, order, matrix.data(), order, metric.data(), order, Scalar{}, Scalar{},
4454 first, last, Scalar{}, selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz,
4455 work.data(), lwork, iwork.data(), ifail.data());
4456
4457 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
4458 {
4459 throw std::runtime_error("LAPACK sygvx returned an unexpected number of eigenvalues");
4460 }
4461
4462 result.eigenvalues.resize(selected);
4463 result.eigenvectors = compute_vectors ? std::move(eigenvectors) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
4464 return result;
4465}
4466
4476template <uni20::LapackComplexReal Real>
4477ComplexHermitianEigensystem<Real>
4478complex_hermitian_eigensystem(detail::ColumnMajorLapackMatrix<uni20::detail::standard_complex<Real>> matrix, bool compute_vectors,
4479 MatrixFill triangle = MatrixFill::Upper)
4480{
4481 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4482 {
4483 throw std::invalid_argument("complex_hermitian_eigensystem requires a square matrix");
4484 }
4485
4487 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4489 result.eigenvalues.resize(n);
4490 if (n == 0)
4491 {
4493 return result;
4494 }
4495
4496 blas_int const order = detail::checked_blas_int(n);
4497 char const jobz = compute_vectors ? 'V' : 'N';
4498 char const uplo = detail::lapack_uplo(triangle);
4499 std::vector<Real> rwork(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order - 2)), Real{});
4500 Complex work_query{};
4501 blas_int const query_lwork = -1;
4502 uni20::lapack::heev(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), &work_query, query_lwork,
4503 rwork.data());
4504
4505 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(std::real(work_query)));
4506 std::vector<Complex> work(static_cast<std::size_t>(lwork), Complex{});
4507 uni20::lapack::heev(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), work.data(), lwork,
4508 rwork.data());
4509
4510 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Complex>(0, 0);
4511 return result;
4512}
4513
4522template <uni20::LapackComplexReal Real>
4523ComplexHermitianEigensystem<Real>
4525 bool compute_vectors, MatrixFill triangle = MatrixFill::Upper)
4526{
4527 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4528 {
4529 throw std::invalid_argument("complex_hermitian_eigensystem_divide_and_conquer requires a square matrix");
4530 }
4531
4533 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4535 result.eigenvalues.resize(n);
4536 if (n == 0)
4537 {
4539 return result;
4540 }
4541
4542 blas_int const order = detail::checked_blas_int(n);
4543 char const jobz = compute_vectors ? 'V' : 'N';
4544 char const uplo = detail::lapack_uplo(triangle);
4545 Complex work_query{};
4546 Real rwork_query{};
4547 blas_int iwork_query = 0;
4548 blas_int const query_lwork = -1;
4549 uni20::lapack::heevd(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), &work_query, query_lwork,
4550 &rwork_query, query_lwork, &iwork_query, query_lwork);
4551
4552 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(std::real(work_query)));
4553 blas_int const lrwork = std::max<blas_int>(1, static_cast<blas_int>(rwork_query));
4554 blas_int const liwork = std::max<blas_int>(1, iwork_query);
4555 std::vector<Complex> work(static_cast<std::size_t>(lwork), Complex{});
4556 std::vector<Real> rwork(static_cast<std::size_t>(lrwork), Real{});
4557 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
4558 uni20::lapack::heevd(jobz, uplo, order, matrix.data(), order, result.eigenvalues.data(), work.data(), lwork,
4559 rwork.data(), lrwork, iwork.data(), liwork);
4560
4561 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Complex>(0, 0);
4562 return result;
4563}
4564
4575template <uni20::LapackComplexReal Real>
4576ComplexHermitianEigensystem<Real>
4578 std::size_t first_index, std::size_t last_index, bool compute_vectors,
4579 MatrixFill triangle = MatrixFill::Upper)
4580{
4581 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4582 {
4583 throw std::invalid_argument("complex_hermitian_eigensystem_index_range requires a square matrix");
4584 }
4585
4587 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4588 if (n == 0 || first_index > last_index || last_index >= n)
4589 {
4590 throw std::invalid_argument("complex_hermitian_eigensystem_index_range received an invalid index range");
4591 }
4592
4593 std::size_t const selected = last_index - first_index + 1;
4595 result.eigenvalues.resize(selected);
4596
4597 blas_int const order = detail::checked_blas_int(n);
4598 blas_int const first = detail::checked_blas_int(first_index + 1);
4599 blas_int const last = detail::checked_blas_int(last_index + 1);
4600 char const jobz = compute_vectors ? 'V' : 'N';
4601 char const range = 'I';
4602 char const uplo = detail::lapack_uplo(triangle);
4603 detail::ColumnMajorLapackMatrix<Complex> eigenvectors(compute_vectors ? n : 1, compute_vectors ? selected : 1);
4604 blas_int const ldz = compute_vectors ? order : 1;
4605 std::vector<blas_int> support(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order)), 0);
4606 Complex work_query{};
4607 Real rwork_query{};
4608 blas_int iwork_query = 0;
4609 blas_int selected_count = 0;
4610 blas_int const query_lwork = -1;
4611 uni20::lapack::heevr(jobz, range, uplo, order, matrix.data(), order, Real{}, Real{}, first, last, Real{},
4612 selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, support.data(), &work_query,
4613 query_lwork, &rwork_query, query_lwork, &iwork_query, query_lwork);
4614
4615 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(std::real(work_query)));
4616 blas_int const lrwork = std::max<blas_int>(1, static_cast<blas_int>(rwork_query));
4617 blas_int const liwork = std::max<blas_int>(1, iwork_query);
4618 std::vector<Complex> work(static_cast<std::size_t>(lwork), Complex{});
4619 std::vector<Real> rwork(static_cast<std::size_t>(lrwork), Real{});
4620 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
4621 selected_count = 0;
4622 uni20::lapack::heevr(jobz, range, uplo, order, matrix.data(), order, Real{}, Real{}, first, last, Real{},
4623 selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, support.data(), work.data(),
4624 lwork, rwork.data(), lrwork, iwork.data(), liwork);
4625
4626 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
4627 {
4628 throw std::runtime_error("LAPACK heevr returned an unexpected number of eigenvalues");
4629 }
4630
4631 result.eigenvectors = compute_vectors ? std::move(eigenvectors) : detail::ColumnMajorLapackMatrix<Complex>(0, 0);
4632 return result;
4633}
4634
4645template <uni20::LapackComplexReal Real>
4646ComplexHermitianEigensystem<Real>
4648 detail::ColumnMajorLapackMatrix<uni20::detail::standard_complex<Real>> metric,
4649 bool compute_vectors, MatrixFill triangle = MatrixFill::Upper)
4650{
4651 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4652 {
4653 throw std::invalid_argument("complex_generalized_hermitian_eigensystem requires a square matrix");
4654 }
4655 if (!std::cmp_equal(metric.rows(), metric.cols()))
4656 {
4657 throw std::invalid_argument("complex_generalized_hermitian_eigensystem requires a square metric matrix");
4658 }
4659 if (!std::cmp_equal(matrix.rows(), metric.rows()))
4660 {
4661 throw std::invalid_argument("complex_generalized_hermitian_eigensystem received incompatible matrix sizes");
4662 }
4663
4665 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4667 result.eigenvalues.resize(n);
4668 if (n == 0)
4669 {
4671 return result;
4672 }
4673
4674 blas_int const order = detail::checked_blas_int(n);
4675 char const jobz = compute_vectors ? 'V' : 'N';
4676 char const uplo = detail::lapack_uplo(triangle);
4677 std::vector<Real> rwork(static_cast<std::size_t>(std::max<blas_int>(1, 3 * order - 2)), Real{});
4678 Complex work_query{};
4679 blas_int const query_lwork = -1;
4680 uni20::lapack::hegv(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4681 &work_query, query_lwork, rwork.data());
4682
4683 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(std::real(work_query)));
4684 std::vector<Complex> work(static_cast<std::size_t>(lwork), Complex{});
4685 uni20::lapack::hegv(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4686 work.data(), lwork, rwork.data());
4687
4688 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Complex>(0, 0);
4689 return result;
4690}
4691
4701template <uni20::LapackComplexReal Real>
4703 detail::ColumnMajorLapackMatrix<uni20::detail::standard_complex<Real>> matrix,
4704 detail::ColumnMajorLapackMatrix<uni20::detail::standard_complex<Real>> metric, bool compute_vectors,
4705 MatrixFill triangle = MatrixFill::Upper)
4706{
4707 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4708 {
4709 throw std::invalid_argument(
4710 "complex_generalized_hermitian_eigensystem_divide_and_conquer requires a square matrix");
4711 }
4712 if (!std::cmp_equal(metric.rows(), metric.cols()))
4713 {
4714 throw std::invalid_argument(
4715 "complex_generalized_hermitian_eigensystem_divide_and_conquer requires a square metric matrix");
4716 }
4717 if (!std::cmp_equal(matrix.rows(), metric.rows()))
4718 {
4719 throw std::invalid_argument(
4720 "complex_generalized_hermitian_eigensystem_divide_and_conquer received incompatible matrix sizes");
4721 }
4722
4724 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4726 result.eigenvalues.resize(n);
4727 if (n == 0)
4728 {
4730 return result;
4731 }
4732
4733 blas_int const order = detail::checked_blas_int(n);
4734 char const jobz = compute_vectors ? 'V' : 'N';
4735 char const uplo = detail::lapack_uplo(triangle);
4736 Complex work_query{};
4737 Real rwork_query{};
4738 blas_int iwork_query = 0;
4739 blas_int const query_lwork = -1;
4740 uni20::lapack::hegvd(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4741 &work_query, query_lwork, &rwork_query, query_lwork, &iwork_query, query_lwork);
4742
4743 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(std::real(work_query)));
4744 blas_int const lrwork = std::max<blas_int>(1, static_cast<blas_int>(rwork_query));
4745 blas_int const liwork = std::max<blas_int>(1, iwork_query);
4746 std::vector<Complex> work(static_cast<std::size_t>(lwork), Complex{});
4747 std::vector<Real> rwork(static_cast<std::size_t>(lrwork), Real{});
4748 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
4749 uni20::lapack::hegvd(1, jobz, uplo, order, matrix.data(), order, metric.data(), order, result.eigenvalues.data(),
4750 work.data(), lwork, rwork.data(), lrwork, iwork.data(), liwork);
4751
4752 result.eigenvectors = compute_vectors ? std::move(matrix) : detail::ColumnMajorLapackMatrix<Complex>(0, 0);
4753 return result;
4754}
4755
4767template <uni20::LapackComplexReal Real>
4768ComplexHermitianEigensystem<Real>
4770 detail::ColumnMajorLapackMatrix<uni20::detail::standard_complex<Real>> metric,
4771 std::size_t first_index, std::size_t last_index,
4772 bool compute_vectors, MatrixFill triangle = MatrixFill::Upper)
4773{
4774 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
4775 {
4776 throw std::invalid_argument("complex_generalized_hermitian_eigensystem_index_range requires a square matrix");
4777 }
4778 if (!std::cmp_equal(metric.rows(), metric.cols()))
4779 {
4780 throw std::invalid_argument(
4781 "complex_generalized_hermitian_eigensystem_index_range requires a square metric matrix");
4782 }
4783 if (!std::cmp_equal(matrix.rows(), metric.rows()))
4784 {
4785 throw std::invalid_argument(
4786 "complex_generalized_hermitian_eigensystem_index_range received incompatible matrix sizes");
4787 }
4788
4790 std::size_t const n = static_cast<std::size_t>(matrix.rows());
4791 if (n == 0 || first_index > last_index || last_index >= n)
4792 {
4793 throw std::invalid_argument(
4794 "complex_generalized_hermitian_eigensystem_index_range received an invalid index range");
4795 }
4796
4797 std::size_t const selected = last_index - first_index + 1;
4799 result.eigenvalues.resize(n);
4800
4801 blas_int const order = detail::checked_blas_int(n);
4802 blas_int const first = detail::checked_blas_int(first_index + 1);
4803 blas_int const last = detail::checked_blas_int(last_index + 1);
4804 char const jobz = compute_vectors ? 'V' : 'N';
4805 char const range = 'I';
4806 char const uplo = detail::lapack_uplo(triangle);
4807 detail::ColumnMajorLapackMatrix<Complex> eigenvectors(compute_vectors ? n : 1, compute_vectors ? selected : 1);
4808 blas_int const ldz = compute_vectors ? order : 1;
4809 std::vector<Real> rwork(static_cast<std::size_t>(std::max<blas_int>(1, 7 * order)), Real{});
4810 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, 5 * order)), 0);
4811 std::vector<blas_int> ifail(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
4812 Complex work_query{};
4813 blas_int selected_count = 0;
4814 blas_int const query_lwork = -1;
4815 uni20::lapack::hegvx(1, jobz, range, uplo, order, matrix.data(), order, metric.data(), order, Real{}, Real{}, first,
4816 last, Real{}, selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, &work_query,
4817 query_lwork, rwork.data(), iwork.data(), ifail.data());
4818
4819 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(std::real(work_query)));
4820 std::vector<Complex> work(static_cast<std::size_t>(lwork), Complex{});
4821 selected_count = 0;
4822 uni20::lapack::hegvx(1, jobz, range, uplo, order, matrix.data(), order, metric.data(), order, Real{}, Real{}, first,
4823 last, Real{}, selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, work.data(),
4824 lwork, rwork.data(), iwork.data(), ifail.data());
4825
4826 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
4827 {
4828 throw std::runtime_error("LAPACK hegvx returned an unexpected number of eigenvalues");
4829 }
4830
4831 result.eigenvalues.resize(selected);
4832 result.eigenvectors = compute_vectors ? std::move(eigenvectors) : detail::ColumnMajorLapackMatrix<Complex>(0, 0);
4833 return result;
4834}
4835
4843template <uni20::LapackReal Scalar>
4845{
4846 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
4847 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
4848 std::size_t const rank = std::min(rows, cols);
4849
4851 result.reflectors = std::move(matrix);
4852 result.tau.resize(rank);
4853 result.rank = rank;
4854 if (rank == 0)
4855 {
4856 return result;
4857 }
4858
4859 blas_int const m = detail::checked_blas_int(rows);
4860 blas_int const n = detail::checked_blas_int(cols);
4861 blas_int const lda = std::max<blas_int>(1, m);
4862
4863 Scalar geqrf_work_query{};
4864 blas_int const query_lwork = -1;
4865 uni20::lapack::geqrf(m, n, result.reflectors.data(), lda, result.tau.data(), &geqrf_work_query, query_lwork);
4866
4867 blas_int const geqrf_lwork = std::max<blas_int>(1, static_cast<blas_int>(geqrf_work_query));
4868 std::vector<Scalar> geqrf_work(static_cast<std::size_t>(geqrf_lwork), Scalar{});
4869 uni20::lapack::geqrf(m, n, result.reflectors.data(), lda, result.tau.data(), geqrf_work.data(), geqrf_lwork);
4870
4871 return result;
4872}
4873
4884template <uni20::LapackReal Scalar>
4889{
4890 std::size_t const reflector_rows = static_cast<std::size_t>(factorization.reflectors.rows());
4891 std::size_t const reflector_cols = static_cast<std::size_t>(factorization.reflectors.cols());
4892 if (factorization.rank > std::min(reflector_rows, reflector_cols))
4893 {
4894 throw std::invalid_argument("apply_real_qr_factor received inconsistent QR reflector dimensions");
4895 }
4896 if (factorization.tau.size() < factorization.rank)
4897 {
4898 throw std::invalid_argument("apply_real_qr_factor received too few QR scalar factors");
4899 }
4900
4901 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), reflector_rows))
4902 {
4903 throw std::invalid_argument("apply_real_qr_factor left application requires matching target row count");
4904 }
4905 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), reflector_rows))
4906 {
4907 throw std::invalid_argument("apply_real_qr_factor right application requires matching target column count");
4908 }
4909 if (target.rows() == 0 || target.cols() == 0 || factorization.rank == 0)
4910 {
4911 return target;
4912 }
4913
4914 detail::ColumnMajorLapackMatrix<Scalar> reflectors = factorization.reflectors;
4915 std::vector<Scalar> tau = factorization.tau;
4916 blas_int const m = detail::checked_blas_int(target.rows());
4917 blas_int const n = detail::checked_blas_int(target.cols());
4918 blas_int const k = detail::checked_blas_int(factorization.rank);
4919 blas_int const lda = std::max<blas_int>(1, detail::checked_blas_int(reflector_rows));
4920 blas_int const ldc = std::max<blas_int>(1, m);
4921 char const side_char = detail::lapack_side(side);
4922 char const trans = detail::lapack_transpose(transpose);
4923
4924 Scalar work_query{};
4925 blas_int const query_lwork = -1;
4926 uni20::lapack::ormqr(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, &work_query,
4927 query_lwork);
4928
4929 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
4930 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
4931 uni20::lapack::ormqr(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, work.data(),
4932 lwork);
4933
4934 return target;
4935}
4936
4943template <uni20::LapackReal Scalar>
4945{
4946 auto compact = real_compact_qr_factorization(std::move(matrix));
4947 std::size_t const rows = static_cast<std::size_t>(compact.reflectors.rows());
4948 std::size_t const cols = static_cast<std::size_t>(compact.reflectors.cols());
4949 std::size_t const rank = compact.rank;
4950
4952 result.q = detail::ColumnMajorLapackMatrix<Scalar>(rows, rank);
4953 result.r = detail::ColumnMajorLapackMatrix<Scalar>(rank, cols);
4954 if (rank == 0)
4955 {
4956 return result;
4957 }
4958 laset(result.r, Scalar{}, Scalar{}, MatrixFill::All);
4959
4960 blas_int const m = detail::checked_blas_int(rows);
4961 blas_int const k = detail::checked_blas_int(rank);
4962 blas_int const lda = std::max<blas_int>(1, m);
4963
4964 for (std::size_t row = 0; row < rank; ++row)
4965 {
4966 for (std::size_t col = row; col < cols; ++col)
4967 {
4968 result.r[row, col] = compact.reflectors[row, col];
4969 }
4970 }
4971
4972 for (std::size_t col = 0; col < rank; ++col)
4973 {
4974 for (std::size_t row = 0; row < rows; ++row)
4975 {
4976 result.q[row, col] = compact.reflectors[row, col];
4977 }
4978 }
4979
4980 Scalar orgqr_work_query{};
4981 blas_int const query_lwork = -1;
4982 uni20::lapack::orgqr(m, k, k, result.q.data(), lda, compact.tau.data(), &orgqr_work_query, query_lwork);
4983
4984 blas_int const orgqr_lwork = std::max<blas_int>(1, static_cast<blas_int>(orgqr_work_query));
4985 std::vector<Scalar> orgqr_work(static_cast<std::size_t>(orgqr_lwork), Scalar{});
4986 uni20::lapack::orgqr(m, k, k, result.q.data(), lda, compact.tau.data(), orgqr_work.data(), orgqr_lwork);
4987
4988 return result;
4989}
4990
4998template <uni20::LapackReal Scalar>
5000{
5001 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
5002 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
5003 std::size_t const rank = std::min(rows, cols);
5004
5006 result.reflectors = std::move(matrix);
5007 result.tau.resize(rank);
5008 result.rank = rank;
5009 if (rank == 0)
5010 {
5011 return result;
5012 }
5013
5014 blas_int const m = detail::checked_blas_int(rows);
5015 blas_int const n = detail::checked_blas_int(cols);
5016 blas_int const lda = std::max<blas_int>(1, m);
5017
5018 Scalar gelqf_work_query{};
5019 blas_int const query_lwork = -1;
5020 uni20::lapack::gelqf(m, n, result.reflectors.data(), lda, result.tau.data(), &gelqf_work_query, query_lwork);
5021
5022 blas_int const gelqf_lwork = std::max<blas_int>(1, static_cast<blas_int>(gelqf_work_query));
5023 std::vector<Scalar> gelqf_work(static_cast<std::size_t>(gelqf_lwork), Scalar{});
5024 uni20::lapack::gelqf(m, n, result.reflectors.data(), lda, result.tau.data(), gelqf_work.data(), gelqf_lwork);
5025
5026 return result;
5027}
5028
5039template <uni20::LapackReal Scalar>
5044{
5045 std::size_t const reflector_rows = static_cast<std::size_t>(factorization.reflectors.rows());
5046 std::size_t const reflector_cols = static_cast<std::size_t>(factorization.reflectors.cols());
5047 if (factorization.rank > std::min(reflector_rows, reflector_cols))
5048 {
5049 throw std::invalid_argument("apply_real_lq_factor received inconsistent LQ reflector dimensions");
5050 }
5051 if (factorization.tau.size() < factorization.rank)
5052 {
5053 throw std::invalid_argument("apply_real_lq_factor received too few LQ scalar factors");
5054 }
5055
5056 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), reflector_cols))
5057 {
5058 throw std::invalid_argument("apply_real_lq_factor left application requires matching target row count");
5059 }
5060 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), reflector_cols))
5061 {
5062 throw std::invalid_argument("apply_real_lq_factor right application requires matching target column count");
5063 }
5064 if (target.rows() == 0 || target.cols() == 0 || factorization.rank == 0)
5065 {
5066 return target;
5067 }
5068
5069 detail::ColumnMajorLapackMatrix<Scalar> reflectors = factorization.reflectors;
5070 std::vector<Scalar> tau = factorization.tau;
5071 blas_int const m = detail::checked_blas_int(target.rows());
5072 blas_int const n = detail::checked_blas_int(target.cols());
5073 blas_int const k = detail::checked_blas_int(factorization.rank);
5074 blas_int const lda = std::max<blas_int>(1, detail::checked_blas_int(reflector_rows));
5075 blas_int const ldc = std::max<blas_int>(1, m);
5076 char const side_char = detail::lapack_side(side);
5077 char const trans = detail::lapack_transpose(transpose);
5078
5079 Scalar work_query{};
5080 blas_int const query_lwork = -1;
5081 uni20::lapack::ormlq(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, &work_query,
5082 query_lwork);
5083
5084 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5085 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5086 uni20::lapack::ormlq(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, work.data(),
5087 lwork);
5088
5089 return target;
5090}
5091
5098template <uni20::LapackReal Scalar>
5100{
5101 auto compact = real_compact_lq_factorization(std::move(matrix));
5102 std::size_t const rows = static_cast<std::size_t>(compact.reflectors.rows());
5103 std::size_t const cols = static_cast<std::size_t>(compact.reflectors.cols());
5104 std::size_t const rank = compact.rank;
5105
5107 result.l = detail::ColumnMajorLapackMatrix<Scalar>(rows, rank);
5108 result.q = detail::ColumnMajorLapackMatrix<Scalar>(rank, cols);
5109 if (rank == 0)
5110 {
5111 return result;
5112 }
5113 laset(result.l, Scalar{}, Scalar{}, MatrixFill::All);
5114
5115 blas_int const n = detail::checked_blas_int(cols);
5116 blas_int const k = detail::checked_blas_int(rank);
5117 blas_int const lda = std::max<blas_int>(1, k);
5118
5119 for (std::size_t row = 0; row < rows; ++row)
5120 {
5121 for (std::size_t col = 0; col < rank && col <= row; ++col)
5122 {
5123 result.l[row, col] = compact.reflectors[row, col];
5124 }
5125 }
5126
5127 for (std::size_t row = 0; row < rank; ++row)
5128 {
5129 for (std::size_t col = 0; col < cols; ++col)
5130 {
5131 result.q[row, col] = compact.reflectors[row, col];
5132 }
5133 }
5134
5135 Scalar orglq_work_query{};
5136 blas_int const query_lwork = -1;
5137 uni20::lapack::orglq(k, n, k, result.q.data(), lda, compact.tau.data(), &orglq_work_query, query_lwork);
5138
5139 blas_int const orglq_lwork = std::max<blas_int>(1, static_cast<blas_int>(orglq_work_query));
5140 std::vector<Scalar> orglq_work(static_cast<std::size_t>(orglq_lwork), Scalar{});
5141 uni20::lapack::orglq(k, n, k, result.q.data(), lda, compact.tau.data(), orglq_work.data(), orglq_lwork);
5142
5143 return result;
5144}
5145
5153template <uni20::LapackReal Scalar>
5155{
5156 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
5157 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
5158 std::size_t const rank = std::min(rows, cols);
5159
5161 result.reflectors = std::move(matrix);
5162 result.tau.resize(rank);
5163 result.rank = rank;
5164 if (rank == 0)
5165 {
5166 return result;
5167 }
5168
5169 blas_int const m = detail::checked_blas_int(rows);
5170 blas_int const n = detail::checked_blas_int(cols);
5171 blas_int const lda = std::max<blas_int>(1, m);
5172
5173 Scalar geqlf_work_query{};
5174 blas_int const query_lwork = -1;
5175 uni20::lapack::geqlf(m, n, result.reflectors.data(), lda, result.tau.data(), &geqlf_work_query, query_lwork);
5176
5177 blas_int const geqlf_lwork = std::max<blas_int>(1, static_cast<blas_int>(geqlf_work_query));
5178 std::vector<Scalar> geqlf_work(static_cast<std::size_t>(geqlf_lwork), Scalar{});
5179 uni20::lapack::geqlf(m, n, result.reflectors.data(), lda, result.tau.data(), geqlf_work.data(), geqlf_lwork);
5180
5181 return result;
5182}
5183
5194template <uni20::LapackReal Scalar>
5199{
5200 std::size_t const reflector_rows = static_cast<std::size_t>(factorization.reflectors.rows());
5201 std::size_t const reflector_cols = static_cast<std::size_t>(factorization.reflectors.cols());
5202 if (factorization.rank > std::min(reflector_rows, reflector_cols))
5203 {
5204 throw std::invalid_argument("apply_real_ql_factor received inconsistent QL reflector dimensions");
5205 }
5206 if (factorization.tau.size() < factorization.rank)
5207 {
5208 throw std::invalid_argument("apply_real_ql_factor received too few QL scalar factors");
5209 }
5210
5211 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), reflector_rows))
5212 {
5213 throw std::invalid_argument("apply_real_ql_factor left application requires matching target row count");
5214 }
5215 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), reflector_rows))
5216 {
5217 throw std::invalid_argument("apply_real_ql_factor right application requires matching target column count");
5218 }
5219 if (target.rows() == 0 || target.cols() == 0 || factorization.rank == 0)
5220 {
5221 return target;
5222 }
5223
5224 detail::ColumnMajorLapackMatrix<Scalar> reflectors = factorization.reflectors;
5225 std::vector<Scalar> tau = factorization.tau;
5226 blas_int const m = detail::checked_blas_int(target.rows());
5227 blas_int const n = detail::checked_blas_int(target.cols());
5228 blas_int const k = detail::checked_blas_int(factorization.rank);
5229 blas_int const lda = std::max<blas_int>(1, detail::checked_blas_int(reflector_rows));
5230 blas_int const ldc = std::max<blas_int>(1, m);
5231 char const side_char = detail::lapack_side(side);
5232 char const trans = detail::lapack_transpose(transpose);
5233
5234 Scalar work_query{};
5235 blas_int const query_lwork = -1;
5236 uni20::lapack::ormql(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, &work_query,
5237 query_lwork);
5238
5239 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5240 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5241 uni20::lapack::ormql(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, work.data(),
5242 lwork);
5243
5244 return target;
5245}
5246
5253template <uni20::LapackReal Scalar>
5255{
5256 auto compact = real_compact_ql_factorization(std::move(matrix));
5257 std::size_t const rows = static_cast<std::size_t>(compact.reflectors.rows());
5258 std::size_t const cols = static_cast<std::size_t>(compact.reflectors.cols());
5259 std::size_t const rank = compact.rank;
5260
5262 result.q = detail::ColumnMajorLapackMatrix<Scalar>(rows, rank);
5263 result.l = detail::ColumnMajorLapackMatrix<Scalar>(rank, cols);
5264 if (rank == 0)
5265 {
5266 return result;
5267 }
5268 laset(result.l, Scalar{}, Scalar{}, MatrixFill::All);
5269
5270 blas_int const m = detail::checked_blas_int(rows);
5271 blas_int const k = detail::checked_blas_int(rank);
5272 blas_int const lda = std::max<blas_int>(1, m);
5273
5274 std::size_t const first_l_row = rows - rank;
5275 std::size_t const diagonal_offset = cols - rank;
5276 for (std::size_t row = 0; row < rank; ++row)
5277 {
5278 std::size_t const max_col = diagonal_offset + row;
5279 for (std::size_t col = 0; col < cols && col <= max_col; ++col)
5280 {
5281 result.l[row, col] = compact.reflectors[first_l_row + row, col];
5282 }
5283 }
5284
5285 std::size_t const first_q_col = cols - rank;
5286 for (std::size_t col = 0; col < rank; ++col)
5287 {
5288 for (std::size_t row = 0; row < rows; ++row)
5289 {
5290 result.q[row, col] = compact.reflectors[row, first_q_col + col];
5291 }
5292 }
5293
5294 Scalar orgql_work_query{};
5295 blas_int const query_lwork = -1;
5296 uni20::lapack::orgql(m, k, k, result.q.data(), lda, compact.tau.data(), &orgql_work_query, query_lwork);
5297
5298 blas_int const orgql_lwork = std::max<blas_int>(1, static_cast<blas_int>(orgql_work_query));
5299 std::vector<Scalar> orgql_work(static_cast<std::size_t>(orgql_lwork), Scalar{});
5300 uni20::lapack::orgql(m, k, k, result.q.data(), lda, compact.tau.data(), orgql_work.data(), orgql_lwork);
5301
5302 return result;
5303}
5304
5312template <uni20::LapackReal Scalar>
5314{
5315 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
5316 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
5317 std::size_t const rank = std::min(rows, cols);
5318
5320 result.reflectors = std::move(matrix);
5321 result.tau.resize(rank);
5322 result.rank = rank;
5323 if (rank == 0)
5324 {
5325 return result;
5326 }
5327
5328 blas_int const m = detail::checked_blas_int(rows);
5329 blas_int const n = detail::checked_blas_int(cols);
5330 blas_int const lda = std::max<blas_int>(1, m);
5331
5332 Scalar gerqf_work_query{};
5333 blas_int const query_lwork = -1;
5334 uni20::lapack::gerqf(m, n, result.reflectors.data(), lda, result.tau.data(), &gerqf_work_query, query_lwork);
5335
5336 blas_int const gerqf_lwork = std::max<blas_int>(1, static_cast<blas_int>(gerqf_work_query));
5337 std::vector<Scalar> gerqf_work(static_cast<std::size_t>(gerqf_lwork), Scalar{});
5338 uni20::lapack::gerqf(m, n, result.reflectors.data(), lda, result.tau.data(), gerqf_work.data(), gerqf_lwork);
5339
5340 return result;
5341}
5342
5353template <uni20::LapackReal Scalar>
5358{
5359 std::size_t const reflector_rows = static_cast<std::size_t>(factorization.reflectors.rows());
5360 std::size_t const reflector_cols = static_cast<std::size_t>(factorization.reflectors.cols());
5361 if (factorization.rank > std::min(reflector_rows, reflector_cols))
5362 {
5363 throw std::invalid_argument("apply_real_rq_factor received inconsistent RQ reflector dimensions");
5364 }
5365 if (factorization.tau.size() < factorization.rank)
5366 {
5367 throw std::invalid_argument("apply_real_rq_factor received too few RQ scalar factors");
5368 }
5369
5370 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), reflector_cols))
5371 {
5372 throw std::invalid_argument("apply_real_rq_factor left application requires matching target row count");
5373 }
5374 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), reflector_cols))
5375 {
5376 throw std::invalid_argument("apply_real_rq_factor right application requires matching target column count");
5377 }
5378 if (target.rows() == 0 || target.cols() == 0 || factorization.rank == 0)
5379 {
5380 return target;
5381 }
5382
5383 detail::ColumnMajorLapackMatrix<Scalar> reflectors = factorization.reflectors;
5384 std::vector<Scalar> tau = factorization.tau;
5385 blas_int const m = detail::checked_blas_int(target.rows());
5386 blas_int const n = detail::checked_blas_int(target.cols());
5387 blas_int const k = detail::checked_blas_int(factorization.rank);
5388 blas_int const lda = std::max<blas_int>(1, detail::checked_blas_int(reflector_rows));
5389 blas_int const ldc = std::max<blas_int>(1, m);
5390 char const side_char = detail::lapack_side(side);
5391 char const trans = detail::lapack_transpose(transpose);
5392
5393 Scalar work_query{};
5394 blas_int const query_lwork = -1;
5395 uni20::lapack::ormrq(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, &work_query,
5396 query_lwork);
5397
5398 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5399 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5400 uni20::lapack::ormrq(side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc, work.data(),
5401 lwork);
5402
5403 return target;
5404}
5405
5412template <uni20::LapackReal Scalar>
5414{
5415 auto compact = real_compact_rq_factorization(std::move(matrix));
5416 std::size_t const rows = static_cast<std::size_t>(compact.reflectors.rows());
5417 std::size_t const cols = static_cast<std::size_t>(compact.reflectors.cols());
5418 std::size_t const rank = compact.rank;
5419
5421 result.r = detail::ColumnMajorLapackMatrix<Scalar>(rows, rank);
5422 result.q = detail::ColumnMajorLapackMatrix<Scalar>(rank, cols);
5423 if (rank == 0)
5424 {
5425 return result;
5426 }
5427 laset(result.r, Scalar{}, Scalar{}, MatrixFill::All);
5428
5429 blas_int const n = detail::checked_blas_int(cols);
5430 blas_int const k = detail::checked_blas_int(rank);
5431 blas_int const lda = std::max<blas_int>(1, k);
5432
5433 std::size_t const first_r_col = cols - rank;
5434 std::size_t const diagonal_offset = rows - rank;
5435 for (std::size_t row = 0; row < rows; ++row)
5436 {
5437 for (std::size_t col = 0; col < rank; ++col)
5438 {
5439 if (row <= diagonal_offset + col)
5440 {
5441 result.r[row, col] = compact.reflectors[row, first_r_col + col];
5442 }
5443 }
5444 }
5445
5446 std::size_t const first_q_row = rows - rank;
5447 for (std::size_t row = 0; row < rank; ++row)
5448 {
5449 for (std::size_t col = 0; col < cols; ++col)
5450 {
5451 result.q[row, col] = compact.reflectors[first_q_row + row, col];
5452 }
5453 }
5454
5455 Scalar orgrq_work_query{};
5456 blas_int const query_lwork = -1;
5457 uni20::lapack::orgrq(k, n, k, result.q.data(), lda, compact.tau.data(), &orgrq_work_query, query_lwork);
5458
5459 blas_int const orgrq_lwork = std::max<blas_int>(1, static_cast<blas_int>(orgrq_work_query));
5460 std::vector<Scalar> orgrq_work(static_cast<std::size_t>(orgrq_lwork), Scalar{});
5461 uni20::lapack::orgrq(k, n, k, result.q.data(), lda, compact.tau.data(), orgrq_work.data(), orgrq_lwork);
5462
5463 return result;
5464}
5465
5474template <uni20::LapackReal Scalar>
5476{
5477 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
5478 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
5479 std::size_t const rank = std::min(rows, cols);
5480
5483 result.reflectors = std::move(matrix);
5484 result.diagonal.resize(rank);
5485 result.offdiagonal.resize(rank > 0 ? rank - 1 : 0);
5486 result.tauq.resize(rank);
5487 result.taup.resize(rank);
5488 result.upper = rows >= cols;
5489 if (rank == 0)
5490 {
5491 return result;
5492 }
5493 laset(result.bidiagonal, Scalar{}, Scalar{}, MatrixFill::All);
5494
5495 blas_int const m = detail::checked_blas_int(rows);
5496 blas_int const n = detail::checked_blas_int(cols);
5497 blas_int const lda = std::max<blas_int>(1, m);
5498
5499 Scalar work_query{};
5500 blas_int const query_lwork = -1;
5501 uni20::lapack::gebrd(m, n, result.reflectors.data(), lda, result.diagonal.data(), result.offdiagonal.data(),
5502 result.tauq.data(), result.taup.data(), &work_query, query_lwork);
5503
5504 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5505 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5506 uni20::lapack::gebrd(m, n, result.reflectors.data(), lda, result.diagonal.data(), result.offdiagonal.data(),
5507 result.tauq.data(), result.taup.data(), work.data(), lwork);
5508
5509 for (std::size_t index = 0; index < rank; ++index)
5510 {
5511 result.bidiagonal[index, index] = result.diagonal[index];
5512 }
5513 for (std::size_t index = 0; index + 1 < rank; ++index)
5514 {
5515 if (result.upper)
5516 {
5517 result.bidiagonal[index, index + 1] = result.offdiagonal[index];
5518 }
5519 else
5520 {
5521 result.bidiagonal[index + 1, index] = result.offdiagonal[index];
5522 }
5523 }
5524
5525 return result;
5526}
5527
5538template <uni20::LapackReal Scalar>
5539std::vector<Scalar> real_bidiagonal_singular_values(std::vector<Scalar> diagonal, std::vector<Scalar> offdiagonal,
5540 bool upper = true)
5541{
5542 std::size_t const order = diagonal.size();
5543 if (offdiagonal.size() != (order > 0 ? order - 1 : 0))
5544 {
5545 throw std::invalid_argument("real_bidiagonal_singular_values received inconsistent bidiagonal dimensions");
5546 }
5547 if (order == 0)
5548 {
5549 return diagonal;
5550 }
5551
5552 blas_int const n = detail::checked_blas_int(order);
5553 blas_int const zero = 0;
5554 blas_int const dummy_ld = 1;
5555 Scalar dummy{};
5556 Scalar* e = offdiagonal.empty() ? &dummy : offdiagonal.data();
5557 std::vector<Scalar> work(static_cast<std::size_t>(std::max<blas_int>(1, 4 * n)), Scalar{});
5558 uni20::lapack::bdsqr(upper ? 'U' : 'L', n, zero, zero, zero, diagonal.data(), e, &dummy, dummy_ld, &dummy, dummy_ld,
5559 &dummy, dummy_ld, work.data());
5560 return diagonal;
5561}
5562
5568template <uni20::LapackReal Scalar>
5570{
5571 return real_bidiagonal_singular_values(reduction.diagonal, reduction.offdiagonal, reduction.upper);
5572}
5573
5587template <uni20::LapackReal Scalar>
5588RealBidiagonalSvd<Scalar> real_bidiagonal_svd(std::vector<Scalar> diagonal, std::vector<Scalar> offdiagonal,
5589 bool compute_vectors, bool upper = true)
5590{
5591 std::size_t const order = diagonal.size();
5592 if (offdiagonal.size() != (order > 0 ? order - 1 : 0))
5593 {
5594 throw std::invalid_argument("real_bidiagonal_svd received inconsistent bidiagonal dimensions");
5595 }
5596
5598 result.singular_values = std::move(diagonal);
5599 if (order == 0)
5600 {
5603 return result;
5604 }
5605
5606 blas_int const n = detail::checked_blas_int(order);
5607 char const compq = compute_vectors ? 'I' : 'N';
5608 detail::ColumnMajorLapackMatrix<Scalar> u(compute_vectors ? order : 1, compute_vectors ? order : 1);
5609 detail::ColumnMajorLapackMatrix<Scalar> vt(compute_vectors ? order : 1, compute_vectors ? order : 1);
5610 blas_int const leading_dimension = compute_vectors ? n : 1;
5611 Scalar dummy_subdiagonal{};
5612 Scalar* e = offdiagonal.empty() ? &dummy_subdiagonal : offdiagonal.data();
5613 std::vector<Scalar> q(1, Scalar{});
5614 std::vector<blas_int> iq(1, 0);
5615 std::size_t const work_size = compute_vectors ? 3 * order * order + 4 * order : 4 * order;
5616 std::vector<Scalar> work(std::max<std::size_t>(1, work_size), Scalar{});
5617 std::vector<blas_int> iwork(std::max<std::size_t>(1, 8 * order), 0);
5618 uni20::lapack::bdsdc(upper ? 'U' : 'L', compq, n, result.singular_values.data(), e, u.data(), leading_dimension,
5619 vt.data(), leading_dimension, q.data(), iq.data(), work.data(), iwork.data());
5620
5621 result.u = compute_vectors ? std::move(u) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
5622 result.vt = compute_vectors ? std::move(vt) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
5623 return result;
5624}
5625
5632template <uni20::LapackReal Scalar>
5634{
5635 return real_bidiagonal_svd(reduction.diagonal, reduction.offdiagonal, compute_vectors, reduction.upper);
5636}
5637
5654template <uni20::LapackReal Scalar>
5655RealBidiagonalSvd<Scalar> real_bidiagonal_svd_index_range(std::vector<Scalar> diagonal, std::vector<Scalar> offdiagonal,
5656 std::size_t first_index, std::size_t last_index,
5657 bool compute_vectors, bool upper = true)
5658{
5659 std::size_t const order = diagonal.size();
5660 if (offdiagonal.size() != (order > 0 ? order - 1 : 0))
5661 {
5662 throw std::invalid_argument("real_bidiagonal_svd_index_range received inconsistent bidiagonal dimensions");
5663 }
5664 if (order == 0 || first_index > last_index || last_index >= order)
5665 {
5666 throw std::invalid_argument("real_bidiagonal_svd_index_range received an invalid index range");
5667 }
5668
5669 std::size_t const selected = last_index - first_index + 1;
5671
5672 blas_int const n = detail::checked_blas_int(order);
5673 blas_int const first = detail::checked_blas_int(first_index + 1);
5674 blas_int const last = detail::checked_blas_int(last_index + 1);
5675 char const jobz = compute_vectors ? 'V' : 'N';
5676 char const range = 'I';
5677 Scalar dummy_subdiagonal{};
5678 Scalar* e = offdiagonal.empty() ? &dummy_subdiagonal : offdiagonal.data();
5679 // bdsvdx uses S(1:N) and up to N vector columns internally before discarding
5680 // unselected values.
5681 std::vector<Scalar> singular_values_workspace(order, Scalar{});
5682 detail::ColumnMajorLapackMatrix<Scalar> z(compute_vectors ? 2 * order : 1, compute_vectors ? order : 1);
5683 blas_int const ldz = compute_vectors ? detail::checked_blas_int(2 * order) : 1;
5684 std::vector<Scalar> work(std::max<std::size_t>(1, 14 * order), Scalar{});
5685 std::vector<blas_int> iwork(std::max<std::size_t>(1, 12 * order), 0);
5686 blas_int selected_count = 0;
5687 uni20::lapack::bdsvdx(upper ? 'U' : 'L', jobz, range, n, diagonal.data(), e, Scalar{}, Scalar{}, first, last,
5688 selected_count, singular_values_workspace.data(), z.data(), ldz, work.data(), iwork.data());
5689
5690 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
5691 {
5692 throw std::runtime_error("LAPACK bdsvdx returned an unexpected number of singular values");
5693 }
5694
5695 result.singular_values.assign(singular_values_workspace.begin(), singular_values_workspace.begin() + selected_count);
5696
5697 if (!compute_vectors)
5698 {
5701 return result;
5702 }
5703
5704 result.u = detail::ColumnMajorLapackMatrix<Scalar>(order, selected);
5705 result.vt = detail::ColumnMajorLapackMatrix<Scalar>(selected, order);
5706 for (std::size_t col = 0; col < selected; ++col)
5707 {
5708 for (std::size_t row = 0; row < order; ++row)
5709 {
5710 result.u[row, col] = z[row, col];
5711 result.vt[col, row] = z[order + row, col];
5712 }
5713 }
5714 return result;
5715}
5716
5725template <uni20::LapackReal Scalar>
5727 std::size_t first_index, std::size_t last_index,
5728 bool compute_vectors)
5729{
5730 return real_bidiagonal_svd_index_range(reduction.diagonal, reduction.offdiagonal, first_index, last_index,
5731 compute_vectors, reduction.upper);
5732}
5733
5739template <uni20::LapackReal Scalar>
5740detail::ColumnMajorLapackMatrix<Scalar>
5742{
5743 std::size_t const rows = static_cast<std::size_t>(reduction.reflectors.rows());
5744 std::size_t const cols = static_cast<std::size_t>(reduction.reflectors.cols());
5745 detail::ColumnMajorLapackMatrix<Scalar> factor(rows, rows);
5746 laset(factor, Scalar{1}, Scalar{}, MatrixFill::All);
5747 if (rows == 0 || cols == 0)
5748 {
5749 return factor;
5750 }
5751 if (reduction.tauq.size() < std::min(rows, cols))
5752 {
5753 throw std::invalid_argument("real_bidiagonal_left_orthogonal_factor received too few Q scalar factors");
5754 }
5755
5756 for (std::size_t col = 0; col < std::min(cols, rows); ++col)
5757 {
5758 for (std::size_t row = 0; row < rows; ++row)
5759 {
5760 factor[row, col] = reduction.reflectors[row, col];
5761 }
5762 }
5763
5764 blas_int const m = detail::checked_blas_int(rows);
5765 blas_int const n = detail::checked_blas_int(rows);
5766 blas_int const k = detail::checked_blas_int(cols);
5767 blas_int const lda = std::max<blas_int>(1, m);
5768
5769 Scalar work_query{};
5770 blas_int const query_lwork = -1;
5771 uni20::lapack::orgbr('Q', m, n, k, factor.data(), lda, const_cast<Scalar*>(reduction.tauq.data()), &work_query,
5772 query_lwork);
5773
5774 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5775 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5776 uni20::lapack::orgbr('Q', m, n, k, factor.data(), lda, const_cast<Scalar*>(reduction.tauq.data()), work.data(),
5777 lwork);
5778
5779 return factor;
5780}
5781
5788template <uni20::LapackReal Scalar>
5789detail::ColumnMajorLapackMatrix<Scalar>
5791{
5792 std::size_t const rows = static_cast<std::size_t>(reduction.reflectors.rows());
5793 std::size_t const cols = static_cast<std::size_t>(reduction.reflectors.cols());
5794 detail::ColumnMajorLapackMatrix<Scalar> factor(cols, cols);
5795 laset(factor, Scalar{1}, Scalar{}, MatrixFill::All);
5796 if (rows == 0 || cols == 0)
5797 {
5798 return factor;
5799 }
5800 if (reduction.taup.size() < std::min(rows, cols))
5801 {
5802 throw std::invalid_argument("real_bidiagonal_right_orthogonal_factor_transpose received too few P scalar factors");
5803 }
5804
5805 for (std::size_t col = 0; col < cols; ++col)
5806 {
5807 for (std::size_t row = 0; row < std::min(rows, cols); ++row)
5808 {
5809 factor[row, col] = reduction.reflectors[row, col];
5810 }
5811 }
5812
5813 blas_int const m = detail::checked_blas_int(cols);
5814 blas_int const n = detail::checked_blas_int(cols);
5815 blas_int const k = detail::checked_blas_int(rows);
5816 blas_int const lda = std::max<blas_int>(1, m);
5817
5818 Scalar work_query{};
5819 blas_int const query_lwork = -1;
5820 uni20::lapack::orgbr('P', m, n, k, factor.data(), lda, const_cast<Scalar*>(reduction.taup.data()), &work_query,
5821 query_lwork);
5822
5823 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5824 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5825 uni20::lapack::orgbr('P', m, n, k, factor.data(), lda, const_cast<Scalar*>(reduction.taup.data()), work.data(),
5826 lwork);
5827
5828 return factor;
5829}
5830
5840template <uni20::LapackReal Scalar>
5841detail::ColumnMajorLapackMatrix<Scalar>
5845{
5846 std::size_t const rows = static_cast<std::size_t>(reduction.reflectors.rows());
5847 std::size_t const cols = static_cast<std::size_t>(reduction.reflectors.cols());
5848 std::size_t const rank = std::min(rows, cols);
5849 if (reduction.tauq.size() < rank)
5850 {
5851 throw std::invalid_argument("apply_real_bidiagonal_left_factor received too few Q scalar factors");
5852 }
5853 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), rows))
5854 {
5855 throw std::invalid_argument(
5856 "apply_real_bidiagonal_left_factor left application requires matching target row count");
5857 }
5858 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), rows))
5859 {
5860 throw std::invalid_argument(
5861 "apply_real_bidiagonal_left_factor right application requires matching target column count");
5862 }
5863 if (target.rows() == 0 || target.cols() == 0 || cols == 0)
5864 {
5865 return target;
5866 }
5867
5869 std::vector<Scalar> tau = reduction.tauq;
5870 blas_int const m = detail::checked_blas_int(target.rows());
5871 blas_int const n = detail::checked_blas_int(target.cols());
5872 blas_int const k = detail::checked_blas_int(cols);
5873 blas_int const lda = std::max<blas_int>(1, detail::checked_blas_int(rows));
5874 blas_int const ldc = std::max<blas_int>(1, m);
5875 char const side_char = detail::lapack_side(side);
5876 char const trans = detail::lapack_transpose(transpose);
5877
5878 Scalar work_query{};
5879 blas_int const query_lwork = -1;
5880 uni20::lapack::ormbr('Q', side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc,
5881 &work_query, query_lwork);
5882
5883 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5884 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5885 uni20::lapack::ormbr('Q', side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc,
5886 work.data(), lwork);
5887
5888 return target;
5889}
5890
5900template <uni20::LapackReal Scalar>
5901detail::ColumnMajorLapackMatrix<Scalar>
5905{
5906 std::size_t const rows = static_cast<std::size_t>(reduction.reflectors.rows());
5907 std::size_t const cols = static_cast<std::size_t>(reduction.reflectors.cols());
5908 std::size_t const rank = std::min(rows, cols);
5909 if (reduction.taup.size() < rank)
5910 {
5911 throw std::invalid_argument("apply_real_bidiagonal_right_factor received too few P scalar factors");
5912 }
5913 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), cols))
5914 {
5915 throw std::invalid_argument(
5916 "apply_real_bidiagonal_right_factor left application requires matching target row count");
5917 }
5918 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), cols))
5919 {
5920 throw std::invalid_argument(
5921 "apply_real_bidiagonal_right_factor right application requires matching target column count");
5922 }
5923 if (target.rows() == 0 || target.cols() == 0 || rows == 0)
5924 {
5925 return target;
5926 }
5927
5929 std::vector<Scalar> tau = reduction.taup;
5930 blas_int const m = detail::checked_blas_int(target.rows());
5931 blas_int const n = detail::checked_blas_int(target.cols());
5932 blas_int const k = detail::checked_blas_int(rows);
5933 blas_int const lda = std::max<blas_int>(1, detail::checked_blas_int(rows));
5934 blas_int const ldc = std::max<blas_int>(1, m);
5935 char const side_char = detail::lapack_side(side);
5936 char const trans = detail::lapack_transpose(transpose);
5937
5938 Scalar work_query{};
5939 blas_int const query_lwork = -1;
5940 uni20::lapack::ormbr('P', side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc,
5941 &work_query, query_lwork);
5942
5943 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5944 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5945 uni20::lapack::ormbr('P', side_char, trans, m, n, k, reflectors.data(), lda, tau.data(), target.data(), ldc,
5946 work.data(), lwork);
5947
5948 return target;
5949}
5950
5959template <uni20::LapackReal Scalar>
5961{
5962 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
5963 {
5964 throw std::invalid_argument("real_hessenberg_reduction requires a square matrix");
5965 }
5966
5967 std::size_t const n = static_cast<std::size_t>(matrix.rows());
5969 result.first = 0;
5970 result.last_exclusive = n;
5971 result.tau.resize(n > 0 ? n - 1 : 0);
5972 if (n == 0)
5973 {
5976 return result;
5977 }
5978
5979 if (n > 1)
5980 {
5981 blas_int const order = detail::checked_blas_int(n);
5982 blas_int const first = 1;
5983 blas_int const last = order;
5984
5985 Scalar work_query{};
5986 blas_int const query_lwork = -1;
5987 uni20::lapack::gehrd(order, first, last, matrix.data(), order, result.tau.data(), &work_query, query_lwork);
5988
5989 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
5990 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
5991 uni20::lapack::gehrd(order, first, last, matrix.data(), order, result.tau.data(), work.data(), lwork);
5992 }
5993
5994 result.reflectors = matrix;
5995 result.hessenberg = std::move(matrix);
5996 for (std::size_t col = 0; col < n; ++col)
5997 {
5998 for (std::size_t row = col + 2; row < n; ++row)
5999 {
6000 result.hessenberg[row, col] = Scalar{};
6001 }
6002 }
6003 return result;
6004}
6005
6012template <uni20::LapackReal Scalar>
6013detail::ColumnMajorLapackMatrix<Scalar>
6015{
6016 if (!std::cmp_equal(reduction.reflectors.rows(), reduction.reflectors.cols()))
6017 {
6018 throw std::invalid_argument("real_hessenberg_orthogonal_factor requires square compact reflector storage");
6019 }
6020
6021 std::size_t const n = static_cast<std::size_t>(reduction.reflectors.rows());
6022 if (reduction.tau.size() != (n > 0 ? n - 1 : 0))
6023 {
6024 throw std::invalid_argument("real_hessenberg_orthogonal_factor received inconsistent tau data");
6025 }
6026 if (reduction.first > reduction.last_exclusive || reduction.last_exclusive > n)
6027 {
6028 throw std::invalid_argument("real_hessenberg_orthogonal_factor received invalid active interval");
6029 }
6030
6032 for (std::size_t diagonal = 0; diagonal < n; ++diagonal)
6033 {
6034 q[diagonal, diagonal] = Scalar{1};
6035 }
6036 if (n <= 1)
6037 {
6038 return q;
6039 }
6040
6041 q = reduction.reflectors;
6042 blas_int const order = detail::checked_blas_int(n);
6043 blas_int const first = detail::checked_blas_int(reduction.first + 1);
6044 blas_int const last = detail::checked_blas_int(reduction.last_exclusive);
6045
6046 Scalar work_query{};
6047 blas_int const query_lwork = -1;
6048 uni20::lapack::orghr(order, first, last, q.data(), order, reduction.tau.data(), &work_query, query_lwork);
6049
6050 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6051 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6052 uni20::lapack::orghr(order, first, last, q.data(), order, reduction.tau.data(), work.data(), lwork);
6053 return q;
6054}
6055
6066template <uni20::LapackReal Scalar>
6070{
6071 if (!std::cmp_equal(reduction.reflectors.rows(), reduction.reflectors.cols()))
6072 {
6073 throw std::invalid_argument("apply_real_hessenberg_orthogonal_factor requires square compact reflector storage");
6074 }
6075
6076 std::size_t const order_size = static_cast<std::size_t>(reduction.reflectors.rows());
6077 if (reduction.tau.size() != (order_size > 0 ? order_size - 1 : 0))
6078 {
6079 throw std::invalid_argument("apply_real_hessenberg_orthogonal_factor received inconsistent tau data");
6080 }
6081 if (reduction.first > reduction.last_exclusive || reduction.last_exclusive > order_size)
6082 {
6083 throw std::invalid_argument("apply_real_hessenberg_orthogonal_factor received invalid active interval");
6084 }
6085 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), order_size))
6086 {
6087 throw std::invalid_argument("apply_real_hessenberg_orthogonal_factor left application requires matching rows");
6088 }
6089 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), order_size))
6090 {
6091 throw std::invalid_argument("apply_real_hessenberg_orthogonal_factor right application requires matching columns");
6092 }
6093 if (target.rows() == 0 || target.cols() == 0 || order_size <= 1)
6094 {
6095 return target;
6096 }
6097
6099 blas_int const m = detail::checked_blas_int(target.rows());
6100 blas_int const n = detail::checked_blas_int(target.cols());
6101 blas_int const order = detail::checked_blas_int(order_size);
6102 blas_int const first = detail::checked_blas_int(reduction.first + 1);
6103 blas_int const last = detail::checked_blas_int(reduction.last_exclusive);
6104 blas_int const lda = std::max<blas_int>(1, order);
6105 blas_int const ldc = std::max<blas_int>(1, m);
6106 char const side_char = detail::lapack_side(side);
6107 char const trans = detail::lapack_transpose(transpose);
6108
6109 Scalar work_query{};
6110 blas_int const query_lwork = -1;
6111 uni20::lapack::ormhr(side_char, trans, m, n, first, last, reflectors.data(), lda, reduction.tau.data(), target.data(),
6112 ldc, &work_query, query_lwork);
6113
6114 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6115 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6116 uni20::lapack::ormhr(side_char, trans, m, n, first, last, reflectors.data(), lda, reduction.tau.data(), target.data(),
6117 ldc, work.data(), lwork);
6118 return target;
6119}
6120
6132template <uni20::LapackReal Scalar>
6133RealSymmetricTridiagonalReduction<Scalar>
6135 MatrixFill triangle = MatrixFill::Upper)
6136{
6137 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
6138 {
6139 throw std::invalid_argument("real_symmetric_tridiagonal_reduction requires a square matrix");
6140 }
6141
6142 char const uplo = detail::lapack_uplo(triangle);
6143 std::size_t const n = static_cast<std::size_t>(matrix.rows());
6145 result.triangle = triangle;
6146 result.diagonal.resize(n);
6147 result.offdiagonal.resize(n > 0 ? n - 1 : 0);
6148 result.tau.resize(n > 0 ? n - 1 : 0);
6149 if (n == 0)
6150 {
6153 return result;
6154 }
6155
6156 blas_int const order = detail::checked_blas_int(n);
6157 Scalar work_query{};
6158 blas_int const query_lwork = -1;
6159 uni20::lapack::sytrd(uplo, order, matrix.data(), order, result.diagonal.data(), result.offdiagonal.data(),
6160 result.tau.data(), &work_query, query_lwork);
6161
6162 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6163 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6164 uni20::lapack::sytrd(uplo, order, matrix.data(), order, result.diagonal.data(), result.offdiagonal.data(),
6165 result.tau.data(), work.data(), lwork);
6166
6167 result.reflectors = matrix;
6169 laset(result.tridiagonal, Scalar{}, Scalar{}, MatrixFill::All);
6170 for (std::size_t index = 0; index < n; ++index)
6171 {
6172 result.tridiagonal[index, index] = result.diagonal[index];
6173 }
6174 for (std::size_t index = 0; index + 1 < n; ++index)
6175 {
6176 result.tridiagonal[index, index + 1] = result.offdiagonal[index];
6177 result.tridiagonal[index + 1, index] = result.offdiagonal[index];
6178 }
6179 return result;
6180}
6181
6189template <uni20::LapackReal Scalar>
6190detail::ColumnMajorLapackMatrix<Scalar>
6192{
6193 if (!std::cmp_equal(reduction.reflectors.rows(), reduction.reflectors.cols()))
6194 {
6195 throw std::invalid_argument(
6196 "real_symmetric_tridiagonal_orthogonal_factor requires square compact reflector storage");
6197 }
6198
6199 std::size_t const n = static_cast<std::size_t>(reduction.reflectors.rows());
6200 if (reduction.diagonal.size() != n || reduction.offdiagonal.size() != (n > 0 ? n - 1 : 0) ||
6201 reduction.tau.size() != (n > 0 ? n - 1 : 0))
6202 {
6203 throw std::invalid_argument("real_symmetric_tridiagonal_orthogonal_factor received inconsistent reduction data");
6204 }
6205
6207 for (std::size_t diagonal = 0; diagonal < n; ++diagonal)
6208 {
6209 q[diagonal, diagonal] = Scalar{1};
6210 }
6211 if (n <= 1)
6212 {
6213 return q;
6214 }
6215
6216 q = reduction.reflectors;
6217 blas_int const order = detail::checked_blas_int(n);
6218 char const uplo = detail::lapack_uplo(reduction.triangle);
6219
6220 Scalar work_query{};
6221 blas_int const query_lwork = -1;
6222 uni20::lapack::orgtr(uplo, order, q.data(), order, reduction.tau.data(), &work_query, query_lwork);
6223
6224 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6225 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6226 uni20::lapack::orgtr(uplo, order, q.data(), order, reduction.tau.data(), work.data(), lwork);
6227 return q;
6228}
6229
6241template <uni20::LapackReal Scalar>
6245{
6246 if (!std::cmp_equal(reduction.reflectors.rows(), reduction.reflectors.cols()))
6247 {
6248 throw std::invalid_argument(
6249 "apply_real_symmetric_tridiagonal_orthogonal_factor requires square compact reflector storage");
6250 }
6251
6252 std::size_t const order_size = static_cast<std::size_t>(reduction.reflectors.rows());
6253 if (reduction.diagonal.size() != order_size ||
6254 reduction.offdiagonal.size() != (order_size > 0 ? order_size - 1 : 0) ||
6255 reduction.tau.size() != (order_size > 0 ? order_size - 1 : 0))
6256 {
6257 throw std::invalid_argument(
6258 "apply_real_symmetric_tridiagonal_orthogonal_factor received inconsistent reduction data");
6259 }
6260 if (side == MatrixSide::Left && !std::cmp_equal(target.rows(), order_size))
6261 {
6262 throw std::invalid_argument(
6263 "apply_real_symmetric_tridiagonal_orthogonal_factor left application requires matching rows");
6264 }
6265 if (side == MatrixSide::Right && !std::cmp_equal(target.cols(), order_size))
6266 {
6267 throw std::invalid_argument(
6268 "apply_real_symmetric_tridiagonal_orthogonal_factor right application requires matching columns");
6269 }
6270 if (target.rows() == 0 || target.cols() == 0 || order_size <= 1)
6271 {
6272 return target;
6273 }
6274
6276 blas_int const m = detail::checked_blas_int(target.rows());
6277 blas_int const n = detail::checked_blas_int(target.cols());
6278 blas_int const order = detail::checked_blas_int(order_size);
6279 blas_int const lda = std::max<blas_int>(1, order);
6280 blas_int const ldc = std::max<blas_int>(1, m);
6281 char const side_char = detail::lapack_side(side);
6282 char const uplo = detail::lapack_uplo(reduction.triangle);
6283 char const trans = detail::lapack_transpose(transpose);
6284
6285 Scalar work_query{};
6286 blas_int const query_lwork = -1;
6287 uni20::lapack::ormtr(side_char, uplo, trans, m, n, reflectors.data(), lda, reduction.tau.data(), target.data(), ldc,
6288 &work_query, query_lwork);
6289
6290 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6291 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6292 uni20::lapack::ormtr(side_char, uplo, trans, m, n, reflectors.data(), lda, reduction.tau.data(), target.data(), ldc,
6293 work.data(), lwork);
6294 return target;
6295}
6296
6304template <uni20::LapackReal Scalar>
6306{
6307 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
6308 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
6309 std::size_t const rank = std::min(rows, cols);
6310
6312 result.q = detail::ColumnMajorLapackMatrix<Scalar>(rows, rank);
6313 result.r = detail::ColumnMajorLapackMatrix<Scalar>(rank, cols);
6314 result.pivot_columns.resize(cols);
6315 std::iota(result.pivot_columns.begin(), result.pivot_columns.end(), std::size_t{0});
6316 if (rank == 0)
6317 {
6318 return result;
6319 }
6320 laset(result.r, Scalar{}, Scalar{}, MatrixFill::All);
6321
6322 blas_int const m = detail::checked_blas_int(rows);
6323 blas_int const n = detail::checked_blas_int(cols);
6324 blas_int const k = detail::checked_blas_int(rank);
6325 blas_int const lda = std::max<blas_int>(1, m);
6326 std::vector<Scalar> tau(rank, Scalar{});
6327 std::vector<blas_int> jpvt(cols, 0);
6328
6329 Scalar geqp3_work_query{};
6330 blas_int const query_lwork = -1;
6331 uni20::lapack::geqp3(m, n, matrix.data(), lda, jpvt.data(), tau.data(), &geqp3_work_query, query_lwork);
6332
6333 std::fill(jpvt.begin(), jpvt.end(), 0);
6334 blas_int const geqp3_lwork = std::max<blas_int>(1, static_cast<blas_int>(geqp3_work_query));
6335 std::vector<Scalar> geqp3_work(static_cast<std::size_t>(geqp3_lwork), Scalar{});
6336 uni20::lapack::geqp3(m, n, matrix.data(), lda, jpvt.data(), tau.data(), geqp3_work.data(), geqp3_lwork);
6337
6338 for (std::size_t col = 0; col < cols; ++col)
6339 {
6340 if (jpvt[col] <= 0 || static_cast<std::size_t>(jpvt[col]) > cols)
6341 {
6342 throw std::runtime_error("LAPACK geqp3 returned an invalid column pivot");
6343 }
6344 result.pivot_columns[col] = static_cast<std::size_t>(jpvt[col] - 1);
6345 }
6346
6347 for (std::size_t row = 0; row < rank; ++row)
6348 {
6349 for (std::size_t col = row; col < cols; ++col)
6350 {
6351 result.r[row, col] = matrix[row, col];
6352 }
6353 }
6354
6355 for (std::size_t col = 0; col < rank; ++col)
6356 {
6357 for (std::size_t row = 0; row < rows; ++row)
6358 {
6359 result.q[row, col] = matrix[row, col];
6360 }
6361 }
6362
6363 Scalar orgqr_work_query{};
6364 uni20::lapack::orgqr(m, k, k, result.q.data(), lda, tau.data(), &orgqr_work_query, query_lwork);
6365
6366 blas_int const orgqr_lwork = std::max<blas_int>(1, static_cast<blas_int>(orgqr_work_query));
6367 std::vector<Scalar> orgqr_work(static_cast<std::size_t>(orgqr_lwork), Scalar{});
6368 uni20::lapack::orgqr(m, k, k, result.q.data(), lda, tau.data(), orgqr_work.data(), orgqr_lwork);
6369
6370 return result;
6371}
6372
6382template <uni20::LapackReal Scalar>
6384 bool compute_vectors)
6385{
6386 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
6387 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
6388 std::size_t const singular_value_count = std::min(rows, cols);
6389
6391 result.singular_values.resize(singular_value_count);
6392 if (singular_value_count == 0)
6393 {
6394 result.left_singular_vectors = compute_vectors ? detail::ColumnMajorLapackMatrix<Scalar>(rows, rows)
6398 return result;
6399 }
6400
6401 blas_int const m = detail::checked_blas_int(rows);
6402 blas_int const n = detail::checked_blas_int(cols);
6403 blas_int const lda = std::max<blas_int>(1, m);
6404 char const jobu = compute_vectors ? 'A' : 'N';
6405 char const jobvt = compute_vectors ? 'A' : 'N';
6406 detail::ColumnMajorLapackMatrix<Scalar> left(compute_vectors ? rows : 1, compute_vectors ? rows : 1);
6407 detail::ColumnMajorLapackMatrix<Scalar> right_transpose(compute_vectors ? cols : 1, compute_vectors ? cols : 1);
6408 blas_int const ldu = compute_vectors ? std::max<blas_int>(1, m) : 1;
6409 blas_int const ldvt = compute_vectors ? std::max<blas_int>(1, n) : 1;
6410
6411 Scalar work_query{};
6412 blas_int const query_lwork = -1;
6413 uni20::lapack::gesvd(jobu, jobvt, m, n, matrix.data(), lda, result.singular_values.data(), left.data(), ldu,
6414 right_transpose.data(), ldvt, &work_query, query_lwork);
6415
6416 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6417 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6418 uni20::lapack::gesvd(jobu, jobvt, m, n, matrix.data(), lda, result.singular_values.data(), left.data(), ldu,
6419 right_transpose.data(), ldvt, work.data(), lwork);
6420
6421 result.left_singular_vectors = compute_vectors ? std::move(left) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6423 compute_vectors ? std::move(right_transpose) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6424 return result;
6425}
6426
6437template <uni20::LapackReal Scalar>
6438RealSingularValueDecomposition<Scalar>
6440 bool compute_vectors)
6441{
6442 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
6443 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
6444 std::size_t const singular_value_count = std::min(rows, cols);
6445
6447 result.singular_values.resize(singular_value_count);
6448 if (singular_value_count == 0)
6449 {
6450 result.left_singular_vectors = compute_vectors ? detail::ColumnMajorLapackMatrix<Scalar>(rows, rows)
6454 return result;
6455 }
6456
6457 blas_int const m = detail::checked_blas_int(rows);
6458 blas_int const n = detail::checked_blas_int(cols);
6459 blas_int const lda = std::max<blas_int>(1, m);
6460 char const jobz = compute_vectors ? 'A' : 'N';
6461 detail::ColumnMajorLapackMatrix<Scalar> left(compute_vectors ? rows : 1, compute_vectors ? rows : 1);
6462 detail::ColumnMajorLapackMatrix<Scalar> right_transpose(compute_vectors ? cols : 1, compute_vectors ? cols : 1);
6463 blas_int const ldu = compute_vectors ? std::max<blas_int>(1, m) : 1;
6464 blas_int const ldvt = compute_vectors ? std::max<blas_int>(1, n) : 1;
6465 std::vector<blas_int> iwork(
6466 static_cast<std::size_t>(std::max<blas_int>(1, 8 * detail::checked_blas_int(singular_value_count))));
6467
6468 Scalar work_query{};
6469 blas_int const query_lwork = -1;
6470 uni20::lapack::gesdd(jobz, m, n, matrix.data(), lda, result.singular_values.data(), left.data(), ldu,
6471 right_transpose.data(), ldvt, &work_query, query_lwork, iwork.data());
6472
6473 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6474 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6475 uni20::lapack::gesdd(jobz, m, n, matrix.data(), lda, result.singular_values.data(), left.data(), ldu,
6476 right_transpose.data(), ldvt, work.data(), lwork, iwork.data());
6477
6478 result.left_singular_vectors = compute_vectors ? std::move(left) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6480 compute_vectors ? std::move(right_transpose) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6481 return result;
6482}
6483
6496template <uni20::LapackReal Scalar>
6497RealSingularValueDecomposition<Scalar>
6499 std::size_t last_index, bool compute_vectors)
6500{
6501 std::size_t const rows = static_cast<std::size_t>(matrix.rows());
6502 std::size_t const cols = static_cast<std::size_t>(matrix.cols());
6503 std::size_t const singular_value_count = std::min(rows, cols);
6504 if (singular_value_count == 0 || first_index > last_index || last_index >= singular_value_count)
6505 {
6506 throw std::invalid_argument("real_singular_value_decomposition_index_range received an invalid index range");
6507 }
6508
6509 std::size_t const selected = last_index - first_index + 1;
6511
6512 blas_int const m = detail::checked_blas_int(rows);
6513 blas_int const n = detail::checked_blas_int(cols);
6514 blas_int const lda = std::max<blas_int>(1, m);
6515 blas_int const first = detail::checked_blas_int(first_index + 1);
6516 blas_int const last = detail::checked_blas_int(last_index + 1);
6517 char const jobu = compute_vectors ? 'V' : 'N';
6518 char const jobvt = compute_vectors ? 'V' : 'N';
6519 char const range = 'I';
6520 std::vector<Scalar> singular_values_workspace(singular_value_count, Scalar{});
6521 detail::ColumnMajorLapackMatrix<Scalar> left(compute_vectors ? rows : 1, compute_vectors ? selected : 1);
6522 detail::ColumnMajorLapackMatrix<Scalar> right_transpose(compute_vectors ? selected : 1, compute_vectors ? cols : 1);
6523 blas_int const ldu = compute_vectors ? std::max<blas_int>(1, m) : 1;
6524 blas_int const ldvt = compute_vectors ? detail::checked_blas_int(selected) : 1;
6525 std::vector<blas_int> iwork(
6526 static_cast<std::size_t>(std::max<blas_int>(1, 12 * detail::checked_blas_int(singular_value_count))));
6527 blas_int selected_count = 0;
6528
6529 Scalar work_query{};
6530 blas_int const query_lwork = -1;
6531 uni20::lapack::gesvdx(jobu, jobvt, range, m, n, matrix.data(), lda, Scalar{}, Scalar{}, first, last, selected_count,
6532 singular_values_workspace.data(), left.data(), ldu, right_transpose.data(), ldvt, &work_query,
6533 query_lwork, iwork.data());
6534
6535 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6536 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6537 uni20::lapack::gesvdx(jobu, jobvt, range, m, n, matrix.data(), lda, Scalar{}, Scalar{}, first, last, selected_count,
6538 singular_values_workspace.data(), left.data(), ldu, right_transpose.data(), ldvt, work.data(),
6539 lwork, iwork.data());
6540
6541 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
6542 {
6543 throw std::runtime_error("LAPACK gesvdx returned an unexpected number of singular values");
6544 }
6545
6546 result.singular_values.assign(singular_values_workspace.begin(), singular_values_workspace.begin() + selected_count);
6547 result.left_singular_vectors = compute_vectors ? std::move(left) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6549 compute_vectors ? std::move(right_transpose) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6550 return result;
6551}
6552
6561
6562template <uni20::LapackReal Scalar>
6564 std::vector<Scalar> subdiagonal,
6565 bool compute_vectors)
6566{
6567 std::size_t const n = diagonal.size();
6568 if (subdiagonal.size() + 1 != n && !(n == 0 && subdiagonal.empty()))
6569 {
6570 throw std::invalid_argument(
6571 "symmetric_tridiagonal_eigensystem_divide_and_conquer received inconsistent diagonal sizes");
6572 }
6573
6575 result.eigenvalues = std::move(diagonal);
6576 if (n == 0)
6577 {
6579 return result;
6580 }
6581
6582 blas_int const order = detail::checked_blas_int(n);
6583 char const jobz = compute_vectors ? 'V' : 'N';
6584 detail::ColumnMajorLapackMatrix<Scalar> z(compute_vectors ? n : 1, compute_vectors ? n : 1);
6585 blas_int const ldz = compute_vectors ? order : 1;
6586 Scalar dummy_subdiagonal{};
6587 Scalar* e = subdiagonal.empty() ? &dummy_subdiagonal : subdiagonal.data();
6588
6589 Scalar work_query{};
6590 blas_int iwork_query = 0;
6591 blas_int const query_lwork = -1;
6592 uni20::lapack::stevd(jobz, order, result.eigenvalues.data(), e, z.data(), ldz, &work_query, query_lwork, &iwork_query,
6593 query_lwork);
6594
6595 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6596 blas_int const liwork = std::max<blas_int>(1, iwork_query);
6597 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6598 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
6599 uni20::lapack::stevd(jobz, order, result.eigenvalues.data(), e, z.data(), ldz, work.data(), lwork, iwork.data(),
6600 liwork);
6601
6602 result.eigenvectors = compute_vectors ? std::move(z) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6603 return result;
6604}
6605
6620template <uni20::LapackReal Scalar>
6621TridiagonalEigensystem<Scalar>
6622symmetric_tridiagonal_eigensystem_index_range(std::vector<Scalar> diagonal, std::vector<Scalar> subdiagonal,
6623 std::size_t first_index, std::size_t last_index, bool compute_vectors)
6624{
6625 std::size_t const n = diagonal.size();
6626 if (subdiagonal.size() + 1 != n)
6627 {
6628 throw std::invalid_argument("symmetric_tridiagonal_eigensystem_index_range received inconsistent diagonal sizes");
6629 }
6630 if (n == 0 || first_index > last_index || last_index >= n)
6631 {
6632 throw std::invalid_argument("symmetric_tridiagonal_eigensystem_index_range received an invalid index range");
6633 }
6634
6635 std::size_t const selected = last_index - first_index + 1;
6637 result.eigenvalues.resize(selected);
6638
6639 blas_int const order = detail::checked_blas_int(n);
6640 blas_int const first = detail::checked_blas_int(first_index + 1);
6641 blas_int const last = detail::checked_blas_int(last_index + 1);
6642 char const jobz = compute_vectors ? 'V' : 'N';
6643 char const range = 'I';
6644 detail::ColumnMajorLapackMatrix<Scalar> eigenvectors(compute_vectors ? n : 1, compute_vectors ? selected : 1);
6645 blas_int const ldz = compute_vectors ? order : 1;
6646 std::vector<blas_int> support(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order)), 0);
6647 Scalar dummy_subdiagonal{};
6648 Scalar* e = subdiagonal.empty() ? &dummy_subdiagonal : subdiagonal.data();
6649 Scalar work_query{};
6650 blas_int iwork_query = 0;
6651 blas_int selected_count = 0;
6652 blas_int const query_lwork = -1;
6653 uni20::lapack::stevr(jobz, range, order, diagonal.data(), e, Scalar{}, Scalar{}, first, last, Scalar{},
6654 selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, support.data(), &work_query,
6655 query_lwork, &iwork_query, query_lwork);
6656
6657 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6658 blas_int const liwork = std::max<blas_int>(1, iwork_query);
6659 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6660 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
6661 selected_count = 0;
6662 uni20::lapack::stevr(jobz, range, order, diagonal.data(), e, Scalar{}, Scalar{}, first, last, Scalar{},
6663 selected_count, result.eigenvalues.data(), eigenvectors.data(), ldz, support.data(), work.data(),
6664 lwork, iwork.data(), liwork);
6665
6666 if (selected_count < 0 || static_cast<std::size_t>(selected_count) != selected)
6667 {
6668 throw std::runtime_error("LAPACK stevr returned an unexpected number of eigenvalues");
6669 }
6670
6671 result.eigenvectors = compute_vectors ? std::move(eigenvectors) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6672 return result;
6673}
6674
6683
6684template <uni20::LapackReal Scalar>
6686 std::vector<std::size_t> const& selected_blocks)
6687{
6688 if (!std::cmp_equal(decomposition.schur_form.rows(), decomposition.schur_form.cols()))
6689 {
6690 throw std::invalid_argument("real_schur_selected_subspace requires a square Schur form");
6691 }
6692
6693 std::size_t const n = static_cast<std::size_t>(decomposition.schur_form.rows());
6695 if (n == 0 || selected_blocks.empty())
6696 {
6697 result.decomposition = std::move(decomposition);
6698 return result;
6699 }
6700 if (decomposition.eigenvalues.size() != n)
6701 {
6702 throw std::invalid_argument("real_schur_selected_subspace received inconsistent Schur eigenvalue data");
6703 }
6704 bool const update_vectors = decomposition.schur_vectors.rows() != 0 || decomposition.schur_vectors.cols() != 0;
6705 if (update_vectors && (!std::cmp_equal(decomposition.schur_vectors.rows(), n) ||
6706 !std::cmp_equal(decomposition.schur_vectors.cols(), n)))
6707 {
6708 throw std::invalid_argument("real_schur_selected_subspace received inconsistent Schur vectors");
6709 }
6710 if (decomposition.blocks.empty())
6711 {
6712 decomposition.blocks = detail::real_schur_blocks(decomposition.eigenvalues);
6713 }
6714
6715 std::vector<bool> seen_blocks(decomposition.blocks.size(), false);
6716 std::vector<blas_int> select(n, 0);
6717 std::size_t expected_selected_dimension = 0;
6718 for (std::size_t const block_index : selected_blocks)
6719 {
6720 if (block_index >= decomposition.blocks.size())
6721 {
6722 throw std::invalid_argument("real_schur_selected_subspace received an out-of-range selected block index");
6723 }
6724 if (seen_blocks[block_index])
6725 {
6726 throw std::invalid_argument("real_schur_selected_subspace received a duplicate selected block index");
6727 }
6728 seen_blocks[block_index] = true;
6729 auto const& block = decomposition.blocks[block_index];
6730 if (block.begin + block.size > n)
6731 {
6732 throw std::invalid_argument("real_schur_selected_subspace received inconsistent Schur block metadata");
6733 }
6734 for (std::size_t offset = 0; offset < block.size; ++offset)
6735 {
6736 select[block.begin + offset] = 1;
6737 }
6738 expected_selected_dimension += block.size;
6739 }
6740
6741 detail::ColumnMajorLapackMatrix<Scalar> schur_form = std::move(decomposition.schur_form);
6743 update_vectors ? std::move(decomposition.schur_vectors) : detail::ColumnMajorLapackMatrix<Scalar>(1, 1);
6744 blas_int const order = detail::checked_blas_int(n);
6745 blas_int const ldq = update_vectors ? order : 1;
6746 char const job = 'B';
6747 char const compq = update_vectors ? 'V' : 'N';
6748 std::vector<Scalar> wr(n, Scalar{});
6749 std::vector<Scalar> wi(n, Scalar{});
6750 blas_int selected_dimension = 0;
6751 Scalar reciprocal_eigenvalue_cluster_condition{};
6752 Scalar reciprocal_invariant_subspace_condition{};
6753
6754 Scalar work_query = Scalar{};
6755 blas_int iwork_query = 0;
6756 blas_int const query_workspace = -1;
6757 uni20::lapack::trsen(job, compq, select.data(), order, schur_form.data(), order, schur_vectors.data(), ldq, wr.data(),
6758 wi.data(), selected_dimension, reciprocal_eigenvalue_cluster_condition,
6759 reciprocal_invariant_subspace_condition, &work_query, query_workspace, &iwork_query,
6760 query_workspace);
6761
6762 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
6763 blas_int const liwork = std::max<blas_int>(1, iwork_query);
6764 std::vector<Scalar> work(static_cast<std::size_t>(lwork), Scalar{});
6765 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
6766 uni20::lapack::trsen(job, compq, select.data(), order, schur_form.data(), order, schur_vectors.data(), ldq, wr.data(),
6767 wi.data(), selected_dimension, reciprocal_eigenvalue_cluster_condition,
6768 reciprocal_invariant_subspace_condition, work.data(), lwork, iwork.data(), liwork);
6769
6770 if (selected_dimension < 0 || static_cast<std::size_t>(selected_dimension) != expected_selected_dimension)
6771 {
6772 throw std::runtime_error("LAPACK trsen returned an unexpected selected subspace dimension");
6773 }
6774
6775 decomposition.schur_form = std::move(schur_form);
6776 decomposition.schur_vectors =
6777 update_vectors ? std::move(schur_vectors) : detail::ColumnMajorLapackMatrix<Scalar>(0, 0);
6778 decomposition.eigenvalues.clear();
6779 decomposition.eigenvalues.reserve(n);
6780 for (std::size_t index = 0; index < n; ++index)
6781 {
6782 decomposition.eigenvalues.push_back(uni20::complex<Scalar>{wr[index], wi[index]});
6783 }
6784 decomposition.blocks = detail::real_schur_blocks(decomposition.eigenvalues);
6785
6786 result.decomposition = std::move(decomposition);
6787 result.selected_dimension = static_cast<std::size_t>(selected_dimension);
6788 result.reciprocal_eigenvalue_cluster_condition = reciprocal_eigenvalue_cluster_condition;
6789 result.reciprocal_invariant_subspace_condition = reciprocal_invariant_subspace_condition;
6790 return result;
6791}
6792
6801template <uni20::LapackReal Scalar>
6803{
6804 if (!std::cmp_equal(decomposition.schur_form.rows(), decomposition.schur_form.cols()))
6805 {
6806 throw std::invalid_argument("real_schur_right_eigenvectors requires a square Schur form");
6807 }
6808
6809 std::size_t const n = static_cast<std::size_t>(decomposition.schur_form.rows());
6812 if (n == 0)
6813 {
6814 return result;
6815 }
6816 if (decomposition.eigenvalues.size() != n)
6817 {
6818 throw std::invalid_argument("real_schur_right_eigenvectors received inconsistent Schur eigenvalue data");
6819 }
6820
6821 detail::ColumnMajorLapackMatrix<Scalar> schur_form = std::move(decomposition.schur_form);
6822 detail::ColumnMajorLapackMatrix<Scalar> left_vectors(1, 1);
6823 detail::ColumnMajorLapackMatrix<Scalar> right_vectors(n, n);
6824 std::vector<blas_int> select(n, 1);
6825 std::vector<Scalar> work(3 * n, Scalar{});
6826 blas_int const order = detail::checked_blas_int(n);
6827 blas_int computed_vectors = 0;
6828 char const side = 'R';
6829 char const howmny = 'A';
6830 uni20::lapack::trevc(side, howmny, select.data(), order, schur_form.data(), order, left_vectors.data(), 1,
6831 right_vectors.data(), order, order, computed_vectors, work.data());
6832
6833 if (computed_vectors < 0 || static_cast<std::size_t>(computed_vectors) != n)
6834 {
6835 throw std::runtime_error("LAPACK trevc returned an unexpected number of eigenvectors");
6836 }
6837
6838 for (std::size_t col = 0; col < n;)
6839 {
6840 auto const eigenvalue = decomposition.eigenvalues[col];
6841 if (eigenvalue.imag() == Scalar{})
6842 {
6843 for (std::size_t row = 0; row < n; ++row)
6844 {
6845 result.right_eigenvectors[row, col] = uni20::complex<Scalar>{right_vectors[row, col], Scalar{}};
6846 }
6847 ++col;
6848 continue;
6849 }
6850
6851 if (col + 1 >= n)
6852 {
6853 throw std::runtime_error("LAPACK trevc returned an incomplete complex eigenvector pair");
6854 }
6855 auto const next_eigenvalue = decomposition.eigenvalues[col + 1];
6856 Scalar const scale = std::max(Scalar{1}, std::max(uni20::math::abs(eigenvalue), uni20::math::abs(next_eigenvalue)));
6857 if (uni20::math::abs(next_eigenvalue - std::conj(eigenvalue)) >
6859 {
6860 throw std::runtime_error("LAPACK trevc returned a non-adjacent complex eigenvector pair");
6861 }
6862
6863 for (std::size_t row = 0; row < n; ++row)
6864 {
6865 uni20::complex<Scalar> const positive_imaginary_vector{right_vectors[row, col], right_vectors[row, col + 1]};
6866 if (eigenvalue.imag() > Scalar{})
6867 {
6868 result.right_eigenvectors[row, col] = positive_imaginary_vector;
6869 result.right_eigenvectors[row, col + 1] = std::conj(positive_imaginary_vector);
6870 }
6871 else
6872 {
6873 result.right_eigenvectors[row, col] = std::conj(positive_imaginary_vector);
6874 result.right_eigenvectors[row, col + 1] = positive_imaginary_vector;
6875 }
6876 }
6877 col += 2;
6878 }
6879
6880 result.computed_vectors = static_cast<std::size_t>(computed_vectors);
6881 return result;
6882}
6883
6891template <uni20::LapackReal Scalar>
6893{
6894 if (!std::cmp_equal(decomposition.schur_form.rows(), decomposition.schur_form.cols()))
6895 {
6896 throw std::invalid_argument("real_schur_condition_estimates requires a square Schur form");
6897 }
6898
6899 std::size_t const n = static_cast<std::size_t>(decomposition.schur_form.rows());
6903 if (n == 0)
6904 {
6905 return result;
6906 }
6907 if (decomposition.eigenvalues.size() != n)
6908 {
6909 throw std::invalid_argument("real_schur_condition_estimates received inconsistent Schur eigenvalue data");
6910 }
6911
6912 detail::ColumnMajorLapackMatrix<Scalar> schur_form = std::move(decomposition.schur_form);
6913 detail::ColumnMajorLapackMatrix<Scalar> left_vectors(n, n);
6914 detail::ColumnMajorLapackMatrix<Scalar> right_vectors(n, n);
6915 std::vector<blas_int> select(n, 1);
6916 std::vector<Scalar> trevc_work(3 * n, Scalar{});
6917 blas_int const order = detail::checked_blas_int(n);
6918 blas_int computed_vectors = 0;
6919 uni20::lapack::trevc('B', 'A', select.data(), order, schur_form.data(), order, left_vectors.data(), order,
6920 right_vectors.data(), order, order, computed_vectors, trevc_work.data());
6921
6922 if (computed_vectors < 0 || static_cast<std::size_t>(computed_vectors) != n)
6923 {
6924 throw std::runtime_error("LAPACK trevc returned an unexpected number of Schur eigenvectors");
6925 }
6926
6927 blas_int computed_estimates = 0;
6928 blas_int const ldwork = order;
6929 std::vector<Scalar> work(n * (n + 6), Scalar{});
6930 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order - 2)), 0);
6931 uni20::lapack::trsna('B', 'A', select.data(), order, schur_form.data(), order, left_vectors.data(), order,
6932 right_vectors.data(), order, result.reciprocal_eigenvalue_condition_numbers.data(),
6933 result.reciprocal_eigenvector_condition_numbers.data(), order, computed_estimates, work.data(),
6934 ldwork, iwork.data());
6935
6936 if (computed_estimates < 0 || static_cast<std::size_t>(computed_estimates) != n)
6937 {
6938 throw std::runtime_error("LAPACK trsna returned an unexpected number of condition estimates");
6939 }
6940
6941 result.computed_estimates = static_cast<std::size_t>(computed_estimates);
6942 return result;
6943}
6944
6955
6956template <uni20::LapackReal Real>
6957RealNonsymmetricBalance<Real>
6960{
6961 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
6962 {
6963 throw std::invalid_argument("real_nonsymmetric_balance requires a square matrix");
6964 }
6965
6966 std::size_t const n = static_cast<std::size_t>(matrix.rows());
6968 result.balanced_matrix = std::move(matrix);
6969 result.scale.resize(n);
6970 result.balanced_last_exclusive = n;
6971 if (n == 0)
6972 {
6973 return result;
6974 }
6975
6976 blas_int const order = detail::checked_blas_int(n);
6977 blas_int first = 0;
6978 blas_int last = 0;
6979 uni20::lapack::gebal(detail::lapack_balance_job(job), order, result.balanced_matrix.data(), order, first, last,
6980 result.scale.data());
6981
6982 if (first <= 0 || last < first || last > order)
6983 {
6984 throw std::runtime_error("LAPACK gebal returned an invalid balanced block interval");
6985 }
6986 result.balanced_first = static_cast<std::size_t>(first - 1);
6987 result.balanced_last_exclusive = static_cast<std::size_t>(last);
6988 return result;
6989}
6990
7000template <uni20::LapackReal Real>
7001detail::ColumnMajorLapackMatrix<Real>
7003 RealNonsymmetricBalance<Real> const& balance,
7005{
7006 std::size_t const n = balance.scale.size();
7007 if (!std::cmp_equal(vectors.rows(), n))
7008 {
7009 throw std::invalid_argument("real_nonsymmetric_balance_backtransform_right_vectors received incompatible vectors");
7010 }
7011 if (balance.balanced_first > balance.balanced_last_exclusive || balance.balanced_last_exclusive > n)
7012 {
7013 throw std::invalid_argument("real_nonsymmetric_balance_backtransform_right_vectors received invalid balance data");
7014 }
7015 if (n == 0 || vectors.cols() == 0)
7016 {
7017 return vectors;
7018 }
7019
7020 blas_int const order = detail::checked_blas_int(n);
7021 blas_int const first = detail::checked_blas_int(balance.balanced_first + 1);
7022 blas_int const last = detail::checked_blas_int(balance.balanced_last_exclusive);
7023 blas_int const vector_count = detail::checked_blas_int(vectors.cols());
7024 std::vector<Real> scale = balance.scale;
7025 uni20::lapack::gebak(detail::lapack_balance_job(job), 'R', order, first, last, scale.data(), vector_count,
7026 vectors.data(), order);
7027 return vectors;
7028}
7029
7041template <uni20::LapackReal Real>
7042RealGeneralizedNonsymmetricBalance<Real>
7046{
7047 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
7048 {
7049 throw std::invalid_argument("real_generalized_nonsymmetric_balance requires a square matrix");
7050 }
7051 if (!std::cmp_equal(metric.rows(), metric.cols()))
7052 {
7053 throw std::invalid_argument("real_generalized_nonsymmetric_balance requires a square metric matrix");
7054 }
7055 if (!std::cmp_equal(matrix.rows(), metric.rows()))
7056 {
7057 throw std::invalid_argument("real_generalized_nonsymmetric_balance received incompatible matrix sizes");
7058 }
7059
7060 std::size_t const n = static_cast<std::size_t>(matrix.rows());
7062 result.balanced_matrix = std::move(matrix);
7063 result.balanced_metric = std::move(metric);
7064 result.left_scale.resize(n);
7065 result.right_scale.resize(n);
7066 result.balanced_last_exclusive = n;
7067 if (n == 0)
7068 {
7069 return result;
7070 }
7071
7072 blas_int const order = detail::checked_blas_int(n);
7073 blas_int first = 0;
7074 blas_int last = 0;
7075 std::vector<Real> work(6 * n, Real{});
7076 uni20::lapack::ggbal(detail::lapack_balance_job(job), order, result.balanced_matrix.data(), order,
7077 result.balanced_metric.data(), order, first, last, result.left_scale.data(),
7078 result.right_scale.data(), work.data());
7079
7080 if (first <= 0 || last < first || last > order)
7081 {
7082 throw std::runtime_error("LAPACK ggbal returned an invalid balanced block interval");
7083 }
7084 result.balanced_first = static_cast<std::size_t>(first - 1);
7085 result.balanced_last_exclusive = static_cast<std::size_t>(last);
7086 return result;
7087}
7088
7098template <uni20::LapackReal Real>
7102{
7103 std::size_t const n = balance.left_scale.size();
7104 if (balance.right_scale.size() != n)
7105 {
7106 throw std::invalid_argument(
7107 "real_generalized_nonsymmetric_balance_backtransform_right_vectors received inconsistent balance data");
7108 }
7109 if (!std::cmp_equal(vectors.rows(), n))
7110 {
7111 throw std::invalid_argument(
7112 "real_generalized_nonsymmetric_balance_backtransform_right_vectors received incompatible vectors");
7113 }
7114 if (balance.balanced_first > balance.balanced_last_exclusive || balance.balanced_last_exclusive > n)
7115 {
7116 throw std::invalid_argument(
7117 "real_generalized_nonsymmetric_balance_backtransform_right_vectors received invalid balance data");
7118 }
7119 if (n == 0 || vectors.cols() == 0)
7120 {
7121 return vectors;
7122 }
7123
7124 blas_int const order = detail::checked_blas_int(n);
7125 blas_int const first = detail::checked_blas_int(balance.balanced_first + 1);
7126 blas_int const last = detail::checked_blas_int(balance.balanced_last_exclusive);
7127 blas_int const vector_count = detail::checked_blas_int(vectors.cols());
7128 std::vector<Real> left_scale = balance.left_scale;
7129 std::vector<Real> right_scale = balance.right_scale;
7130 uni20::lapack::ggbak(detail::lapack_balance_job(job), 'R', order, first, last, left_scale.data(), right_scale.data(),
7131 vector_count, vectors.data(), order);
7132 return vectors;
7133}
7134
7146template <uni20::LapackReal Real>
7147RealGeneralizedHessenbergReduction<Real>
7149 detail::ColumnMajorLapackMatrix<Real> upper_triangular_metric,
7150 bool compute_vectors)
7151{
7152 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
7153 {
7154 throw std::invalid_argument("real_generalized_hessenberg_reduction requires a square matrix");
7155 }
7156 if (!std::cmp_equal(upper_triangular_metric.rows(), upper_triangular_metric.cols()))
7157 {
7158 throw std::invalid_argument("real_generalized_hessenberg_reduction requires a square metric matrix");
7159 }
7160 if (!std::cmp_equal(matrix.rows(), upper_triangular_metric.rows()))
7161 {
7162 throw std::invalid_argument("real_generalized_hessenberg_reduction received incompatible matrix sizes");
7163 }
7164
7165 std::size_t const n = static_cast<std::size_t>(matrix.rows());
7166 for (std::size_t col = 0; col < n; ++col)
7167 {
7168 for (std::size_t row = col + 1; row < n; ++row)
7169 {
7170 if (upper_triangular_metric[row, col] != Real{})
7171 {
7172 throw std::invalid_argument("real_generalized_hessenberg_reduction requires an upper-triangular metric");
7173 }
7174 }
7175 }
7176
7178 result.first = 0;
7179 result.last_exclusive = n;
7180 if (n == 0)
7181 {
7186 return result;
7187 }
7188
7189 detail::ColumnMajorLapackMatrix<Real> left_vectors(compute_vectors ? n : 1, compute_vectors ? n : 1);
7190 detail::ColumnMajorLapackMatrix<Real> right_vectors(compute_vectors ? n : 1, compute_vectors ? n : 1);
7191 blas_int const order = detail::checked_blas_int(n);
7192 blas_int const first = 1;
7193 blas_int const last = order;
7194 blas_int const ldq = compute_vectors ? order : 1;
7195 blas_int const ldz = compute_vectors ? order : 1;
7196 char const compq = compute_vectors ? 'I' : 'N';
7197 char const compz = compute_vectors ? 'I' : 'N';
7198
7199 uni20::lapack::gghrd(compq, compz, order, first, last, matrix.data(), order, upper_triangular_metric.data(), order,
7200 left_vectors.data(), ldq, right_vectors.data(), ldz);
7201
7202 result.matrix_hessenberg_form = std::move(matrix);
7203 result.metric_triangular_form = std::move(upper_triangular_metric);
7205 compute_vectors ? std::move(left_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7207 compute_vectors ? std::move(right_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7208 return result;
7209}
7210
7222template <uni20::LapackReal Real>
7223RealGeneralizedSchurDecomposition<Real>
7225 detail::ColumnMajorLapackMatrix<Real> upper_triangular_metric, bool compute_vectors)
7226{
7227 if (!std::cmp_equal(hessenberg.rows(), hessenberg.cols()))
7228 {
7229 throw std::invalid_argument("real_generalized_hessenberg_schur requires a square Hessenberg matrix");
7230 }
7231 if (!std::cmp_equal(upper_triangular_metric.rows(), upper_triangular_metric.cols()))
7232 {
7233 throw std::invalid_argument("real_generalized_hessenberg_schur requires a square metric matrix");
7234 }
7235 if (!std::cmp_equal(hessenberg.rows(), upper_triangular_metric.rows()))
7236 {
7237 throw std::invalid_argument("real_generalized_hessenberg_schur received incompatible matrix sizes");
7238 }
7239
7240 std::size_t const n = static_cast<std::size_t>(hessenberg.rows());
7241 for (std::size_t col = 0; col < n; ++col)
7242 {
7243 for (std::size_t row = col + 2; row < n; ++row)
7244 {
7245 if (hessenberg[row, col] != Real{})
7246 {
7247 throw std::invalid_argument("real_generalized_hessenberg_schur requires an upper-Hessenberg matrix");
7248 }
7249 }
7250 for (std::size_t row = col + 1; row < n; ++row)
7251 {
7252 if (upper_triangular_metric[row, col] != Real{})
7253 {
7254 throw std::invalid_argument("real_generalized_hessenberg_schur requires an upper-triangular metric");
7255 }
7256 }
7257 }
7258
7260 result.alpha.resize(n);
7261 result.beta.resize(n);
7262 result.eigenvalues.resize(n);
7263 if (n == 0)
7264 {
7269 return result;
7270 }
7271
7272 blas_int const order = detail::checked_blas_int(n);
7273 std::vector<Real> alphar(n, Real{});
7274 std::vector<Real> alphai(n, Real{});
7275 std::vector<Real> beta(n, Real{});
7276 detail::ColumnMajorLapackMatrix<Real> left_vectors(compute_vectors ? n : 1, compute_vectors ? n : 1);
7277 detail::ColumnMajorLapackMatrix<Real> right_vectors(compute_vectors ? n : 1, compute_vectors ? n : 1);
7278 blas_int const ldq = compute_vectors ? order : 1;
7279 blas_int const ldz = compute_vectors ? order : 1;
7280 blas_int const first = 1;
7281 blas_int const last = order;
7282 char const job = 'S';
7283 char const compq = compute_vectors ? 'I' : 'N';
7284 char const compz = compute_vectors ? 'I' : 'N';
7285
7286 Real work_query{};
7287 blas_int const query_lwork = -1;
7288 uni20::lapack::hgeqz(job, compq, compz, order, first, last, hessenberg.data(), order, upper_triangular_metric.data(),
7289 order, alphar.data(), alphai.data(), beta.data(), left_vectors.data(), ldq, right_vectors.data(),
7290 ldz, &work_query, query_lwork);
7291
7292 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
7293 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
7294 uni20::lapack::hgeqz(job, compq, compz, order, first, last, hessenberg.data(), order, upper_triangular_metric.data(),
7295 order, alphar.data(), alphai.data(), beta.data(), left_vectors.data(), ldq, right_vectors.data(),
7296 ldz, work.data(), lwork);
7297
7298 result.matrix_schur_form = std::move(hessenberg);
7299 result.metric_schur_form = std::move(upper_triangular_metric);
7300 result.left_schur_vectors = compute_vectors ? std::move(left_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7301 result.right_schur_vectors = compute_vectors ? std::move(right_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7302 for (std::size_t i = 0; i < n; ++i)
7303 {
7304 result.alpha[i] = uni20::complex<Real>{alphar[i], alphai[i]};
7305 result.beta[i] = beta[i];
7306 result.eigenvalues[i] = detail::generalized_eigenvalue(alphar[i], alphai[i], beta[i]);
7307 }
7308 result.blocks = detail::real_schur_blocks(result.eigenvalues);
7309 return result;
7310}
7311
7323template <uni20::LapackReal Real>
7326 bool compute_vectors)
7327{
7328 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
7329 {
7330 throw std::invalid_argument("real_generalized_schur requires a square matrix");
7331 }
7332 if (!std::cmp_equal(metric.rows(), metric.cols()))
7333 {
7334 throw std::invalid_argument("real_generalized_schur requires a square metric matrix");
7335 }
7336 if (!std::cmp_equal(matrix.rows(), metric.rows()))
7337 {
7338 throw std::invalid_argument("real_generalized_schur received incompatible matrix sizes");
7339 }
7340
7341 std::size_t const n = static_cast<std::size_t>(matrix.rows());
7343 result.alpha.resize(n);
7344 result.beta.resize(n);
7345 result.eigenvalues.resize(n);
7346 if (n == 0)
7347 {
7352 return result;
7353 }
7354
7355 blas_int const order = detail::checked_blas_int(n);
7356 std::vector<Real> alphar(n, Real{});
7357 std::vector<Real> alphai(n, Real{});
7358 std::vector<Real> beta(n, Real{});
7359 detail::ColumnMajorLapackMatrix<Real> left_vectors(compute_vectors ? n : 1, compute_vectors ? n : 1);
7360 detail::ColumnMajorLapackMatrix<Real> right_vectors(compute_vectors ? n : 1, compute_vectors ? n : 1);
7361 blas_int const ldvsl = compute_vectors ? order : 1;
7362 blas_int const ldvsr = compute_vectors ? order : 1;
7363 char const jobvsl = compute_vectors ? 'V' : 'N';
7364 char const jobvsr = compute_vectors ? 'V' : 'N';
7365 char const sort = 'N';
7366 blas_int selected_dimension = 0;
7367 std::vector<blas_int> bwork(std::max<std::size_t>(1, n), 0);
7368
7369 Real work_query = Real{};
7370 blas_int const query_lwork = -1;
7371 uni20::lapack::gges(jobvsl, jobvsr, sort, order, matrix.data(), order, metric.data(), order, selected_dimension,
7372 alphar.data(), alphai.data(), beta.data(), left_vectors.data(), ldvsl, right_vectors.data(),
7373 ldvsr, &work_query, query_lwork, bwork.data());
7374
7375 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
7376 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
7377 uni20::lapack::gges(jobvsl, jobvsr, sort, order, matrix.data(), order, metric.data(), order, selected_dimension,
7378 alphar.data(), alphai.data(), beta.data(), left_vectors.data(), ldvsl, right_vectors.data(),
7379 ldvsr, work.data(), lwork, bwork.data());
7380
7381 result.matrix_schur_form = std::move(matrix);
7382 result.metric_schur_form = std::move(metric);
7383 result.left_schur_vectors = compute_vectors ? std::move(left_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7384 result.right_schur_vectors = compute_vectors ? std::move(right_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7385 result.selected_dimension = static_cast<std::size_t>(selected_dimension);
7386 for (std::size_t i = 0; i < n; ++i)
7387 {
7388 result.alpha[i] = uni20::complex<Real>{alphar[i], alphai[i]};
7389 result.beta[i] = beta[i];
7390 result.eigenvalues[i] = detail::generalized_eigenvalue(alphar[i], alphai[i], beta[i]);
7391 }
7392 result.blocks = detail::real_schur_blocks(result.eigenvalues);
7393
7394 return result;
7395}
7396
7406template <uni20::LapackReal Real>
7407RealGeneralizedSchurDecomposition<Real>
7409 std::vector<std::size_t> const& leading_block_order)
7410{
7411 if (!std::cmp_equal(decomposition.matrix_schur_form.rows(), decomposition.matrix_schur_form.cols()))
7412 {
7413 throw std::invalid_argument("reorder_real_generalized_schur requires a square matrix Schur form");
7414 }
7415 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), decomposition.metric_schur_form.cols()))
7416 {
7417 throw std::invalid_argument("reorder_real_generalized_schur requires a square metric Schur form");
7418 }
7419
7420 std::size_t const n = static_cast<std::size_t>(decomposition.matrix_schur_form.rows());
7421 if (n == 0 || leading_block_order.empty())
7422 {
7423 return decomposition;
7424 }
7425 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), n) ||
7426 !std::cmp_equal(decomposition.matrix_schur_form.cols(), n) ||
7427 !std::cmp_equal(decomposition.metric_schur_form.cols(), n))
7428 {
7429 throw std::invalid_argument("reorder_real_generalized_schur received inconsistent Schur form sizes");
7430 }
7431 if (decomposition.alpha.size() != n || decomposition.beta.size() != n || decomposition.eigenvalues.size() != n)
7432 {
7433 throw std::invalid_argument("reorder_real_generalized_schur received inconsistent eigenvalue data");
7434 }
7435 bool const has_left_vectors =
7436 decomposition.left_schur_vectors.rows() != 0 || decomposition.left_schur_vectors.cols() != 0;
7437 bool const has_right_vectors =
7438 decomposition.right_schur_vectors.rows() != 0 || decomposition.right_schur_vectors.cols() != 0;
7439 if (has_left_vectors != has_right_vectors)
7440 {
7441 throw std::invalid_argument("reorder_real_generalized_schur requires both Schur vector sets or neither");
7442 }
7443 bool const update_vectors = has_left_vectors;
7444 if (update_vectors && (!std::cmp_equal(decomposition.left_schur_vectors.rows(), n) ||
7445 !std::cmp_equal(decomposition.left_schur_vectors.cols(), n) ||
7446 !std::cmp_equal(decomposition.right_schur_vectors.rows(), n) ||
7447 !std::cmp_equal(decomposition.right_schur_vectors.cols(), n)))
7448 {
7449 throw std::invalid_argument("reorder_real_generalized_schur received inconsistent Schur vectors");
7450 }
7451
7452 std::vector<std::size_t> target_order =
7453 detail::complete_leading_block_order(decomposition.blocks.size(), leading_block_order);
7454 std::vector<std::size_t> current_order(decomposition.blocks.size());
7455 std::iota(current_order.begin(), current_order.end(), std::size_t{0});
7456 std::vector<RealSchurBlock<Real>> current_blocks = decomposition.blocks;
7457
7458 blas_int const order = detail::checked_blas_int(n);
7459 blas_int const ldq = update_vectors ? order : 1;
7460 blas_int const ldz = update_vectors ? order : 1;
7462 update_vectors ? std::move(decomposition.left_schur_vectors) : detail::ColumnMajorLapackMatrix<Real>(1, 1);
7464 update_vectors ? std::move(decomposition.right_schur_vectors) : detail::ColumnMajorLapackMatrix<Real>(1, 1);
7465 blas_int const lwork = std::max<blas_int>(1, 4 * order + 16);
7466 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
7467
7468 for (std::size_t target_position = 0; target_position < leading_block_order.size(); ++target_position)
7469 {
7470 std::size_t const desired_original_index = target_order[target_position];
7471 auto const current_it = std::ranges::find(current_order, desired_original_index);
7472 if (current_it == current_order.end())
7473 {
7474 throw std::logic_error("generalized real Schur block reorder lost a block index");
7475 }
7476 std::size_t const current_position = static_cast<std::size_t>(current_it - current_order.begin());
7477 if (current_position == target_position)
7478 {
7479 continue;
7480 }
7481
7482 blas_int first = detail::checked_blas_int(current_blocks[current_position].begin + 1);
7483 blas_int last = detail::checked_blas_int(current_blocks[target_position].begin + 1);
7484 uni20::lapack::tgexc(update_vectors, update_vectors, order, decomposition.matrix_schur_form.data(), order,
7485 decomposition.metric_schur_form.data(), order, left_vectors.data(), ldq, right_vectors.data(),
7486 ldz, first, last, work.data(), lwork);
7487
7488 std::size_t const moved_index = current_order[current_position];
7489 current_order.erase(current_order.begin() + static_cast<std::ptrdiff_t>(current_position));
7490 current_order.insert(current_order.begin() + static_cast<std::ptrdiff_t>(target_position), moved_index);
7491 current_blocks = detail::ordered_schur_blocks(decomposition.blocks, current_order);
7492 }
7493
7494 decomposition.left_schur_vectors =
7495 update_vectors ? std::move(left_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7496 decomposition.right_schur_vectors =
7497 update_vectors ? std::move(right_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7498 decomposition.alpha = detail::ordered_schur_block_values(decomposition.alpha, decomposition.blocks, current_order);
7499 decomposition.beta = detail::ordered_schur_block_values(decomposition.beta, decomposition.blocks, current_order);
7500 decomposition.blocks = detail::ordered_schur_blocks(decomposition.blocks, current_order);
7501 decomposition.eigenvalues = detail::eigenvalues_from_schur_blocks(decomposition.blocks);
7502 return decomposition;
7503}
7504
7514template <uni20::LapackReal Real>
7515RealGeneralizedSchurSelectedSubspace<Real>
7517 std::vector<std::size_t> const& selected_blocks)
7518{
7519 if (!std::cmp_equal(decomposition.matrix_schur_form.rows(), decomposition.matrix_schur_form.cols()))
7520 {
7521 throw std::invalid_argument("real_generalized_schur_selected_subspace requires a square matrix Schur form");
7522 }
7523 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), decomposition.metric_schur_form.cols()))
7524 {
7525 throw std::invalid_argument("real_generalized_schur_selected_subspace requires a square metric Schur form");
7526 }
7527
7528 std::size_t const n = static_cast<std::size_t>(decomposition.matrix_schur_form.rows());
7530 if (n == 0 || selected_blocks.empty())
7531 {
7532 result.decomposition = std::move(decomposition);
7533 return result;
7534 }
7535 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), n) ||
7536 !std::cmp_equal(decomposition.matrix_schur_form.cols(), n) ||
7537 !std::cmp_equal(decomposition.metric_schur_form.cols(), n))
7538 {
7539 throw std::invalid_argument("real_generalized_schur_selected_subspace received inconsistent Schur form sizes");
7540 }
7541 if (decomposition.alpha.size() != n || decomposition.beta.size() != n || decomposition.eigenvalues.size() != n)
7542 {
7543 throw std::invalid_argument("real_generalized_schur_selected_subspace received inconsistent eigenvalue data");
7544 }
7545 bool const has_left_vectors =
7546 decomposition.left_schur_vectors.rows() != 0 || decomposition.left_schur_vectors.cols() != 0;
7547 bool const has_right_vectors =
7548 decomposition.right_schur_vectors.rows() != 0 || decomposition.right_schur_vectors.cols() != 0;
7549 if (has_left_vectors != has_right_vectors)
7550 {
7551 throw std::invalid_argument("real_generalized_schur_selected_subspace requires both Schur vector sets or neither");
7552 }
7553 bool const update_vectors = has_left_vectors;
7554 if (update_vectors && (!std::cmp_equal(decomposition.left_schur_vectors.rows(), n) ||
7555 !std::cmp_equal(decomposition.left_schur_vectors.cols(), n) ||
7556 !std::cmp_equal(decomposition.right_schur_vectors.rows(), n) ||
7557 !std::cmp_equal(decomposition.right_schur_vectors.cols(), n)))
7558 {
7559 throw std::invalid_argument("real_generalized_schur_selected_subspace received inconsistent Schur vectors");
7560 }
7561 if (decomposition.blocks.empty())
7562 {
7563 decomposition.blocks = detail::real_schur_blocks(decomposition.eigenvalues);
7564 }
7565
7566 std::vector<bool> seen_blocks(decomposition.blocks.size(), false);
7567 std::vector<blas_int> select(n, 0);
7568 std::size_t expected_selected_dimension = 0;
7569 for (std::size_t const block_index : selected_blocks)
7570 {
7571 if (block_index >= decomposition.blocks.size())
7572 {
7573 throw std::invalid_argument("real_generalized_schur_selected_subspace received an out-of-range selected block");
7574 }
7575 if (seen_blocks[block_index])
7576 {
7577 throw std::invalid_argument("real_generalized_schur_selected_subspace received a duplicate selected block");
7578 }
7579 seen_blocks[block_index] = true;
7580 auto const& block = decomposition.blocks[block_index];
7581 if (block.begin + block.size > n)
7582 {
7583 throw std::invalid_argument("real_generalized_schur_selected_subspace received inconsistent block metadata");
7584 }
7585 for (std::size_t offset = 0; offset < block.size; ++offset)
7586 {
7587 select[block.begin + offset] = 1;
7588 }
7589 expected_selected_dimension += block.size;
7590 }
7591
7592 detail::ColumnMajorLapackMatrix<Real> matrix_schur_form = std::move(decomposition.matrix_schur_form);
7593 detail::ColumnMajorLapackMatrix<Real> metric_schur_form = std::move(decomposition.metric_schur_form);
7595 update_vectors ? std::move(decomposition.left_schur_vectors) : detail::ColumnMajorLapackMatrix<Real>(1, 1);
7597 update_vectors ? std::move(decomposition.right_schur_vectors) : detail::ColumnMajorLapackMatrix<Real>(1, 1);
7598 std::vector<Real> alphar(n, Real{});
7599 std::vector<Real> alphai(n, Real{});
7600 std::vector<Real> beta(n, Real{});
7601 std::array<Real, 2> dif{Real{}, Real{}};
7602 blas_int const order = detail::checked_blas_int(n);
7603 blas_int const ldq = update_vectors ? order : 1;
7604 blas_int const ldz = update_vectors ? order : 1;
7605 blas_int const ijob = 4;
7606 blas_int selected_dimension = 0;
7607 Real pl{};
7608 Real pr{};
7609
7610 Real work_query = Real{};
7611 blas_int iwork_query = 0;
7612 blas_int const query_workspace = -1;
7613 uni20::lapack::tgsen(ijob, update_vectors, update_vectors, select.data(), order, matrix_schur_form.data(), order,
7614 metric_schur_form.data(), order, alphar.data(), alphai.data(), beta.data(), left_vectors.data(),
7615 ldq, right_vectors.data(), ldz, selected_dimension, pl, pr, dif.data(), &work_query,
7616 query_workspace, &iwork_query, query_workspace);
7617
7618 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
7619 blas_int const liwork = std::max<blas_int>(1, iwork_query);
7620 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
7621 std::vector<blas_int> iwork(static_cast<std::size_t>(liwork), 0);
7622 uni20::lapack::tgsen(ijob, update_vectors, update_vectors, select.data(), order, matrix_schur_form.data(), order,
7623 metric_schur_form.data(), order, alphar.data(), alphai.data(), beta.data(), left_vectors.data(),
7624 ldq, right_vectors.data(), ldz, selected_dimension, pl, pr, dif.data(), work.data(), lwork,
7625 iwork.data(), liwork);
7626
7627 if (selected_dimension < 0 || static_cast<std::size_t>(selected_dimension) != expected_selected_dimension)
7628 {
7629 throw std::runtime_error("LAPACK tgsen returned an unexpected selected subspace dimension");
7630 }
7631
7632 decomposition.matrix_schur_form = std::move(matrix_schur_form);
7633 decomposition.metric_schur_form = std::move(metric_schur_form);
7634 decomposition.left_schur_vectors =
7635 update_vectors ? std::move(left_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7636 decomposition.right_schur_vectors =
7637 update_vectors ? std::move(right_vectors) : detail::ColumnMajorLapackMatrix<Real>(0, 0);
7638 for (std::size_t i = 0; i < n; ++i)
7639 {
7640 decomposition.alpha[i] = uni20::complex<Real>{alphar[i], alphai[i]};
7641 decomposition.beta[i] = beta[i];
7642 decomposition.eigenvalues[i] = detail::generalized_eigenvalue(alphar[i], alphai[i], beta[i]);
7643 }
7644 decomposition.blocks = detail::real_schur_blocks(decomposition.eigenvalues);
7645
7646 result.decomposition = std::move(decomposition);
7647 result.selected_dimension = static_cast<std::size_t>(selected_dimension);
7648 result.left_projection_lower_bound = pl;
7649 result.right_projection_lower_bound = pr;
7652 return result;
7653}
7654
7663template <uni20::LapackReal Real>
7664RealGeneralizedSchurRightEigenvectors<Real>
7666{
7667 if (!std::cmp_equal(decomposition.matrix_schur_form.rows(), decomposition.matrix_schur_form.cols()))
7668 {
7669 throw std::invalid_argument("real_generalized_schur_right_eigenvectors requires a square matrix Schur form");
7670 }
7671 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), decomposition.metric_schur_form.cols()))
7672 {
7673 throw std::invalid_argument("real_generalized_schur_right_eigenvectors requires a square metric Schur form");
7674 }
7675
7676 std::size_t const n = static_cast<std::size_t>(decomposition.matrix_schur_form.rows());
7679 if (n == 0)
7680 {
7681 return result;
7682 }
7683 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), n) ||
7684 !std::cmp_equal(decomposition.matrix_schur_form.cols(), n) ||
7685 !std::cmp_equal(decomposition.metric_schur_form.cols(), n))
7686 {
7687 throw std::invalid_argument("real_generalized_schur_right_eigenvectors received inconsistent Schur form sizes");
7688 }
7689 if (decomposition.eigenvalues.size() != n)
7690 {
7691 throw std::invalid_argument("real_generalized_schur_right_eigenvectors received inconsistent eigenvalue data");
7692 }
7693
7694 detail::ColumnMajorLapackMatrix<Real> matrix_schur_form = std::move(decomposition.matrix_schur_form);
7695 detail::ColumnMajorLapackMatrix<Real> metric_schur_form = std::move(decomposition.metric_schur_form);
7696 detail::ColumnMajorLapackMatrix<Real> left_vectors(1, 1);
7697 detail::ColumnMajorLapackMatrix<Real> right_vectors(n, n);
7698 std::vector<blas_int> select(n, 1);
7699 std::vector<Real> work(6 * n, Real{});
7700 blas_int const order = detail::checked_blas_int(n);
7701 blas_int computed_vectors = 0;
7702 char const side = 'R';
7703 char const howmny = 'A';
7704 uni20::lapack::tgevc(side, howmny, select.data(), order, matrix_schur_form.data(), order, metric_schur_form.data(),
7705 order, left_vectors.data(), 1, right_vectors.data(), order, order, computed_vectors,
7706 work.data());
7707
7708 if (computed_vectors < 0 || static_cast<std::size_t>(computed_vectors) != n)
7709 {
7710 throw std::runtime_error("LAPACK tgevc returned an unexpected number of generalized Schur eigenvectors");
7711 }
7712
7713 for (std::size_t col = 0; col < n;)
7714 {
7715 auto const eigenvalue = decomposition.eigenvalues[col];
7716 if (eigenvalue.imag() == Real{})
7717 {
7718 for (std::size_t row = 0; row < n; ++row)
7719 {
7720 result.right_eigenvectors[row, col] = uni20::complex<Real>{right_vectors[row, col], Real{}};
7721 }
7722 ++col;
7723 continue;
7724 }
7725
7726 if (col + 1 >= n)
7727 {
7728 throw std::runtime_error("LAPACK tgevc returned an incomplete complex eigenvector pair");
7729 }
7730 auto const next_eigenvalue = decomposition.eigenvalues[col + 1];
7731 Real const scale = std::max(Real{1}, std::max(uni20::math::abs(eigenvalue), uni20::math::abs(next_eigenvalue)));
7732 if (uni20::math::abs(next_eigenvalue - std::conj(eigenvalue)) >
7734 {
7735 throw std::runtime_error("LAPACK tgevc returned a non-adjacent complex eigenvector pair");
7736 }
7737
7738 for (std::size_t row = 0; row < n; ++row)
7739 {
7740 uni20::complex<Real> const positive_imaginary_vector{right_vectors[row, col], right_vectors[row, col + 1]};
7741 if (eigenvalue.imag() > Real{})
7742 {
7743 result.right_eigenvectors[row, col] = positive_imaginary_vector;
7744 result.right_eigenvectors[row, col + 1] = std::conj(positive_imaginary_vector);
7745 }
7746 else
7747 {
7748 result.right_eigenvectors[row, col] = std::conj(positive_imaginary_vector);
7749 result.right_eigenvectors[row, col + 1] = positive_imaginary_vector;
7750 }
7751 }
7752 col += 2;
7753 }
7754
7755 result.computed_vectors = static_cast<std::size_t>(computed_vectors);
7756 return result;
7757}
7758
7766template <uni20::LapackReal Real>
7767RealGeneralizedSchurConditionEstimates<Real>
7769{
7770 if (!std::cmp_equal(decomposition.matrix_schur_form.rows(), decomposition.matrix_schur_form.cols()))
7771 {
7772 throw std::invalid_argument("real_generalized_schur_condition_estimates requires a square matrix Schur form");
7773 }
7774 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), decomposition.metric_schur_form.cols()))
7775 {
7776 throw std::invalid_argument("real_generalized_schur_condition_estimates requires a square metric Schur form");
7777 }
7778
7779 std::size_t const n = static_cast<std::size_t>(decomposition.matrix_schur_form.rows());
7783 if (n == 0)
7784 {
7785 return result;
7786 }
7787 if (!std::cmp_equal(decomposition.metric_schur_form.rows(), n) ||
7788 !std::cmp_equal(decomposition.matrix_schur_form.cols(), n) ||
7789 !std::cmp_equal(decomposition.metric_schur_form.cols(), n))
7790 {
7791 throw std::invalid_argument("real_generalized_schur_condition_estimates received inconsistent Schur form sizes");
7792 }
7793 if (decomposition.eigenvalues.size() != n)
7794 {
7795 throw std::invalid_argument("real_generalized_schur_condition_estimates received inconsistent eigenvalue data");
7796 }
7797
7798 detail::ColumnMajorLapackMatrix<Real> matrix_schur_form = std::move(decomposition.matrix_schur_form);
7799 detail::ColumnMajorLapackMatrix<Real> metric_schur_form = std::move(decomposition.metric_schur_form);
7800 detail::ColumnMajorLapackMatrix<Real> left_vectors(n, n);
7801 detail::ColumnMajorLapackMatrix<Real> right_vectors(n, n);
7802 std::vector<blas_int> select(n, 1);
7803 std::vector<Real> tgevc_work(6 * n, Real{});
7804 blas_int const order = detail::checked_blas_int(n);
7805 blas_int computed_vectors = 0;
7806 uni20::lapack::tgevc('B', 'A', select.data(), order, matrix_schur_form.data(), order, metric_schur_form.data(), order,
7807 left_vectors.data(), order, right_vectors.data(), order, order, computed_vectors,
7808 tgevc_work.data());
7809
7810 if (computed_vectors < 0 || static_cast<std::size_t>(computed_vectors) != n)
7811 {
7812 throw std::runtime_error("LAPACK tgevc returned an unexpected number of generalized Schur eigenvectors");
7813 }
7814
7815 blas_int computed_estimates = 0;
7816 blas_int const lwork = std::max<blas_int>(1, 2 * order * (order + 2) + 16);
7817 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
7818 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order + 6)), 0);
7819 uni20::lapack::tgsna('B', 'A', select.data(), order, matrix_schur_form.data(), order, metric_schur_form.data(), order,
7820 left_vectors.data(), order, right_vectors.data(), order,
7822 result.reciprocal_eigenvector_condition_numbers.data(), order, computed_estimates, work.data(),
7823 lwork, iwork.data());
7824
7825 if (computed_estimates < 0 || static_cast<std::size_t>(computed_estimates) != n)
7826 {
7827 throw std::runtime_error("LAPACK tgsna returned an unexpected number of condition estimates");
7828 }
7829
7830 result.computed_estimates = static_cast<std::size_t>(computed_estimates);
7831 return result;
7832}
7833
7845
7846template <uni20::LapackReal Real>
7847RealNonsymmetricExpertEigensystem<Real>
7849{
7850 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
7851 {
7852 throw std::invalid_argument("real_nonsymmetric_expert_eigensystem requires a square matrix");
7853 }
7854
7855 std::size_t const n = static_cast<std::size_t>(matrix.rows());
7857 result.eigenvalues.resize(n);
7858 result.right_eigenvectors = detail::ColumnMajorLapackMatrix<uni20::complex<Real>>(compute_right_vectors ? n : 0,
7859 compute_right_vectors ? n : 0);
7862 result.balance_scale.resize(n);
7863 if (n == 0)
7864 {
7865 return result;
7866 }
7867
7868 blas_int const order = detail::checked_blas_int(n);
7869 std::vector<Real> wr(n, Real{});
7870 std::vector<Real> wi(n, Real{});
7873 blas_int const ldvl = order;
7874 blas_int const ldvr = order;
7875 char const balanc = 'B';
7876 char const jobvl = 'V';
7877 char const jobvr = 'V';
7878 char const sense = 'B';
7879 blas_int ilo = 0;
7880 blas_int ihi = 0;
7881 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, 2 * order - 2)), 0);
7882
7883 Real work_query = Real{};
7884 blas_int const query_lwork = -1;
7885 uni20::lapack::geevx(balanc, jobvl, jobvr, sense, order, matrix.data(), order, wr.data(), wi.data(), vl.data(), ldvl,
7886 vr.data(), ldvr, ilo, ihi, result.balance_scale.data(), result.balanced_matrix_norm,
7888 result.reciprocal_eigenvector_condition_numbers.data(), &work_query, query_lwork, iwork.data());
7889
7890 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
7891 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
7892 uni20::lapack::geevx(balanc, jobvl, jobvr, sense, order, matrix.data(), order, wr.data(), wi.data(), vl.data(), ldvl,
7893 vr.data(), ldvr, ilo, ihi, result.balance_scale.data(), result.balanced_matrix_norm,
7895 result.reciprocal_eigenvector_condition_numbers.data(), work.data(), lwork, iwork.data());
7896
7897 if (ilo <= 0 || ihi < ilo || ihi > order)
7898 {
7899 throw std::runtime_error("LAPACK geevx returned an invalid balanced block interval");
7900 }
7901 result.balanced_first = static_cast<std::size_t>(ilo - 1);
7902 result.balanced_last_exclusive = static_cast<std::size_t>(ihi);
7903
7904 for (std::size_t i = 0; i < n; ++i)
7905 {
7906 result.eigenvalues[i] = uni20::complex<Real>{wr[i], wi[i]};
7907 }
7908
7909 if (!compute_right_vectors)
7910 {
7911 return result;
7912 }
7913
7914 for (std::size_t col = 0; col < n; ++col)
7915 {
7916 if (wi[col] == Real{})
7917 {
7918 for (std::size_t row = 0; row < n; ++row)
7919 {
7920 result.right_eigenvectors[row, col] = uni20::complex<Real>{vr[row, col], Real{}};
7921 }
7922 }
7923 else if (wi[col] > Real{})
7924 {
7925 if (col + 1 >= n)
7926 {
7927 throw std::runtime_error("LAPACK geevx returned an incomplete complex conjugate eigenvector pair");
7928 }
7929 for (std::size_t row = 0; row < n; ++row)
7930 {
7931 uni20::complex<Real> const vector_value{vr[row, col], vr[row, col + 1]};
7932 result.right_eigenvectors[row, col] = vector_value;
7933 result.right_eigenvectors[row, col + 1] = std::conj(vector_value);
7934 }
7935 ++col;
7936 }
7937 }
7938
7939 return result;
7940}
7941
7956template <uni20::LapackReal Real>
7957RealGeneralizedNonsymmetricEigensystem<Real>
7959 detail::ColumnMajorLapackMatrix<Real> metric, bool compute_right_vectors)
7960{
7961 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
7962 {
7963 throw std::invalid_argument("real_generalized_nonsymmetric_eigensystem requires a square matrix");
7964 }
7965 if (!std::cmp_equal(metric.rows(), metric.cols()))
7966 {
7967 throw std::invalid_argument("real_generalized_nonsymmetric_eigensystem requires a square metric matrix");
7968 }
7969 if (!std::cmp_equal(matrix.rows(), metric.rows()))
7970 {
7971 throw std::invalid_argument("real_generalized_nonsymmetric_eigensystem received incompatible matrix sizes");
7972 }
7973
7974 std::size_t const n = static_cast<std::size_t>(matrix.rows());
7976 result.alpha.resize(n);
7977 result.beta.resize(n);
7978 result.eigenvalues.resize(n);
7979 result.right_eigenvectors = detail::ColumnMajorLapackMatrix<uni20::complex<Real>>(compute_right_vectors ? n : 0,
7980 compute_right_vectors ? n : 0);
7981 if (n == 0)
7982 {
7983 return result;
7984 }
7985
7986 blas_int const order = detail::checked_blas_int(n);
7987 std::vector<Real> alphar(n, Real{});
7988 std::vector<Real> alphai(n, Real{});
7989 std::vector<Real> beta(n, Real{});
7990 std::vector<Real> vl(1, Real{});
7991 detail::ColumnMajorLapackMatrix<Real> vr(compute_right_vectors ? n : 1, compute_right_vectors ? n : 1);
7992 blas_int const ldvl = 1;
7993 blas_int const ldvr = compute_right_vectors ? order : 1;
7994 char const jobvl = 'N';
7995 char const jobvr = compute_right_vectors ? 'V' : 'N';
7996
7997 Real work_query = Real{};
7998 blas_int const query_lwork = -1;
7999 uni20::lapack::ggev(jobvl, jobvr, order, matrix.data(), order, metric.data(), order, alphar.data(), alphai.data(),
8000 beta.data(), vl.data(), ldvl, vr.data(), ldvr, &work_query, query_lwork);
8001
8002 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
8003 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
8004 uni20::lapack::ggev(jobvl, jobvr, order, matrix.data(), order, metric.data(), order, alphar.data(), alphai.data(),
8005 beta.data(), vl.data(), ldvl, vr.data(), ldvr, work.data(), lwork);
8006
8007 for (std::size_t i = 0; i < n; ++i)
8008 {
8009 result.alpha[i] = uni20::complex<Real>{alphar[i], alphai[i]};
8010 result.beta[i] = beta[i];
8011 result.eigenvalues[i] = detail::generalized_eigenvalue(alphar[i], alphai[i], beta[i]);
8012 }
8013
8014 if (!compute_right_vectors)
8015 {
8016 return result;
8017 }
8018
8019 for (std::size_t col = 0; col < n; ++col)
8020 {
8021 if (alphai[col] == Real{})
8022 {
8023 for (std::size_t row = 0; row < n; ++row)
8024 {
8025 result.right_eigenvectors[row, col] = uni20::complex<Real>{vr[row, col], Real{}};
8026 }
8027 }
8028 else if (alphai[col] > Real{})
8029 {
8030 if (col + 1 >= n)
8031 {
8032 throw std::runtime_error("LAPACK ggev returned an incomplete complex conjugate eigenvector pair");
8033 }
8034 for (std::size_t row = 0; row < n; ++row)
8035 {
8036 uni20::complex<Real> const vector_value{vr[row, col], vr[row, col + 1]};
8037 result.right_eigenvectors[row, col] = vector_value;
8038 result.right_eigenvectors[row, col + 1] = std::conj(vector_value);
8039 }
8040 ++col;
8041 }
8042 }
8043
8044 return result;
8045}
8046
8062template <uni20::LapackReal Real>
8063RealGeneralizedNonsymmetricExpertEigensystem<Real>
8066 bool compute_right_vectors)
8067{
8068 if (!std::cmp_equal(matrix.rows(), matrix.cols()))
8069 {
8070 throw std::invalid_argument("real_generalized_nonsymmetric_expert_eigensystem requires a square matrix");
8071 }
8072 if (!std::cmp_equal(metric.rows(), metric.cols()))
8073 {
8074 throw std::invalid_argument("real_generalized_nonsymmetric_expert_eigensystem requires a square metric matrix");
8075 }
8076 if (!std::cmp_equal(matrix.rows(), metric.rows()))
8077 {
8078 throw std::invalid_argument("real_generalized_nonsymmetric_expert_eigensystem received incompatible matrix sizes");
8079 }
8080
8081 std::size_t const n = static_cast<std::size_t>(matrix.rows());
8083 result.alpha.resize(n);
8084 result.beta.resize(n);
8085 result.eigenvalues.resize(n);
8086 result.right_eigenvectors = detail::ColumnMajorLapackMatrix<uni20::complex<Real>>(compute_right_vectors ? n : 0,
8087 compute_right_vectors ? n : 0);
8090 result.left_balance_scale.resize(n);
8091 result.right_balance_scale.resize(n);
8092 if (n == 0)
8093 {
8094 return result;
8095 }
8096
8097 blas_int const order = detail::checked_blas_int(n);
8098 std::vector<Real> alphar(n, Real{});
8099 std::vector<Real> alphai(n, Real{});
8100 std::vector<Real> beta(n, Real{});
8103 blas_int const ldvl = order;
8104 blas_int const ldvr = order;
8105 char const balanc = 'B';
8106 char const jobvl = 'V';
8107 char const jobvr = 'V';
8108 char const sense = 'B';
8109 blas_int ilo = 0;
8110 blas_int ihi = 0;
8111 std::vector<blas_int> iwork(static_cast<std::size_t>(std::max<blas_int>(1, order + 6)), 0);
8112 std::vector<blas_int> bwork(static_cast<std::size_t>(std::max<blas_int>(1, order)), 0);
8113
8114 Real work_query = Real{};
8115 blas_int const query_lwork = -1;
8116 uni20::lapack::ggevx(balanc, jobvl, jobvr, sense, order, matrix.data(), order, metric.data(), order, alphar.data(),
8117 alphai.data(), beta.data(), vl.data(), ldvl, vr.data(), ldvr, ilo, ihi,
8118 result.left_balance_scale.data(), result.right_balance_scale.data(), result.balanced_matrix_norm,
8120 result.reciprocal_eigenvector_condition_numbers.data(), &work_query, query_lwork, iwork.data(),
8121 bwork.data());
8122
8123 blas_int const lwork = std::max<blas_int>(1, static_cast<blas_int>(work_query));
8124 std::vector<Real> work(static_cast<std::size_t>(lwork), Real{});
8125 uni20::lapack::ggevx(balanc, jobvl, jobvr, sense, order, matrix.data(), order, metric.data(), order, alphar.data(),
8126 alphai.data(), beta.data(), vl.data(), ldvl, vr.data(), ldvr, ilo, ihi,
8127 result.left_balance_scale.data(), result.right_balance_scale.data(), result.balanced_matrix_norm,
8129 result.reciprocal_eigenvector_condition_numbers.data(), work.data(), lwork, iwork.data(),
8130 bwork.data());
8131
8132 if (ilo <= 0 || ihi < ilo || ihi > order)
8133 {
8134 throw std::runtime_error("LAPACK ggevx returned an invalid balanced block interval");
8135 }
8136 result.balanced_first = static_cast<std::size_t>(ilo - 1);
8137 result.balanced_last_exclusive = static_cast<std::size_t>(ihi);
8138
8139 for (std::size_t i = 0; i < n; ++i)
8140 {
8141 result.alpha[i] = uni20::complex<Real>{alphar[i], alphai[i]};
8142 result.beta[i] = beta[i];
8143 result.eigenvalues[i] = detail::generalized_eigenvalue(alphar[i], alphai[i], beta[i]);
8144 }
8145
8146 if (!compute_right_vectors)
8147 {
8148 return result;
8149 }
8150
8151 for (std::size_t col = 0; col < n; ++col)
8152 {
8153 if (alphai[col] == Real{})
8154 {
8155 for (std::size_t row = 0; row < n; ++row)
8156 {
8157 result.right_eigenvectors[row, col] = uni20::complex<Real>{vr[row, col], Real{}};
8158 }
8159 }
8160 else if (alphai[col] > Real{})
8161 {
8162 if (col + 1 >= n)
8163 {
8164 throw std::runtime_error("LAPACK ggevx returned an incomplete complex conjugate eigenvector pair");
8165 }
8166 for (std::size_t row = 0; row < n; ++row)
8167 {
8168 uni20::complex<Real> const vector_value{vr[row, col], vr[row, col + 1]};
8169 result.right_eigenvectors[row, col] = vector_value;
8170 result.right_eigenvectors[row, col + 1] = std::conj(vector_value);
8171 }
8172 ++col;
8173 }
8174 }
8175
8176 return result;
8177}
8178
8179} // namespace uni20::krylov
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
auto rows() const noexcept
Return the matrix row count for rank-two tensors.
Definition basic_tensor.hpp:768
auto data() noexcept -> element_type *requires(immediately_writable &&(std::same_as< layout_type, stdex::layout_left >||std::same_as< layout_type, stdex::layout_right >) &&is_default_accessor_v< accessor_type > &&std::same_as< mutable_handle_type, element_type * >)
Return contiguous mutable element storage for a canonical tensor.
Definition basic_tensor.hpp:684
auto size() const noexcept -> size_type
Return the number of elements in the mapped storage span.
Definition basic_tensor.hpp:756
auto cols() const noexcept
Return the matrix column count for rank-two tensors.
Definition basic_tensor.hpp:776
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
Concept for real scalar types (float, double, etc.).
Definition scalar_concepts.hpp:69
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
locale_t value
Definition data_table.cpp:49
typename detail::complex_type< Real >::type complex
Project-level complex scalar spelling.
Definition types.hpp:66
int rows(std::FILE *stream)
Return the terminal row count associated with a C stream.
Definition terminal.cpp:482
Definition block_tensor_vector.hpp:23
Scalar real_matrix_norm(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixNorm norm)
Definition dense_subspace_unused.hpp:738
RealGeneralizedSchurSelectedSubspace< Real > real_generalized_schur_selected_subspace(RealGeneralizedSchurDecomposition< Real > decomposition, std::vector< std::size_t > const &selected_blocks)
Move selected generalized real Schur blocks to the leading deflating subspace and estimate conditioni...
Definition dense_subspace_unused.hpp:7516
detail::ColumnMajorLapackMatrix< Scalar > real_bidiagonal_right_orthogonal_factor_transpose(RealBidiagonalReduction< Scalar > const &reduction)
Materialize the right transposed factor P^T from a real bidiagonal reduction.
Definition dense_subspace_unused.hpp:5790
ComplexHermitianEigensystem< Real > complex_hermitian_eigensystem(detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > matrix, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense complex Hermitian eigensystem through LAPACK heev.
Definition dense_subspace_unused.hpp:4478
detail::ColumnMajorLapackMatrix< Scalar > real_dense_solve_linear_system(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a dense real linear system through LAPACK gesv.
Definition dense_subspace_unused.hpp:2576
detail::ColumnMajorLapackMatrix< Scalar > apply_real_bidiagonal_left_factor(RealBidiagonalReduction< Scalar > const &reduction, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Left, MatrixTranspose transpose=MatrixTranspose::None)
Apply the left orthogonal factor Q from a real bidiagonal reduction.
Definition dense_subspace_unused.hpp:5842
RealGeneralizedSchurDecomposition< Real > real_generalized_hessenberg_schur(detail::ColumnMajorLapackMatrix< Real > hessenberg, detail::ColumnMajorLapackMatrix< Real > upper_triangular_metric, bool compute_vectors)
Compute generalized real Schur form from a generalized Hessenberg pencil through LAPACK hgeqz.
Definition dense_subspace_unused.hpp:7224
RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > real_symmetric_positive_definite_tridiagonal_from_dense(detail::ColumnMajorLapackMatrix< Scalar > const &matrix)
Extract real SPD tridiagonal storage from a dense matrix.
Definition dense_subspace_unused.hpp:2198
RealCompactQrFactorization< Scalar > real_compact_qr_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a compact dense real QR factorization through LAPACK geqrf.
Definition dense_subspace_unused.hpp:4844
RealHessenbergReduction< Scalar > real_hessenberg_reduction(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Reduce a dense real square matrix to upper Hessenberg form through LAPACK gehrd.
Definition dense_subspace_unused.hpp:5960
RealGeneralizedNonsymmetricEigensystem< Real > real_generalized_nonsymmetric_eigensystem(detail::ColumnMajorLapackMatrix< Real > matrix, detail::ColumnMajorLapackMatrix< Real > metric, bool compute_right_vectors)
Solve a dense real generalized nonsymmetric eigensystem through LAPACK ggev.
Definition dense_subspace_unused.hpp:7958
Scalar real_general_tridiagonal_one_norm(RealGeneralTridiagonalMatrix< Scalar > const &matrix)
Compute the one-norm of a real general tridiagonal matrix.
Definition dense_subspace_unused.hpp:1182
RealGeneralizedSchurConditionEstimates< Real > real_generalized_schur_condition_estimates(RealGeneralizedSchurDecomposition< Real > decomposition)
Estimate generalized Schur eigenvalue and eigenvector conditioning through LAPACK tgsna.
Definition dense_subspace_unused.hpp:7768
detail::ColumnMajorLapackMatrix< Scalar > real_triangular_inverse(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper, bool unit_diagonal=false)
Invert a dense real triangular matrix through LAPACK trtri.
Definition dense_subspace_unused.hpp:2958
Scalar real_symmetric_indefinite_one_norm_reciprocal_condition_number(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Estimate a dense real symmetric-indefinite one-norm reciprocal condition number through LAPACK sycon.
Definition dense_subspace_unused.hpp:4029
RealRefinedLinearSolve< Scalar > real_symmetric_positive_definite_tridiagonal_refined_solve(RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve and refine a real SPD tridiagonal linear system through LAPACK ptrfs.
Definition dense_subspace_unused.hpp:2412
RealSymmetricEigensystem< Scalar > real_symmetric_eigensystem(detail::ColumnMajorLapackMatrix< Scalar > matrix, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense real symmetric eigensystem through LAPACK syev.
Definition dense_subspace_unused.hpp:4117
TridiagonalEigensystem< Scalar > symmetric_tridiagonal_eigensystem_divide_and_conquer(std::vector< Scalar > diagonal, std::vector< Scalar > subdiagonal, bool compute_vectors)
Solve a real symmetric tridiagonal eigenvalue problem through LAPACK sterf.
Definition dense_subspace_unused.hpp:6563
RealGeneralTridiagonalFactorization< Scalar > real_general_tridiagonal_factorization(RealGeneralTridiagonalMatrix< Scalar > matrix)
Compute a real general tridiagonal LU factorization through LAPACK gttrf.
Definition dense_subspace_unused.hpp:1079
RealGeneralizedSchurRightEigenvectors< Real > real_generalized_schur_right_eigenvectors(RealGeneralizedSchurDecomposition< Real > decomposition)
Compute right eigenvectors of a generalized real Schur form through LAPACK tgevc.
Definition dense_subspace_unused.hpp:7665
RealQrFactorization< Scalar > real_qr_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a reduced dense real QR factorization through LAPACK geqrf and orgqr.
Definition dense_subspace_unused.hpp:4944
RealCompactLqFactorization< Scalar > real_compact_lq_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a compact dense real LQ factorization through LAPACK gelqf.
Definition dense_subspace_unused.hpp:4999
RealRefinedLinearSolve< Scalar > real_general_band_refined_solve(RealGeneralBandMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixTranspose transpose=MatrixTranspose::None)
Solve and refine a real general-band system through LAPACK gbrfs.
Definition dense_subspace_unused.hpp:1513
detail::ColumnMajorLapackMatrix< Scalar > real_general_band_solve(RealGeneralBandMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a real general-band linear system through LAPACK gbsv.
Definition dense_subspace_unused.hpp:884
RealSymmetricPositiveDefiniteBandExpertLinearSolve< Scalar > real_symmetric_positive_definite_band_expert_linear_solve(RealSymmetricPositiveDefiniteBandMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a real SPD band linear system with LAPACK pbsvx diagnostics.
Definition dense_subspace_unused.hpp:2138
RealSymmetricEigensystem< Scalar > real_generalized_symmetric_eigensystem(detail::ColumnMajorLapackMatrix< Scalar > matrix, detail::ColumnMajorLapackMatrix< Scalar > metric, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense type-1 generalized real symmetric eigensystem through LAPACK sygv.
Definition dense_subspace_unused.hpp:4282
detail::ColumnMajorLapackMatrix< Scalar > real_least_squares(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a dense real least-squares or minimum-norm problem through LAPACK gels.
Definition dense_subspace_unused.hpp:3130
void laset(uni20::DenseMatrix< Scalar > &matrix, Scalar const &diagonal, Scalar const &off_diagonal, MatrixFill fill)
Set a selected region of a local dense matrix.
Definition dense_linalg.hpp:294
detail::ColumnMajorLapackMatrix< Scalar > real_lu_solve(RealLuFactorization< Scalar > const &factorization, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixTranspose transpose=MatrixTranspose::None)
Solve using an existing dense real LU factorization through LAPACK getrs.
Definition dense_subspace_unused.hpp:2713
detail::ColumnMajorLapackMatrix< Scalar > real_dense_inverse(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Invert a dense real square matrix through LAPACK getrf and getri.
Definition dense_subspace_unused.hpp:3093
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_indefinite_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper)
Solve a dense real symmetric indefinite system through LAPACK sytrf and sytrs.
Definition dense_subspace_unused.hpp:3778
RealSvdLeastSquares< Scalar > real_divide_and_conquer_svd_least_squares(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, Scalar rcond=Scalar{100} *uni20::numeric_limits< Scalar >::epsilon())
Solve a dense real divide-and-conquer SVD least-squares problem through LAPACK gelsd.
Definition dense_subspace_unused.hpp:3277
Scalar real_one_norm_reciprocal_condition_number(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Estimate the one-norm reciprocal condition number of a dense real matrix.
Definition dense_subspace_unused.hpp:2846
RealLuFactorization< Scalar > real_lu_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a dense real LU factorization through LAPACK getrf.
Definition dense_subspace_unused.hpp:2673
RealSymmetricEigensystem< Scalar > real_generalized_symmetric_eigensystem_index_range(detail::ColumnMajorLapackMatrix< Scalar > matrix, detail::ColumnMajorLapackMatrix< Scalar > metric, std::size_t first_index, std::size_t last_index, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a selected dense type-1 generalized real symmetric eigensystem through LAPACK sygvx.
Definition dense_subspace_unused.hpp:4405
RealSymmetricPositiveDefiniteBandMatrix< Scalar > real_symmetric_positive_definite_band_from_dense(detail::ColumnMajorLapackMatrix< Scalar > const &matrix, std::size_t bandwidth, MatrixFill triangle=MatrixFill::Upper)
Pack a dense real SPD matrix into LAPACK symmetric-band storage.
Definition dense_subspace_unused.hpp:1812
RealNonsymmetricExpertEigensystem< Real > real_nonsymmetric_expert_eigensystem(detail::ColumnMajorLapackMatrix< Real > matrix, bool compute_right_vectors)
Solve a dense real nonsymmetric eigensystem through LAPACK geev.
Definition dense_subspace_unused.hpp:7848
detail::ColumnMajorLapackMatrix< Real > real_nonsymmetric_balance_backtransform_right_vectors(detail::ColumnMajorLapackMatrix< Real > vectors, RealNonsymmetricBalance< Real > const &balance, RealNonsymmetricBalanceJob job=RealNonsymmetricBalanceJob::Both)
Backtransform real right eigenvectors after real_nonsymmetric_balance.
Definition dense_subspace_unused.hpp:7002
Scalar real_symmetric_matrix_norm(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixNorm norm, MatrixFill triangle=MatrixFill::Upper)
Compute a dense real symmetric matrix norm through LAPACK lansy.
Definition dense_subspace_unused.hpp:760
RealBidiagonalSvd< Scalar > real_bidiagonal_svd(std::vector< Scalar > diagonal, std::vector< Scalar > offdiagonal, bool compute_vectors, bool upper=true)
Compute the SVD of a real bidiagonal matrix through LAPACK bdsdc.
Definition dense_subspace_unused.hpp:5588
RealGeneralizedSchurDecomposition< Real > real_generalized_schur(detail::ColumnMajorLapackMatrix< Real > matrix, detail::ColumnMajorLapackMatrix< Real > metric, bool compute_vectors)
Compute the generalized real Schur decomposition of a matrix pencil through LAPACK gges.
Definition dense_subspace_unused.hpp:7324
RealGeneralBandExpertLinearSolve< Scalar > real_general_band_expert_linear_solve(RealGeneralBandMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixTranspose transpose=MatrixTranspose::None)
Solve a real general-band linear system with LAPACK gbsvx diagnostics.
Definition dense_subspace_unused.hpp:1582
RealSymmetricPositiveDefiniteBandFactorization< Scalar > real_symmetric_positive_definite_band_factorization(RealSymmetricPositiveDefiniteBandMatrix< Scalar > matrix)
Compute a real SPD band Cholesky factorization through LAPACK pbtrf.
Definition dense_subspace_unused.hpp:1895
RealRqFactorization< Scalar > real_rq_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a reduced dense real RQ factorization through LAPACK gerqf and orgrq.
Definition dense_subspace_unused.hpp:5413
RealGeneralizedNonsymmetricExpertEigensystem< Real > real_generalized_nonsymmetric_expert_eigensystem(detail::ColumnMajorLapackMatrix< Real > matrix, detail::ColumnMajorLapackMatrix< Real > metric, bool compute_right_vectors)
Solve a dense real generalized nonsymmetric eigensystem through LAPACK ggevx.
Definition dense_subspace_unused.hpp:8064
RealSymmetricPositiveDefiniteExpertLinearSolve< Scalar > real_symmetric_positive_definite_expert_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper)
Solve a dense real SPD linear system with LAPACK posvx diagnostics.
Definition dense_subspace_unused.hpp:3573
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_positive_definite_tridiagonal_solve(RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a real SPD tridiagonal system through LAPACK ptsv.
Definition dense_subspace_unused.hpp:2230
detail::ColumnMajorLapackMatrix< Scalar > real_triangular_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper, MatrixTranspose transpose=MatrixTranspose::None, bool unit_diagonal=false)
Solve a dense real triangular system through LAPACK trtrs.
Definition dense_subspace_unused.hpp:2867
RealSchurSelectedSubspace< Scalar > real_schur_selected_subspace(RealSchurDecomposition< Scalar > decomposition, std::vector< std::size_t > const &selected_blocks)
Compute the real Schur decomposition of a layout-right matrix through LAPACK gees.
Definition dense_subspace_unused.hpp:6685
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_positive_definite_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper)
Solve a dense real symmetric positive definite system through LAPACK potrf and potrs.
Definition dense_subspace_unused.hpp:3448
detail::ColumnMajorLapackMatrix< Scalar > apply_real_bidiagonal_right_factor(RealBidiagonalReduction< Scalar > const &reduction, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Right, MatrixTranspose transpose=MatrixTranspose::None)
Apply the right orthogonal factor P from a real bidiagonal reduction.
Definition dense_subspace_unused.hpp:5902
RealSymmetricPositiveDefiniteFactorization< Scalar > real_symmetric_positive_definite_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Compute a dense real SPD Cholesky factorization through LAPACK potrf.
Definition dense_subspace_unused.hpp:1779
RealSingularValueDecomposition< Scalar > real_singular_value_decomposition(detail::ColumnMajorLapackMatrix< Scalar > matrix, bool compute_vectors)
Compute a dense real singular value decomposition through LAPACK gesvd.
Definition dense_subspace_unused.hpp:6383
ComplexHermitianEigensystem< Real > complex_hermitian_eigensystem_divide_and_conquer(detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > matrix, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense complex Hermitian eigensystem through LAPACK heevd.
Definition dense_subspace_unused.hpp:4524
RealGeneralizedNonsymmetricBalance< Real > real_generalized_nonsymmetric_balance(detail::ColumnMajorLapackMatrix< Real > matrix, detail::ColumnMajorLapackMatrix< Real > metric, RealNonsymmetricBalanceJob job=RealNonsymmetricBalanceJob::Both)
Balance a dense real nonsymmetric matrix pencil through LAPACK ggbal.
Definition dense_subspace_unused.hpp:7043
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_positive_definite_inverse(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Invert a dense real SPD matrix through LAPACK potrf and potri.
Definition dense_subspace_unused.hpp:3697
TridiagonalEigensystem< Scalar > symmetric_tridiagonal_eigensystem_index_range(std::vector< Scalar > diagonal, std::vector< Scalar > subdiagonal, std::size_t first_index, std::size_t last_index, bool compute_vectors)
Solve a selected real symmetric tridiagonal eigensystem through LAPACK stevr.
Definition dense_subspace_unused.hpp:6622
RealRefinedLinearSolve< Scalar > real_refined_linear_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixTranspose transpose=MatrixTranspose::None)
Solve and refine a dense real linear system through LAPACK gerfs.
Definition dense_subspace_unused.hpp:2764
ComplexHermitianEigensystem< Real > complex_generalized_hermitian_eigensystem_index_range(detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > matrix, detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > metric, std::size_t first_index, std::size_t last_index, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a selected dense type-1 generalized complex Hermitian eigensystem through LAPACK hegvx.
Definition dense_subspace_unused.hpp:4769
MatrixTranspose
Matrix transpose mode for local dense Krylov operations.
Definition dense_linalg.hpp:78
RealSymmetricIndefiniteFactorization< Scalar > real_symmetric_indefinite_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Compute a dense real symmetric-indefinite Bunch-Kaufman factorization through LAPACK sytrf.
Definition dense_subspace_unused.hpp:3729
Scalar real_general_band_matrix_norm(RealGeneralBandMatrix< Scalar > band_matrix, MatrixNorm norm)
Compute a real general-band matrix norm through LAPACK langb.
Definition dense_subspace_unused.hpp:854
RealGeneralBandFactorization< Scalar > real_general_band_factorization(RealGeneralBandMatrix< Scalar > matrix)
Compute a real general-band LU factorization through LAPACK gbtrf.
Definition dense_subspace_unused.hpp:916
detail::ColumnMajorLapackMatrix< Scalar > apply_real_lq_factor(RealCompactLqFactorization< Scalar > const &factorization, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Right, MatrixTranspose transpose=MatrixTranspose::None)
Apply the orthogonal factor from a compact real LQ factorization.
Definition dense_subspace_unused.hpp:5040
RealCompactQlFactorization< Scalar > real_compact_ql_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a compact dense real QL factorization through LAPACK geqlf.
Definition dense_subspace_unused.hpp:5154
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_positive_definite_band_solve(RealSymmetricPositiveDefiniteBandMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a real SPD band system through LAPACK pbsv.
Definition dense_subspace_unused.hpp:1863
RealEquilibration< Scalar > real_general_band_power_of_two_equilibration(RealGeneralBandMatrix< Scalar > band_matrix)
Compute power-of-two real general-band equilibration factors through LAPACK gbequb.
Definition dense_subspace_unused.hpp:1691
RealSymmetricPositiveDefiniteTridiagonalFactorization< Scalar > real_symmetric_positive_definite_tridiagonal_factorization(RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > matrix)
Compute a real SPD tridiagonal factorization through LAPACK pttrf.
Definition dense_subspace_unused.hpp:2263
RealSymmetricEigensystem< Scalar > real_symmetric_eigensystem_index_range(detail::ColumnMajorLapackMatrix< Scalar > matrix, std::size_t first_index, std::size_t last_index, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a selected dense real symmetric eigensystem through LAPACK syevr.
Definition dense_subspace_unused.hpp:4214
Scalar real_symmetric_positive_definite_tridiagonal_one_norm(RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > const &matrix)
Compute the one-norm of a real SPD tridiagonal matrix.
Definition dense_subspace_unused.hpp:2327
RealSymmetricPositiveDefiniteEquilibration< Scalar > real_symmetric_positive_definite_equilibration(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute dense real SPD equilibration factors through LAPACK poequ.
Definition dense_subspace_unused.hpp:1751
Scalar real_symmetric_positive_definite_band_one_norm_reciprocal_condition_number(RealSymmetricPositiveDefiniteBandFactorization< Scalar > const &factorization, Scalar original_one_norm)
Estimate a real SPD band one-norm reciprocal condition number through LAPACK pbcon.
Definition dense_subspace_unused.hpp:2022
Scalar real_general_band_one_norm_reciprocal_condition_number(RealGeneralBandFactorization< Scalar > const &factorization, Scalar original_one_norm)
Estimate a real general-band one-norm reciprocal condition number through LAPACK gbcon.
Definition dense_subspace_unused.hpp:1443
RealSchurRightEigenvectors< Scalar > real_schur_right_eigenvectors(RealSchurDecomposition< Scalar > decomposition)
Compute right eigenvectors of a real Schur form through LAPACK trevc.
Definition dense_subspace_unused.hpp:6802
std::vector< Scalar > real_bidiagonal_singular_values(std::vector< Scalar > diagonal, std::vector< Scalar > offdiagonal, bool upper=true)
Compute singular values of a real bidiagonal matrix through LAPACK bdsqr.
Definition dense_subspace_unused.hpp:5539
RealSingularValueDecomposition< Scalar > real_singular_value_decomposition_divide_and_conquer(detail::ColumnMajorLapackMatrix< Scalar > matrix, bool compute_vectors)
Compute a dense real singular value decomposition through LAPACK gesdd.
Definition dense_subspace_unused.hpp:6439
RealSymmetricIndefiniteExpertLinearSolve< Scalar > real_symmetric_indefinite_expert_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper)
Solve a dense real symmetric-indefinite system with LAPACK sysvx diagnostics.
Definition dense_subspace_unused.hpp:3964
RealGeneralizedHessenbergReduction< Real > real_generalized_hessenberg_reduction(detail::ColumnMajorLapackMatrix< Real > matrix, detail::ColumnMajorLapackMatrix< Real > upper_triangular_metric, bool compute_vectors)
Reduce a real matrix pencil to generalized upper Hessenberg form through LAPACK gghrd.
Definition dense_subspace_unused.hpp:7148
RealRefinedLinearSolve< Scalar > real_general_tridiagonal_refined_solve(RealGeneralTridiagonalMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixTranspose transpose=MatrixTranspose::None)
Solve and refine a real general-tridiagonal linear system through LAPACK gtrfs.
Definition dense_subspace_unused.hpp:1291
RealSvdLeastSquares< Scalar > real_svd_least_squares(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, Scalar rcond=Scalar{100} *uni20::numeric_limits< Scalar >::epsilon())
Solve a dense real SVD-based least-squares problem through LAPACK gelss.
Definition dense_subspace_unused.hpp:3197
detail::ColumnMajorLapackMatrix< Scalar > real_bidiagonal_left_orthogonal_factor(RealBidiagonalReduction< Scalar > const &reduction)
Materialize the left orthogonal factor Q from a real bidiagonal reduction.
Definition dense_subspace_unused.hpp:5741
RealGeneralTridiagonalExpertLinearSolve< Scalar > real_general_tridiagonal_expert_linear_solve(RealGeneralTridiagonalMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixTranspose transpose=MatrixTranspose::None)
Solve a real general-tridiagonal linear system with LAPACK gtsvx diagnostics.
Definition dense_subspace_unused.hpp:1368
RealRefinedLinearSolve< Scalar > real_triangular_refined_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper, MatrixTranspose transpose=MatrixTranspose::None, bool unit_diagonal=false)
Solve and refine a dense real triangular system through LAPACK trrfs.
Definition dense_subspace_unused.hpp:2907
RealRankRevealingLeastSquares< Scalar > real_rank_revealing_least_squares(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, Scalar rcond=Scalar{100} *uni20::numeric_limits< Scalar >::epsilon())
Solve a dense real rank-revealing least-squares problem through LAPACK gelsy.
Definition dense_subspace_unused.hpp:3358
ComplexHermitianEigensystem< Real > complex_hermitian_eigensystem_index_range(detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > matrix, std::size_t first_index, std::size_t last_index, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a selected dense complex Hermitian eigensystem through LAPACK heevr.
Definition dense_subspace_unused.hpp:4577
RealSingularValueDecomposition< Scalar > real_singular_value_decomposition_index_range(detail::ColumnMajorLapackMatrix< Scalar > matrix, std::size_t first_index, std::size_t last_index, bool compute_vectors)
Compute a selected-index dense real SVD through LAPACK gesvdx.
Definition dense_subspace_unused.hpp:6498
detail::ColumnMajorLapackMatrix< Scalar > apply_real_qr_factor(RealCompactQrFactorization< Scalar > const &factorization, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Left, MatrixTranspose transpose=MatrixTranspose::None)
Apply the orthogonal factor from a compact real QR factorization.
Definition dense_subspace_unused.hpp:4885
RealSylvesterSolution< Scalar > real_sylvester_solve(detail::ColumnMajorLapackMatrix< Scalar > left, detail::ColumnMajorLapackMatrix< Scalar > right, detail::ColumnMajorLapackMatrix< Scalar > rhs, int sign=1, MatrixTranspose transpose_left=MatrixTranspose::None, MatrixTranspose transpose_right=MatrixTranspose::None)
Solve a dense real Sylvester equation through LAPACK trsyl.
Definition dense_subspace_unused.hpp:3045
RealRefinedLinearSolve< Scalar > real_symmetric_positive_definite_refined_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper)
Solve and refine a dense real SPD system through LAPACK porfs.
Definition dense_subspace_unused.hpp:3519
RealBidiagonalReduction< Scalar > real_bidiagonal_reduction(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Reduce a dense real matrix to bidiagonal form through LAPACK gebrd.
Definition dense_subspace_unused.hpp:5475
RealEquilibration< Scalar > real_equilibration(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute dense real row/column equilibration factors through LAPACK geequ.
Definition dense_subspace_unused.hpp:1725
RealSymmetricTridiagonalReduction< Scalar > real_symmetric_tridiagonal_reduction(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Reduce a dense real symmetric matrix to tridiagonal form through LAPACK sytrd.
Definition dense_subspace_unused.hpp:6134
RealBidiagonalSvd< Scalar > real_bidiagonal_svd_index_range(std::vector< Scalar > diagonal, std::vector< Scalar > offdiagonal, std::size_t first_index, std::size_t last_index, bool compute_vectors, bool upper=true)
Compute a selected-index SVD of a real bidiagonal matrix through LAPACK bdsvdx.
Definition dense_subspace_unused.hpp:5655
Scalar real_triangular_one_norm_reciprocal_condition_number(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper, bool unit_diagonal=false)
Estimate a dense real triangular one-norm reciprocal condition number through LAPACK trcon.
Definition dense_subspace_unused.hpp:3006
Scalar real_general_tridiagonal_one_norm_reciprocal_condition_number(RealGeneralTridiagonalFactorization< Scalar > const &factorization, Scalar original_one_norm)
Estimate a real general-tridiagonal one-norm reciprocal condition number through LAPACK gtcon.
Definition dense_subspace_unused.hpp:1217
RealEquilibration< Scalar > real_general_band_equilibration(RealGeneralBandMatrix< Scalar > band_matrix)
Compute real general-band row/column equilibration factors through LAPACK gbequ.
Definition dense_subspace_unused.hpp:1657
RealNonsymmetricBalanceJob
LAPACK balancing operations for a dense real nonsymmetric matrix.
Definition dense_subspace_unused.hpp:461
Scalar real_triangular_matrix_norm(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixNorm norm, MatrixFill triangle=MatrixFill::Upper, bool unit_diagonal=false)
Compute a dense real triangular or trapezoidal matrix norm through LAPACK lantr.
Definition dense_subspace_unused.hpp:790
uni20::linalg::MatrixRegion MatrixFill
Matrix region selected by LAPACK-style copy and set operations.
Definition dense_linalg.hpp:92
Scalar real_symmetric_positive_definite_tridiagonal_one_norm_reciprocal_condition_number(RealSymmetricPositiveDefiniteTridiagonalFactorization< Scalar > const &factorization, Scalar original_one_norm)
Estimate a real SPD tridiagonal one-norm reciprocal condition number through LAPACK ptcon.
Definition dense_subspace_unused.hpp:2363
MatrixSide
Matrix side selected by LAPACK-style multiply and solve operations.
Definition dense_linalg.hpp:86
RealNonsymmetricBalance< Real > real_nonsymmetric_balance(detail::ColumnMajorLapackMatrix< Real > matrix, RealNonsymmetricBalanceJob job=RealNonsymmetricBalanceJob::Both)
Reorder a layout-right complex Schur decomposition by moving selected entries to the front.
Definition dense_subspace_unused.hpp:6958
detail::ColumnMajorLapackMatrix< Scalar > real_general_tridiagonal_solve(RealGeneralTridiagonalMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a real general tridiagonal linear system through LAPACK gtsv.
Definition dense_subspace_unused.hpp:1044
RealSymmetricPositiveDefiniteTridiagonalExpertLinearSolve< Scalar > real_symmetric_positive_definite_tridiagonal_expert_linear_solve(RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a real SPD tridiagonal linear system with LAPACK ptsvx diagnostics.
Definition dense_subspace_unused.hpp:2466
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_tridiagonal_orthogonal_factor(RealSymmetricTridiagonalReduction< Scalar > const &reduction)
Generate the orthogonal factor from a compact real symmetric tridiagonal reduction.
Definition dense_subspace_unused.hpp:6191
RealExpertLinearSolve< Scalar > real_expert_linear_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve a dense real linear system with LAPACK gesvx diagnostics.
Definition dense_subspace_unused.hpp:2613
RealCompactRqFactorization< Scalar > real_compact_rq_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a compact dense real RQ factorization through LAPACK gerqf.
Definition dense_subspace_unused.hpp:5313
detail::ColumnMajorLapackMatrix< Scalar > real_symmetric_indefinite_inverse(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Invert a dense real symmetric-indefinite matrix through LAPACK sytrf and sytri.
Definition dense_subspace_unused.hpp:3920
MatrixNorm
LAPACK-compatible dense matrix norm selector.
Definition dense_linalg.hpp:96
RealSchurConditionEstimates< Scalar > real_schur_condition_estimates(RealSchurDecomposition< Scalar > decomposition)
Estimate Schur-form eigenvalue and eigenvector conditioning through LAPACK trsna.
Definition dense_subspace_unused.hpp:6892
RealQlFactorization< Scalar > real_ql_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a reduced dense real QL factorization through LAPACK geqlf and orgql.
Definition dense_subspace_unused.hpp:5254
RealLqFactorization< Scalar > real_lq_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a reduced dense real LQ factorization through LAPACK gelqf and orglq.
Definition dense_subspace_unused.hpp:5099
RealPivotedCholeskyFactorization< Scalar > real_pivoted_cholesky_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix, Scalar tolerance=Scalar{-1}, MatrixFill triangle=MatrixFill::Upper)
Compute a dense real pivoted Cholesky factorization through LAPACK pstrf.
Definition dense_subspace_unused.hpp:2523
detail::ColumnMajorLapackMatrix< Scalar > apply_real_rq_factor(RealCompactRqFactorization< Scalar > const &factorization, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Right, MatrixTranspose transpose=MatrixTranspose::None)
Apply the orthogonal factor from a compact real RQ factorization.
Definition dense_subspace_unused.hpp:5354
detail::ColumnMajorLapackMatrix< Real > real_generalized_nonsymmetric_balance_backtransform_right_vectors(detail::ColumnMajorLapackMatrix< Real > vectors, RealGeneralizedNonsymmetricBalance< Real > const &balance, RealNonsymmetricBalanceJob job=RealNonsymmetricBalanceJob::Both)
Backtransform real right eigenvectors after generalized pencil balancing.
Definition dense_subspace_unused.hpp:7099
RealGeneralTridiagonalMatrix< Scalar > real_general_tridiagonal_from_dense(detail::ColumnMajorLapackMatrix< Scalar > const &matrix)
Extract real general tridiagonal storage from a dense matrix.
Definition dense_subspace_unused.hpp:1010
RealPivotedQrFactorization< Scalar > real_pivoted_qr_factorization(detail::ColumnMajorLapackMatrix< Scalar > matrix)
Compute a reduced dense real pivoted QR factorization through LAPACK geqp3.
Definition dense_subspace_unused.hpp:6305
detail::ColumnMajorLapackMatrix< Scalar > apply_real_symmetric_tridiagonal_orthogonal_factor(RealSymmetricTridiagonalReduction< Scalar > const &reduction, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Left, MatrixTranspose transpose=MatrixTranspose::None)
Apply the orthogonal factor from a compact real symmetric tridiagonal reduction.
Definition dense_subspace_unused.hpp:6242
Scalar real_symmetric_positive_definite_one_norm_reciprocal_condition_number(detail::ColumnMajorLapackMatrix< Scalar > matrix, MatrixFill triangle=MatrixFill::Upper)
Estimate a dense real SPD one-norm reciprocal condition number through LAPACK pocon.
Definition dense_subspace_unused.hpp:3627
RealSymmetricEigensystem< Scalar > real_symmetric_eigensystem_divide_and_conquer(detail::ColumnMajorLapackMatrix< Scalar > matrix, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense real symmetric eigensystem through LAPACK syevd.
Definition dense_subspace_unused.hpp:4163
RealRefinedLinearSolve< Scalar > real_symmetric_positive_definite_band_refined_solve(RealSymmetricPositiveDefiniteBandMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides)
Solve and refine a real SPD band linear system through LAPACK pbrfs.
Definition dense_subspace_unused.hpp:2082
RealGeneralizedSchurDecomposition< Real > reorder_real_generalized_schur(RealGeneralizedSchurDecomposition< Real > decomposition, std::vector< std::size_t > const &leading_block_order)
Reorder a generalized real Schur decomposition by moving selected blocks to the front.
Definition dense_subspace_unused.hpp:7408
ComplexHermitianEigensystem< Real > complex_generalized_hermitian_eigensystem(detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > matrix, detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > metric, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense type-1 generalized complex Hermitian eigensystem through LAPACK hegv.
Definition dense_subspace_unused.hpp:4647
detail::ColumnMajorLapackMatrix< Scalar > apply_real_hessenberg_orthogonal_factor(RealHessenbergReduction< Scalar > const &reduction, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Left, MatrixTranspose transpose=MatrixTranspose::None)
Apply the orthogonal factor from a compact real Hessenberg reduction.
Definition dense_subspace_unused.hpp:6067
detail::ColumnMajorLapackMatrix< Scalar > real_hessenberg_orthogonal_factor(RealHessenbergReduction< Scalar > const &reduction)
Generate the orthogonal factor from a compact real Hessenberg reduction.
Definition dense_subspace_unused.hpp:6014
detail::ColumnMajorLapackMatrix< Scalar > apply_real_ql_factor(RealCompactQlFactorization< Scalar > const &factorization, detail::ColumnMajorLapackMatrix< Scalar > target, MatrixSide side=MatrixSide::Left, MatrixTranspose transpose=MatrixTranspose::None)
Apply the orthogonal factor from a compact real QL factorization.
Definition dense_subspace_unused.hpp:5195
Scalar real_lu_one_norm_reciprocal_condition_number(RealLuFactorization< Scalar > const &factorization, Scalar original_one_norm)
Estimate the one-norm reciprocal condition number from an LU factorization.
Definition dense_subspace_unused.hpp:2814
RealRefinedLinearSolve< Scalar > real_symmetric_indefinite_refined_solve(detail::ColumnMajorLapackMatrix< Scalar > coefficients, detail::ColumnMajorLapackMatrix< Scalar > right_hand_sides, MatrixFill triangle=MatrixFill::Upper)
Solve and refine a dense real symmetric-indefinite system through LAPACK syrfs.
Definition dense_subspace_unused.hpp:3862
RealSymmetricEigensystem< Scalar > real_generalized_symmetric_eigensystem_divide_and_conquer(detail::ColumnMajorLapackMatrix< Scalar > matrix, detail::ColumnMajorLapackMatrix< Scalar > metric, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense type-1 generalized real symmetric eigensystem through LAPACK sygvd.
Definition dense_subspace_unused.hpp:4341
RealGeneralBandMatrix< Scalar > real_general_band_from_dense(detail::ColumnMajorLapackMatrix< Scalar > const &matrix, std::size_t lower_bandwidth, std::size_t upper_bandwidth)
Pack a dense real square matrix into LAPACK general-band factor storage.
Definition dense_subspace_unused.hpp:817
Scalar real_symmetric_positive_definite_band_one_norm(RealSymmetricPositiveDefiniteBandMatrix< Scalar > const &matrix)
Compute the one-norm of a real SPD band matrix.
Definition dense_subspace_unused.hpp:1962
ComplexHermitianEigensystem< Real > complex_generalized_hermitian_eigensystem_divide_and_conquer(detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > matrix, detail::ColumnMajorLapackMatrix< uni20::detail::standard_complex< Real > > metric, bool compute_vectors, MatrixFill triangle=MatrixFill::Upper)
Solve a dense type-1 generalized complex Hermitian eigensystem through LAPACK hegvd.
Definition dense_subspace_unused.hpp:4702
void getrf(blas_int m, blas_int n, Scalar *a, blas_int lda, blas_int *ipiv)
Compute a real dense general LU factorization through the configured LAPACK backend.
Definition lapack.hpp:402
void geqp3(blas_int m, blas_int n, Scalar *a, blas_int lda, blas_int *jpvt, Scalar *tau, Scalar *work, blas_int lwork)
Compute a real dense pivoted QR factorization.
Definition lapack.hpp:647
void sygv(blas_int itype, char jobz, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *w, Scalar *work, blas_int lwork)
Compute eigenvalues and optionally eigenvectors of a real generalized symmetric-definite problem.
Definition lapack.hpp:692
Scalar lange(char norm, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *work)
Compute a dense real matrix norm through the configured LAPACK backend.
Definition lapack.hpp:185
void gbsv(blas_int n, blas_int kl, blas_int ku, blas_int nrhs, Scalar *ab, blas_int ldab, blas_int *ipiv, Scalar *b, blas_int ldb)
Solve a real general-band linear system through the configured LAPACK backend.
Definition lapack.hpp:235
void syevd(char jobz, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *w, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Compute eigenvalues and optionally eigenvectors of a real symmetric matrix by divide and conquer.
Definition lapack.hpp:672
void ptsv(blas_int n, blas_int nrhs, Scalar *d, Scalar *e, Scalar *b, blas_int ldb)
Solve a real symmetric positive-definite tridiagonal linear system.
Definition lapack.hpp:1537
void ormql(char side, char trans, blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by the implicit real Q factor from a QL factorization.
Definition lapack.hpp:611
void bdsqr(char uplo, blas_int n, blas_int ncvt, blas_int nru, blas_int ncc, Scalar *d, Scalar *e, Scalar *vt, blas_int ldvt, Scalar *u, blas_int ldu, Scalar *c, blas_int ldc, Scalar *work)
Compute singular values of a real bidiagonal matrix.
Definition lapack.hpp:1247
void ggbak(char job, char side, blas_int n, blas_int first, blas_int last, Scalar *lscale, Scalar *rscale, blas_int vector_count, Scalar *vectors, blas_int leading_dimension)
Back-transform generalized nonsymmetric eigenvectors after pencil balancing.
Definition lapack.hpp:1004
void gbequ(bool power_of_two, blas_int m, blas_int n, blas_int kl, blas_int ku, Scalar *ab, blas_int ldab, Scalar *row_scale, Scalar *column_scale, Scalar &row_condition, Scalar &column_condition, Scalar &max_abs)
Compute real general-band row/column equilibration factors.
Definition lapack.hpp:296
void potrs(char uplo, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb)
Solve using an existing real dense Cholesky factorization.
Definition lapack.hpp:1316
void bdsdc(char uplo, char compq, blas_int n, Scalar *d, Scalar *e, Scalar *u, blas_int ldu, Scalar *vt, blas_int ldvt, Scalar *q, blas_int *iq, Scalar *work, blas_int *iwork)
Compute the singular value decomposition of a real bidiagonal matrix.
Definition lapack.hpp:1256
Scalar langb(char norm, blas_int n, blas_int kl, blas_int ku, Scalar *ab, blas_int ldab, Scalar *work)
Compute a real general-band matrix norm through the configured LAPACK backend.
Definition lapack.hpp:228
bool trsyl(char trans_a, char trans_b, blas_int sign, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *c, blas_int ldc, Scalar &scale)
Solve a dense real Sylvester equation.
Definition lapack.hpp:1471
void sytrf(char uplo, blas_int n, Scalar *a, blas_int lda, blas_int *ipiv, Scalar *work, blas_int lwork)
Compute a dense real symmetric-indefinite Bunch-Kaufman factorization.
Definition lapack.hpp:1377
void gelsy(blas_int m, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, blas_int *jpvt, Scalar rcond, blas_int &rank, Scalar *work, blas_int lwork)
Solve a real dense general least-squares problem by complete orthogonal factorization.
Definition lapack.hpp:516
void gghrd(char compq, char compz, blas_int n, blas_int first, blas_int last, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *q, blas_int ldq, Scalar *z, blas_int ldz)
Reduce a real matrix pencil to generalized Hessenberg form.
Definition lapack.hpp:1037
void poequ(blas_int n, Scalar *a, blas_int lda, Scalar *scale, Scalar &scale_condition, Scalar &max_abs)
Compute real dense SPD equilibration factors.
Definition lapack.hpp:1355
void sygvx(blas_int itype, char jobz, char range, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar vl, Scalar vu, blas_int il, blas_int iu, Scalar abstol, blas_int &selected_count, Scalar *w, Scalar *z, blas_int ldz, Scalar *work, blas_int lwork, blas_int *iwork, blas_int *ifail)
Compute selected eigenvalues and optionally eigenvectors of a real generalized symmetric-definite pro...
Definition lapack.hpp:712
void gttrf(blas_int n, Scalar *dl, Scalar *d, Scalar *du, Scalar *du2, blas_int *ipiv)
Compute a real tridiagonal LU factorization through the configured LAPACK backend.
Definition lapack.hpp:1488
void pbsv(char uplo, blas_int n, blas_int kd, blas_int nrhs, Scalar *ab, blas_int ldab, Scalar *b, blas_int ldb)
Solve a real symmetric positive-definite band linear system.
Definition lapack.hpp:306
void bdsvdx(char uplo, char jobz, char range, blas_int n, Scalar *d, Scalar *e, Scalar vl, Scalar vu, blas_int il, blas_int iu, blas_int &selected_count, Scalar *singular_values, Scalar *z, blas_int ldz, Scalar *work, blas_int *iwork)
Compute selected singular values/vectors of a real bidiagonal matrix.
Definition lapack.hpp:1269
void orgqr(blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Generate an explicit orthogonal or unitary Q factor from a QR factorization.
Definition lapack.hpp:561
void ormrq(char side, char trans, blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by the implicit real Q factor from an RQ factorization.
Definition lapack.hpp:620
bool gesvx(char fact, char trans, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *af, blas_int ldaf, blas_int *ipiv, char &equed, Scalar *row_scale, Scalar *column_scale, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Solve a real dense general linear system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:382
bool pbsvx(char fact, char uplo, blas_int n, blas_int kd, blas_int nrhs, Scalar *ab, blas_int ldab, Scalar *afb, blas_int ldafb, char &equed, Scalar *scale, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Solve a real symmetric positive-definite band system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:354
void heev(char jobz, char uplo, blas_int n, uni20::detail::standard_complex< Real > *a, blas_int lda, Real *w, uni20::detail::standard_complex< Real > *work, blas_int lwork, Real *rwork)
Compute eigenvalues and optionally eigenvectors of a complex Hermitian matrix.
Definition lapack.hpp:723
void gesvdx(char jobu, char jobvt, char range, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar vl, Scalar vu, blas_int il, blas_int iu, blas_int &selected_count, Scalar *singular_values, Scalar *u, blas_int ldu, Scalar *vt, blas_int ldvt, Scalar *work, blas_int lwork, blas_int *iwork)
Compute selected singular values/vectors of a real dense matrix.
Definition lapack.hpp:1224
void sytri(char uplo, blas_int n, Scalar *a, blas_int lda, blas_int const *ipiv, Scalar *work)
Invert a dense real symmetric-indefinite matrix from its factorization.
Definition lapack.hpp:1393
Scalar lantr(char norm, char uplo, char diag, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *work)
Compute a dense real triangular or trapezoidal matrix norm through the configured LAPACK backend.
Definition lapack.hpp:213
void geqrf(blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Compute a real or complex dense QR factorization through the configured LAPACK backend.
Definition lapack.hpp:529
void ormhr(char side, char trans, blas_int m, blas_int n, blas_int first, blas_int last, Scalar *a, blas_int lda, Scalar const *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by an implicit real orthogonal factor from Hessenberg reduction.
Definition lapack.hpp:1159
void porfs(char uplo, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *factors, blas_int factor_lda, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a real dense SPD linear-system solve and return LAPACK diagnostics.
Definition lapack.hpp:1324
void ggevx(char balanc, char jobvl, char jobvr, char sense, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *alphar, Scalar *alphai, Scalar *beta, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, blas_int &ilo, blas_int &ihi, Scalar *lscale, Scalar *rscale, Scalar &abnrm, Scalar &bbnrm, Scalar *rconde, Scalar *rcondv, Scalar *work, blas_int lwork, blas_int *iwork, blas_int *bwork)
Compute generalized real nonsymmetric eigenpairs with balancing and condition estimates.
Definition lapack.hpp:978
void tgexc(bool wantq, bool wantz, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *q, blas_int ldq, Scalar *z, blas_int ldz, blas_int &first, blas_int &last, Scalar *work, blas_int lwork)
Reorder adjacent blocks in a generalized real Schur form.
Definition lapack.hpp:1065
void potri(char uplo, blas_int n, Scalar *a, blas_int lda)
Invert a real dense matrix from an existing Cholesky factorization.
Definition lapack.hpp:1304
void tgsna(char job, char howmny, blas_int *select, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, Scalar *reciprocal_eigenvalue_condition_numbers, Scalar *reciprocal_eigenvector_condition_numbers, blas_int mm, blas_int &computed_estimates, Scalar *work, blas_int lwork, blas_int *iwork)
Estimate reciprocal condition numbers for generalized real Schur eigenpairs.
Definition lapack.hpp:1116
void orgtr(char uplo, blas_int n, Scalar *a, blas_int lda, Scalar const *tau, Scalar *work, blas_int lwork)
Generate an explicit real orthogonal factor from symmetric tridiagonal reduction.
Definition lapack.hpp:1151
void trtrs(char uplo, char trans, char diag, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb)
Solve a dense real triangular system.
Definition lapack.hpp:1434
void syevr(char jobz, char range, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar vl, Scalar vu, blas_int il, blas_int iu, Scalar abstol, blas_int &selected_count, Scalar *w, Scalar *z, blas_int ldz, blas_int *isuppz, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Compute selected eigenvalues and optionally eigenvectors of a real symmetric matrix.
Definition lapack.hpp:681
void gerqf(blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Compute a real dense RQ factorization through the configured LAPACK backend.
Definition lapack.hpp:553
void syev(char jobz, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *w, Scalar *work, blas_int lwork)
Compute eigenvalues and optionally eigenvectors of a real symmetric matrix.
Definition lapack.hpp:664
void geqlf(blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Compute a real dense QL factorization through the configured LAPACK backend.
Definition lapack.hpp:545
void gerfs(char trans, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *factors, blas_int factor_lda, blas_int const *ipiv, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a real dense general linear-system solve and return LAPACK diagnostics.
Definition lapack.hpp:436
void gelsd(blas_int m, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *s, Scalar rcond, blas_int &rank, Scalar *work, blas_int lwork, blas_int *iwork)
Solve a real dense general least-squares problem by divide-and-conquer SVD.
Definition lapack.hpp:507
void gttrs(char trans, blas_int n, blas_int nrhs, Scalar *dl, Scalar *d, Scalar *du, Scalar *du2, blas_int const *ipiv, Scalar *b, blas_int ldb)
Solve using an existing real tridiagonal LU factorization.
Definition lapack.hpp:1496
Scalar gtcon(char norm, blas_int n, Scalar *dl, Scalar *d, Scalar *du, Scalar *du2, blas_int const *ipiv, Scalar matrix_norm, Scalar *work, blas_int *iwork)
Estimate a real tridiagonal reciprocal condition number.
Definition lapack.hpp:1505
void hgeqz(char job, char compq, char compz, blas_int n, blas_int first, blas_int last, Scalar *h, blas_int ldh, Scalar *t, blas_int ldt, Scalar *alphar, Scalar *alphai, Scalar *beta, Scalar *q, blas_int ldq, Scalar *z, blas_int ldz, Scalar *work, blas_int lwork)
Compute a generalized Schur form from a real Hessenberg-triangular pencil.
Definition lapack.hpp:1050
void gebal(char job, blas_int n, Scalar *a, blas_int lda, blas_int &first, blas_int &last, Scalar *scale)
Balance a real nonsymmetric matrix before eigenanalysis.
Definition lapack.hpp:810
void stevr(char jobz, char range, blas_int n, Scalar *d, Scalar *e, Scalar vl, Scalar vu, blas_int il, blas_int iu, Scalar abstol, blas_int &selected_count, Scalar *w, Scalar *z, blas_int ldz, blas_int *isuppz, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Compute selected eigenvalues/eigenvectors of a real symmetric tridiagonal matrix.
Definition lapack.hpp:1615
void gebak(char job, char side, blas_int n, blas_int first, blas_int last, Scalar *scale, blas_int vector_count, Scalar *vectors, blas_int leading_dimension)
Back-transform real nonsymmetric eigenvectors after balancing.
Definition lapack.hpp:822
void orgrq(blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Generate an explicit real Q factor from an RQ factorization.
Definition lapack.hpp:585
Scalar gbcon(char norm, blas_int n, blas_int kl, blas_int ku, Scalar *ab, blas_int ldab, blas_int const *ipiv, Scalar anorm, Scalar *work, blas_int *iwork)
Estimate a real general-band reciprocal condition number.
Definition lapack.hpp:261
void gels(char trans, blas_int m, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *work, blas_int lwork)
Solve a real dense general least-squares problem through the configured LAPACK backend.
Definition lapack.hpp:485
void orglq(blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Generate an explicit orthogonal or unitary Q factor from an LQ factorization.
Definition lapack.hpp:569
void gbtrf(blas_int m, blas_int n, blas_int kl, blas_int ku, Scalar *ab, blas_int ldab, blas_int *ipiv)
Compute a real general-band LU factorization through the configured LAPACK backend.
Definition lapack.hpp:244
void gelss(blas_int m, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *s, Scalar rcond, blas_int &rank, Scalar *work, blas_int lwork)
Solve a real dense general least-squares problem by SVD.
Definition lapack.hpp:498
void gbrfs(char trans, blas_int n, blas_int kl, blas_int ku, blas_int nrhs, Scalar *ab, blas_int ldab, Scalar *afb, blas_int ldafb, blas_int const *ipiv, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a real general-band linear-system solve and return LAPACK diagnostics.
Definition lapack.hpp:272
bool pstrf(char uplo, blas_int n, Scalar *a, blas_int lda, blas_int *pivots, blas_int &rank, Scalar tolerance, Scalar *work)
Compute a real dense pivoted Cholesky factorization.
Definition lapack.hpp:1291
void trsna(char job, char howmny, blas_int *select, blas_int n, Scalar *t, blas_int ldt, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, Scalar *reciprocal_eigenvalue_condition_numbers, Scalar *reciprocal_eigenvector_condition_numbers, blas_int mm, blas_int &computed_estimates, Scalar *work, blas_int ldwork, blas_int *iwork)
Estimate reciprocal condition numbers for real Schur eigenpairs.
Definition lapack.hpp:950
Scalar lansy(char norm, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *work)
Compute a dense real symmetric matrix norm through the configured LAPACK backend.
Definition lapack.hpp:199
void gehrd(blas_int n, blas_int first, blas_int last, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Reduce a real dense general matrix to upper Hessenberg form.
Definition lapack.hpp:638
void trrfs(char uplo, char trans, char diag, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a dense real triangular solve and return LAPACK diagnostics.
Definition lapack.hpp:1443
bool gbsvx(char fact, char trans, blas_int n, blas_int kl, blas_int ku, blas_int nrhs, Scalar *ab, blas_int ldab, Scalar *afb, blas_int ldafb, blas_int *ipiv, char &equed, Scalar *row_scale, Scalar *column_scale, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Solve a real general-band linear system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:283
void gtrfs(char trans, blas_int n, blas_int nrhs, Scalar *dl, Scalar *d, Scalar *du, Scalar *dlf, Scalar *df, Scalar *duf, Scalar *du2, blas_int const *ipiv, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a real tridiagonal linear-system solve and return LAPACK diagnostics.
Definition lapack.hpp:1516
void sygvd(blas_int itype, char jobz, char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *w, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Compute eigenvalues and optionally eigenvectors of a real generalized symmetric-definite problem by d...
Definition lapack.hpp:703
void geevx(char balanc, char jobvl, char jobvr, char sense, blas_int n, Scalar *a, blas_int lda, Scalar *wr, Scalar *wi, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, blas_int &ilo, blas_int &ihi, Scalar *scale, Scalar &abnrm, Scalar *rconde, Scalar *rcondv, Scalar *work, blas_int lwork, blas_int *iwork)
Compute real nonsymmetric eigenpairs with balancing and condition estimates.
Definition lapack.hpp:799
Scalar pbcon(char uplo, blas_int n, blas_int kd, Scalar *ab, blas_int ldab, Scalar anorm, Scalar *work, blas_int *iwork)
Estimate a real symmetric positive-definite band reciprocal condition number.
Definition lapack.hpp:333
void trtri(char uplo, char diag, blas_int n, Scalar *a, blas_int lda)
Invert a dense real triangular matrix.
Definition lapack.hpp:1453
void gbtrs(char trans, blas_int n, blas_int kl, blas_int ku, blas_int nrhs, Scalar *ab, blas_int ldab, blas_int const *ipiv, Scalar *b, blas_int ldb)
Solve using an existing real general-band LU factorization.
Definition lapack.hpp:252
void gesv(blas_int n, blas_int nrhs, Scalar *a, blas_int lda, blas_int *ipiv, Scalar *b, blas_int ldb)
Solve a real dense general linear system through the configured LAPACK backend.
Definition lapack.hpp:365
void orgql(blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Generate an explicit real Q factor from a QL factorization.
Definition lapack.hpp:577
void trsen(char job, char compq, blas_int *select, blas_int n, Scalar *t, blas_int ldt, Scalar *q, blas_int ldq, Scalar *wr, Scalar *wi, blas_int &selected_dimension, Scalar &reciprocal_eigenvalue_cluster_condition, Scalar &reciprocal_invariant_subspace_condition, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Reorder selected blocks in a real Schur form and estimate invariant subspace conditioning.
Definition lapack.hpp:915
void gebrd(blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *d, Scalar *e, Scalar *tauq, Scalar *taup, Scalar *work, blas_int lwork)
Reduce a real dense general matrix to bidiagonal form.
Definition lapack.hpp:629
void getri(blas_int n, Scalar *a, blas_int lda, blas_int *ipiv, Scalar *work, blas_int lwork)
Invert a real dense general matrix from an existing LU factorization.
Definition lapack.hpp:447
void ormlq(char side, char trans, blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by the implicit real Q factor from an LQ factorization.
Definition lapack.hpp:602
void orgbr(char vect, blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Generate an explicit real orthogonal factor from bidiagonal reduction.
Definition lapack.hpp:1133
void ptrfs(blas_int n, blas_int nrhs, Scalar *d, Scalar *e, Scalar *df, Scalar *ef, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work)
Refine a real symmetric positive-definite tridiagonal solve and return LAPACK diagnostics.
Definition lapack.hpp:1572
void trevc(char side, char howmny, blas_int *select, blas_int n, Scalar *t, blas_int ldt, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, blas_int mm, blas_int &computed_vectors, Scalar *work)
Compute eigenvectors of a real upper quasi-triangular Schur form.
Definition lapack.hpp:936
void gtsv(blas_int n, blas_int nrhs, Scalar *dl, Scalar *d, Scalar *du, Scalar *b, blas_int ldb)
Solve a real tridiagonal linear system through the configured LAPACK backend.
Definition lapack.hpp:1480
void orghr(blas_int n, blas_int first, blas_int last, Scalar *a, blas_int lda, Scalar const *tau, Scalar *work, blas_int lwork)
Generate an explicit real orthogonal factor from Hessenberg reduction.
Definition lapack.hpp:1142
void heevr(char jobz, char range, char uplo, blas_int n, uni20::detail::standard_complex< Real > *a, blas_int lda, Real vl, Real vu, blas_int il, blas_int iu, Real abstol, blas_int &selected_count, Real *w, uni20::detail::standard_complex< Real > *z, blas_int ldz, blas_int *isuppz, uni20::detail::standard_complex< Real > *work, blas_int lwork, Real *rwork, blas_int lrwork, blas_int *iwork, blas_int liwork)
Compute selected eigenvalues and optionally eigenvectors of a complex Hermitian matrix.
Definition lapack.hpp:742
bool posvx(char fact, char uplo, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *af, blas_int ldaf, char &equed, Scalar *scale, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Solve a real dense SPD linear system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:1335
void sytrs(char uplo, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, blas_int const *ipiv, Scalar *b, blas_int ldb)
Solve using an existing dense real symmetric-indefinite factorization.
Definition lapack.hpp:1385
void getrs(char trans, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, blas_int const *ipiv, Scalar *b, blas_int ldb)
Solve using an existing real dense general LU factorization.
Definition lapack.hpp:418
void hegvd(blas_int itype, char jobz, char uplo, blas_int n, uni20::detail::standard_complex< Real > *a, blas_int lda, uni20::detail::standard_complex< Real > *b, blas_int ldb, Real *w, uni20::detail::standard_complex< Real > *work, blas_int lwork, Real *rwork, blas_int lrwork, blas_int *iwork, blas_int liwork)
Compute eigenvalues and optionally eigenvectors of a complex generalized Hermitian-definite problem b...
Definition lapack.hpp:766
void geequ(blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *row_scale, Scalar *column_scale, Scalar &row_condition, Scalar &column_condition, Scalar &max_abs)
Compute real dense general row/column equilibration factors.
Definition lapack.hpp:393
void sytrd(char uplo, blas_int n, Scalar *a, blas_int lda, Scalar *d, Scalar *e, Scalar *tau, Scalar *work, blas_int lwork)
Reduce a real dense symmetric matrix to tridiagonal form.
Definition lapack.hpp:655
void pbtrs(char uplo, blas_int n, blas_int kd, blas_int nrhs, Scalar *ab, blas_int ldab, Scalar *b, blas_int ldb)
Solve using an existing real symmetric positive-definite band Cholesky factorization.
Definition lapack.hpp:321
void ggbal(char job, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, blas_int &first, blas_int &last, Scalar *lscale, Scalar *rscale, Scalar *work)
Balance a real nonsymmetric matrix pencil before generalized eigenanalysis.
Definition lapack.hpp:991
void pttrf(blas_int n, Scalar *d, Scalar *e)
Compute a real symmetric positive-definite tridiagonal factorization.
Definition lapack.hpp:1544
Scalar trcon(char norm, char uplo, char diag, blas_int n, Scalar *a, blas_int lda, Scalar *work, blas_int *iwork)
Estimate a dense real triangular reciprocal condition number.
Definition lapack.hpp:1461
void pttrs(blas_int n, blas_int nrhs, Scalar *d, Scalar *e, Scalar *b, blas_int ldb)
Solve using an existing real symmetric positive-definite tridiagonal factorization.
Definition lapack.hpp:1551
void hegvx(blas_int itype, char jobz, char range, char uplo, blas_int n, uni20::detail::standard_complex< Real > *a, blas_int lda, uni20::detail::standard_complex< Real > *b, blas_int ldb, Real vl, Real vu, blas_int il, blas_int iu, Real abstol, blas_int &selected_count, Real *w, uni20::detail::standard_complex< Real > *z, blas_int ldz, uni20::detail::standard_complex< Real > *work, blas_int lwork, Real *rwork, blas_int *iwork, blas_int *ifail)
Compute selected eigenvalues and optionally eigenvectors of a complex generalized Hermitian-definite ...
Definition lapack.hpp:777
void hegv(blas_int itype, char jobz, char uplo, blas_int n, uni20::detail::standard_complex< Real > *a, blas_int lda, uni20::detail::standard_complex< Real > *b, blas_int ldb, Real *w, uni20::detail::standard_complex< Real > *work, blas_int lwork, Real *rwork)
Compute eigenvalues and optionally eigenvectors of a complex generalized Hermitian-definite problem.
Definition lapack.hpp:755
void potrf(char uplo, blas_int n, Scalar *a, blas_int lda)
Compute a real dense Cholesky factorization.
Definition lapack.hpp:1279
void heevd(char jobz, char uplo, blas_int n, uni20::detail::standard_complex< Real > *a, blas_int lda, Real *w, uni20::detail::standard_complex< Real > *work, blas_int lwork, Real *rwork, blas_int lrwork, blas_int *iwork, blas_int liwork)
Compute eigenvalues and optionally eigenvectors of a complex Hermitian matrix by divide and conquer.
Definition lapack.hpp:732
void syrfs(char uplo, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *factors, blas_int factor_lda, blas_int const *ipiv, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a dense real symmetric-indefinite solve and return LAPACK diagnostics.
Definition lapack.hpp:1401
void ormtr(char side, char uplo, char trans, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar const *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by an implicit real orthogonal factor from symmetric tridiagonal reduction.
Definition lapack.hpp:1168
Scalar ptcon(blas_int n, Scalar *d, Scalar *e, Scalar matrix_norm, Scalar *work)
Estimate a real symmetric positive-definite tridiagonal reciprocal condition number.
Definition lapack.hpp:1562
Scalar sycon(char uplo, blas_int n, Scalar *a, blas_int lda, blas_int const *ipiv, Scalar matrix_one_norm, Scalar *work, blas_int *iwork)
Estimate a dense real symmetric-indefinite reciprocal condition number.
Definition lapack.hpp:1423
void pbtrf(char uplo, blas_int n, blas_int kd, Scalar *ab, blas_int ldab)
Compute a real symmetric positive-definite band Cholesky factorization.
Definition lapack.hpp:313
void ormbr(char vect, char side, char trans, blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by an implicit real orthogonal factor from bidiagonal reduction.
Definition lapack.hpp:1177
void pbrfs(char uplo, blas_int n, blas_int kd, blas_int nrhs, Scalar *ab, blas_int ldab, Scalar *afb, blas_int ldafb, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Refine a real symmetric positive-definite band solve and return LAPACK diagnostics.
Definition lapack.hpp:343
Scalar gecon(char norm, blas_int n, Scalar const *a, blas_int lda, Scalar anorm, Scalar *work, blas_int *iwork)
Estimate a real dense general reciprocal condition number.
Definition lapack.hpp:464
void stevd(char jobz, blas_int n, Scalar *d, Scalar *e, Scalar *z, blas_int ldz, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Compute a real symmetric tridiagonal eigensystem by divide-and-conquer.
Definition lapack.hpp:1606
void ggev(char jobvl, char jobvr, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *alphar, Scalar *alphai, Scalar *beta, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, Scalar *work, blas_int lwork)
Compute generalized eigenvalues and optionally eigenvectors of a real nonsymmetric matrix pencil.
Definition lapack.hpp:967
void gelqf(blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *tau, Scalar *work, blas_int lwork)
Compute a real or complex dense LQ factorization through the configured LAPACK backend.
Definition lapack.hpp:537
Scalar pocon(char uplo, blas_int n, Scalar *a, blas_int lda, Scalar matrix_one_norm, Scalar *work, blas_int *iwork)
Estimate a real dense SPD reciprocal condition number.
Definition lapack.hpp:1367
void tgevc(char side, char howmny, blas_int *select, blas_int n, Scalar *s, blas_int lds, Scalar *p, blas_int ldp, Scalar *vl, blas_int ldvl, Scalar *vr, blas_int ldvr, blas_int mm, blas_int &computed_vectors, Scalar *work)
Compute eigenvectors of a generalized real Schur form.
Definition lapack.hpp:1102
void gges(char jobvsl, char jobvsr, char sort, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, blas_int &selected_dimension, Scalar *alphar, Scalar *alphai, Scalar *beta, Scalar *vsl, blas_int ldvsl, Scalar *vsr, blas_int ldvsr, Scalar *work, blas_int lwork, blas_int *bwork)
Compute a generalized real Schur form of a real nonsymmetric matrix pencil.
Definition lapack.hpp:1018
void tgsen(blas_int ijob, bool wantq, bool wantz, blas_int *select, blas_int n, Scalar *a, blas_int lda, Scalar *b, blas_int ldb, Scalar *alphar, Scalar *alphai, Scalar *beta, Scalar *q, blas_int ldq, Scalar *z, blas_int ldz, blas_int &selected_dimension, Scalar &pl, Scalar &pr, Scalar *dif, Scalar *work, blas_int lwork, blas_int *iwork, blas_int liwork)
Reorder selected blocks in a generalized real Schur form and estimate conditioning.
Definition lapack.hpp:1082
bool sysvx(char fact, char uplo, blas_int n, blas_int nrhs, Scalar *a, blas_int lda, Scalar *af, blas_int ldaf, blas_int *ipiv, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int lwork, blas_int *iwork)
Solve a dense real symmetric-indefinite system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:1412
void gesvd(char jobu, char jobvt, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *s, Scalar *u, blas_int ldu, Scalar *vt, blas_int ldvt, Scalar *work, blas_int lwork)
Compute a real dense singular value decomposition.
Definition lapack.hpp:1186
void gesdd(char jobz, blas_int m, blas_int n, Scalar *a, blas_int lda, Scalar *s, Scalar *u, blas_int ldu, Scalar *vt, blas_int ldvt, Scalar *work, blas_int lwork, blas_int *iwork)
Compute a real dense divide-and-conquer singular value decomposition.
Definition lapack.hpp:1205
bool ptsvx(char fact, blas_int n, blas_int nrhs, Scalar *d, Scalar *e, Scalar *df, Scalar *ef, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work)
Solve a real symmetric positive-definite tridiagonal system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:1581
bool gtsvx(char fact, char trans, blas_int n, blas_int nrhs, Scalar *dl, Scalar *d, Scalar *du, Scalar *dlf, Scalar *df, Scalar *duf, Scalar *du2, blas_int *ipiv, Scalar *b, blas_int ldb, Scalar *x, blas_int ldx, Scalar &rcond, Scalar *forward_error, Scalar *backward_error, Scalar *work, blas_int *iwork)
Solve a real tridiagonal linear system with LAPACK expert-driver diagnostics.
Definition lapack.hpp:1527
void ormqr(char side, char trans, blas_int m, blas_int n, blas_int k, Scalar *a, blas_int lda, Scalar *tau, Scalar *c, blas_int ldc, Scalar *work, blas_int lwork)
Multiply by the implicit real Q factor from a QR factorization.
Definition lapack.hpp:593
auto matrix_norm_host(BackendSelector selector, async::Async< MatrixTensor > const &matrix, MatrixNorm kind)
Schedule a dense matrix norm returning a host C++ scalar through an explicit selector.
Definition async/matrix_norm.hpp:167
@ One
Induced one-norm: largest column sum of entry magnitudes.
MatrixRegion
Matrix region selected by structured initialization operations.
Definition operation_tags.hpp:208
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
Definition async/async.hpp:26
constexpr auto abs(basic_half_int< T > value) -> basic_half_int< T >
Return the absolute value of a half-integer.
Definition half_int.hpp:431
mpreal norm(mpcomplex const &z)
Definition mpcomplex.hpp:494
void fill(BackendSelector selector, async::Async< OutputTensor > &output, Value &&value)
Fill an existing async Tensor without reading its old values.
Definition linalg/async/transform.hpp:205
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
std::ptrdiff_t index_type
Definition types.hpp:19
mpreal beta(mpreal const &a, mpreal const &x)
Real Beta function; positive rational/integer pairs retain exact values.
Definition mpreal_special.hpp:164
Eigenvalues and optional eigenvectors of a dense complex Hermitian matrix.
Definition dense_subspace_unused.hpp:32
std::vector< Real > eigenvalues
Definition dense_subspace_unused.hpp:33
detail::ColumnMajorLapackMatrix< uni20::complex< Real > > eigenvectors
Definition dense_subspace_unused.hpp:34
Compact Householder bidiagonal reduction of a dense real matrix.
Definition dense_subspace_unused.hpp:393
std::vector< Scalar > taup
Definition dense_subspace_unused.hpp:399
bool upper
Definition dense_subspace_unused.hpp:400
detail::ColumnMajorLapackMatrix< Scalar > bidiagonal
Definition dense_subspace_unused.hpp:394
std::vector< Scalar > offdiagonal
Definition dense_subspace_unused.hpp:397
std::vector< Scalar > tauq
Definition dense_subspace_unused.hpp:398
std::vector< Scalar > diagonal
Definition dense_subspace_unused.hpp:396
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:395
Singular value decomposition of a real bidiagonal matrix.
Definition dense_subspace_unused.hpp:406
detail::ColumnMajorLapackMatrix< Scalar > vt
Definition dense_subspace_unused.hpp:409
std::vector< Scalar > singular_values
Definition dense_subspace_unused.hpp:407
detail::ColumnMajorLapackMatrix< Scalar > u
Definition dense_subspace_unused.hpp:408
Compact Householder LQ factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:366
std::vector< Scalar > tau
Definition dense_subspace_unused.hpp:368
std::size_t rank
Definition dense_subspace_unused.hpp:369
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:367
Compact Householder QL factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:375
std::size_t rank
Definition dense_subspace_unused.hpp:378
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:376
std::vector< Scalar > tau
Definition dense_subspace_unused.hpp:377
Compact Householder QR factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:357
std::size_t rank
Definition dense_subspace_unused.hpp:360
std::vector< Scalar > tau
Definition dense_subspace_unused.hpp:359
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:358
Compact Householder RQ factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:384
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:385
std::size_t rank
Definition dense_subspace_unused.hpp:387
std::vector< Scalar > tau
Definition dense_subspace_unused.hpp:386
Dense real general matrix equilibration factors.
Definition dense_subspace_unused.hpp:153
Scalar row_condition
Definition dense_subspace_unused.hpp:156
Scalar max_abs
Definition dense_subspace_unused.hpp:158
std::vector< Scalar > row_scale
Definition dense_subspace_unused.hpp:154
Scalar column_condition
Definition dense_subspace_unused.hpp:157
std::vector< Scalar > column_scale
Definition dense_subspace_unused.hpp:155
Expert dense real linear solve result.
Definition dense_subspace_unused.hpp:130
char equilibration
Definition dense_subspace_unused.hpp:137
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:131
std::vector< std::size_t > pivot_rows
Definition dense_subspace_unused.hpp:133
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:134
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:138
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:136
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:135
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:132
Expert real general-band linear solve result.
Definition dense_subspace_unused.hpp:116
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:124
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:120
RealGeneralBandMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:118
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:121
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:117
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:122
std::vector< std::size_t > pivot_rows
Definition dense_subspace_unused.hpp:119
char equilibration
Definition dense_subspace_unused.hpp:123
LU factorization of a real square general band matrix.
Definition dense_subspace_unused.hpp:71
RealGeneralBandMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:72
std::vector< std::size_t > pivot_rows
Definition dense_subspace_unused.hpp:73
LAPACK general band storage for a real square matrix.
Definition dense_subspace_unused.hpp:61
detail::ColumnMajorLapackMatrix< Scalar > storage
Definition dense_subspace_unused.hpp:62
std::size_t upper_bandwidth
Definition dense_subspace_unused.hpp:65
std::size_t lower_bandwidth
Definition dense_subspace_unused.hpp:64
std::size_t order
Definition dense_subspace_unused.hpp:63
Expert real general-tridiagonal linear solve result.
Definition dense_subspace_unused.hpp:104
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:108
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:109
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:107
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:110
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:105
RealGeneralTridiagonalFactorization< Scalar > factorization
Definition dense_subspace_unused.hpp:106
LU factorization of a real square general tridiagonal matrix.
Definition dense_subspace_unused.hpp:95
RealGeneralTridiagonalMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:96
std::vector< std::size_t > pivot_rows
Definition dense_subspace_unused.hpp:98
std::vector< Scalar > second_upper_diagonal
Definition dense_subspace_unused.hpp:97
LAPACK general tridiagonal storage for a real square matrix.
Definition dense_subspace_unused.hpp:81
std::vector< Scalar > lower_diagonal
Definition dense_subspace_unused.hpp:82
std::vector< Scalar > diagonal
Definition dense_subspace_unused.hpp:83
std::vector< Scalar > upper_diagonal
Definition dense_subspace_unused.hpp:84
std::size_t order() const noexcept
Definition dense_subspace_unused.hpp:86
Generalized Hessenberg reduction of a dense real matrix pencil.
Definition dense_subspace_unused.hpp:521
detail::ColumnMajorLapackMatrix< Real > matrix_hessenberg_form
Definition dense_subspace_unused.hpp:522
detail::ColumnMajorLapackMatrix< Real > metric_triangular_form
Definition dense_subspace_unused.hpp:523
detail::ColumnMajorLapackMatrix< Real > left_orthogonal_vectors
Definition dense_subspace_unused.hpp:524
detail::ColumnMajorLapackMatrix< Real > right_orthogonal_vectors
Definition dense_subspace_unused.hpp:525
std::size_t first
Definition dense_subspace_unused.hpp:526
std::size_t last_exclusive
Definition dense_subspace_unused.hpp:527
Balanced dense real nonsymmetric matrix pencil and LAPACK scaling metadata.
Definition dense_subspace_unused.hpp:509
std::size_t balanced_last_exclusive
Definition dense_subspace_unused.hpp:515
std::vector< Real > right_scale
Definition dense_subspace_unused.hpp:513
detail::ColumnMajorLapackMatrix< Real > balanced_metric
Definition dense_subspace_unused.hpp:511
std::size_t balanced_first
Definition dense_subspace_unused.hpp:514
std::vector< Real > left_scale
Definition dense_subspace_unused.hpp:512
detail::ColumnMajorLapackMatrix< Real > balanced_matrix
Definition dense_subspace_unused.hpp:510
Eigenvalues and optional right eigenvectors of a real nonsymmetric matrix pencil.
Definition dense_subspace_unused.hpp:481
detail::ColumnMajorLapackMatrix< uni20::complex< Real > > right_eigenvectors
Definition dense_subspace_unused.hpp:485
std::vector< uni20::complex< Real > > alpha
Definition dense_subspace_unused.hpp:482
std::vector< Real > beta
Definition dense_subspace_unused.hpp:483
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace_unused.hpp:484
Expert eigensystem diagnostics for a dense real nonsymmetric matrix pencil.
Definition dense_subspace_unused.hpp:491
std::size_t balanced_last_exclusive
Definition dense_subspace_unused.hpp:503
std::vector< Real > right_balance_scale
Definition dense_subspace_unused.hpp:499
std::vector< Real > reciprocal_eigenvalue_condition_numbers
Definition dense_subspace_unused.hpp:496
Real balanced_matrix_norm
Definition dense_subspace_unused.hpp:500
std::vector< Real > beta
Definition dense_subspace_unused.hpp:493
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace_unused.hpp:494
std::size_t balanced_first
Definition dense_subspace_unused.hpp:502
std::vector< uni20::complex< Real > > alpha
Definition dense_subspace_unused.hpp:492
Real balanced_metric_norm
Definition dense_subspace_unused.hpp:501
std::vector< Real > left_balance_scale
Definition dense_subspace_unused.hpp:498
std::vector< Real > reciprocal_eigenvector_condition_numbers
Definition dense_subspace_unused.hpp:497
detail::ColumnMajorLapackMatrix< uni20::complex< Real > > right_eigenvectors
Definition dense_subspace_unused.hpp:495
Reciprocal condition estimates for eigenpairs of a generalized real Schur form.
Definition dense_subspace_unused.hpp:569
std::vector< Real > reciprocal_eigenvalue_condition_numbers
Definition dense_subspace_unused.hpp:570
std::vector< Real > reciprocal_eigenvector_condition_numbers
Definition dense_subspace_unused.hpp:571
std::size_t computed_estimates
Definition dense_subspace_unused.hpp:572
One diagonal block of a real Schur form.
Definition dense_subspace_unused.hpp:534
std::size_t selected_dimension
Definition dense_subspace_unused.hpp:543
std::vector< Real > beta
Definition dense_subspace_unused.hpp:540
std::vector< uni20::complex< Real > > alpha
Definition dense_subspace_unused.hpp:539
detail::ColumnMajorLapackMatrix< Real > metric_schur_form
Definition dense_subspace_unused.hpp:536
detail::ColumnMajorLapackMatrix< Real > right_schur_vectors
Definition dense_subspace_unused.hpp:538
detail::ColumnMajorLapackMatrix< Real > left_schur_vectors
Definition dense_subspace_unused.hpp:537
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace_unused.hpp:541
std::vector< RealSchurBlock< Real > > blocks
Definition dense_subspace_unused.hpp:542
detail::ColumnMajorLapackMatrix< Real > matrix_schur_form
Definition dense_subspace_unused.hpp:535
Right eigenvectors of a generalized real Schur form unpacked into complex columns.
Definition dense_subspace_unused.hpp:561
detail::ColumnMajorLapackMatrix< uni20::complex< Real > > right_eigenvectors
Definition dense_subspace_unused.hpp:562
std::size_t computed_vectors
Definition dense_subspace_unused.hpp:563
Selected generalized real Schur subspace with LAPACK tgsen diagnostics.
Definition dense_subspace_unused.hpp:549
std::size_t selected_dimension
Definition dense_subspace_unused.hpp:551
Real left_projection_lower_bound
Definition dense_subspace_unused.hpp:552
RealGeneralizedSchurDecomposition< Real > decomposition
Definition dense_subspace_unused.hpp:550
Real lower_deflating_subspace_separation
Definition dense_subspace_unused.hpp:555
Real upper_deflating_subspace_separation
Definition dense_subspace_unused.hpp:554
Real right_projection_lower_bound
Definition dense_subspace_unused.hpp:553
Compact real Hessenberg reduction of a dense square matrix.
Definition dense_subspace_unused.hpp:415
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:417
std::size_t last_exclusive
Definition dense_subspace_unused.hpp:420
std::size_t first
Definition dense_subspace_unused.hpp:419
detail::ColumnMajorLapackMatrix< Scalar > hessenberg
Definition dense_subspace_unused.hpp:416
std::vector< Scalar > tau
Definition dense_subspace_unused.hpp:418
Reduced LQ factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:333
detail::ColumnMajorLapackMatrix< Scalar > q
Definition dense_subspace_unused.hpp:335
detail::ColumnMajorLapackMatrix< Scalar > l
Definition dense_subspace_unused.hpp:334
LU factorization of a dense real square matrix.
Definition dense_subspace_unused.hpp:49
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:50
std::vector< std::size_t > pivot_rows
Definition dense_subspace_unused.hpp:51
Balanced dense real nonsymmetric matrix and LAPACK scaling metadata.
Definition dense_subspace_unused.hpp:471
detail::ColumnMajorLapackMatrix< Real > balanced_matrix
Definition dense_subspace_unused.hpp:472
std::size_t balanced_first
Definition dense_subspace_unused.hpp:474
std::vector< Real > scale
Definition dense_subspace_unused.hpp:473
std::size_t balanced_last_exclusive
Definition dense_subspace_unused.hpp:475
Eigenvalues and optional right eigenvectors of a real nonsymmetric matrix.
Definition dense_subspace_unused.hpp:448
std::size_t balanced_first
Definition dense_subspace_unused.hpp:455
std::vector< Real > reciprocal_eigenvalue_condition_numbers
Definition dense_subspace_unused.hpp:451
std::vector< uni20::complex< Real > > eigenvalues
Definition dense_subspace_unused.hpp:449
detail::ColumnMajorLapackMatrix< uni20::complex< Real > > right_eigenvectors
Definition dense_subspace_unused.hpp:450
std::size_t balanced_last_exclusive
Definition dense_subspace_unused.hpp:456
std::vector< Real > balance_scale
Definition dense_subspace_unused.hpp:453
Real balanced_matrix_norm
Definition dense_subspace_unused.hpp:454
std::vector< Real > reciprocal_eigenvector_condition_numbers
Definition dense_subspace_unused.hpp:452
Pivoted Cholesky factorization of a dense real positive semidefinite matrix.
Definition dense_subspace_unused.hpp:265
MatrixFill triangle
Definition dense_subspace_unused.hpp:269
std::vector< std::size_t > pivot_order
Definition dense_subspace_unused.hpp:267
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:266
bool rank_deficient
Definition dense_subspace_unused.hpp:270
std::size_t rank
Definition dense_subspace_unused.hpp:268
Reduced QR factorization with column pivoting of a dense real matrix.
Definition dense_subspace_unused.hpp:438
detail::ColumnMajorLapackMatrix< Scalar > q
Definition dense_subspace_unused.hpp:439
detail::ColumnMajorLapackMatrix< Scalar > r
Definition dense_subspace_unused.hpp:440
std::vector< std::size_t > pivot_columns
Definition dense_subspace_unused.hpp:441
Reduced QL factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:341
detail::ColumnMajorLapackMatrix< Scalar > l
Definition dense_subspace_unused.hpp:343
detail::ColumnMajorLapackMatrix< Scalar > q
Definition dense_subspace_unused.hpp:342
Reduced QR factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:325
detail::ColumnMajorLapackMatrix< Scalar > r
Definition dense_subspace_unused.hpp:327
detail::ColumnMajorLapackMatrix< Scalar > q
Definition dense_subspace_unused.hpp:326
Rank-revealing dense real least-squares result.
Definition dense_subspace_unused.hpp:298
std::size_t rank
Definition dense_subspace_unused.hpp:300
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:299
std::vector< std::size_t > pivot_columns
Definition dense_subspace_unused.hpp:301
Dense real refined linear solve result.
Definition dense_subspace_unused.hpp:144
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:146
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:145
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:147
Reduced RQ factorization of a dense real matrix.
Definition dense_subspace_unused.hpp:349
detail::ColumnMajorLapackMatrix< Scalar > q
Definition dense_subspace_unused.hpp:351
detail::ColumnMajorLapackMatrix< Scalar > r
Definition dense_subspace_unused.hpp:350
Reciprocal condition estimates for eigenpairs of a real Schur form.
Definition dense_subspace_unused.hpp:597
std::vector< Scalar > reciprocal_eigenvalue_condition_numbers
Definition dense_subspace_unused.hpp:598
std::vector< Scalar > reciprocal_eigenvector_condition_numbers
Definition dense_subspace_unused.hpp:599
std::size_t computed_estimates
Definition dense_subspace_unused.hpp:600
Definition dense_subspace.hpp:73
uni20::DenseMatrix< Scalar > schur_form
Definition dense_subspace.hpp:74
std::vector< uni20::complex< Scalar > > eigenvalues
Definition dense_subspace.hpp:76
uni20::DenseMatrix< Scalar > schur_vectors
Definition dense_subspace.hpp:75
std::vector< RealSchurBlock< Scalar > > blocks
Definition dense_subspace.hpp:77
Right eigenvectors of a real Schur form unpacked into complex columns.
Definition dense_subspace_unused.hpp:589
std::size_t computed_vectors
Definition dense_subspace_unused.hpp:591
detail::ColumnMajorLapackMatrix< uni20::complex< Scalar > > right_eigenvectors
Definition dense_subspace_unused.hpp:590
Eigenvalues and optional right eigenvectors of a complex nonsymmetric matrix.
Definition dense_subspace_unused.hpp:579
Scalar reciprocal_invariant_subspace_condition
Definition dense_subspace_unused.hpp:583
RealSchurDecomposition< Scalar > decomposition
Definition dense_subspace_unused.hpp:580
Scalar reciprocal_eigenvalue_cluster_condition
Definition dense_subspace_unused.hpp:582
std::size_t selected_dimension
Definition dense_subspace_unused.hpp:581
Singular values and optional singular vectors of a dense real matrix.
Definition dense_subspace_unused.hpp:40
detail::ColumnMajorLapackMatrix< Scalar > left_singular_vectors
Definition dense_subspace_unused.hpp:42
detail::ColumnMajorLapackMatrix< Scalar > right_singular_vectors_transpose
Definition dense_subspace_unused.hpp:43
std::vector< Scalar > singular_values
Definition dense_subspace_unused.hpp:41
SVD-based dense real least-squares result.
Definition dense_subspace_unused.hpp:307
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:308
std::size_t rank
Definition dense_subspace_unused.hpp:309
std::vector< Scalar > singular_values
Definition dense_subspace_unused.hpp:310
Dense real Sylvester equation result.
Definition dense_subspace_unused.hpp:316
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:317
bool separation_perturbed
Definition dense_subspace_unused.hpp:319
Scalar scale
Definition dense_subspace_unused.hpp:318
Definition dense_subspace_unused.hpp:24
std::vector< Scalar > eigenvalues
Definition dense_subspace_unused.hpp:25
detail::ColumnMajorLapackMatrix< Scalar > eigenvectors
Definition dense_subspace_unused.hpp:26
Expert dense real symmetric-indefinite linear solve result.
Definition dense_subspace_unused.hpp:285
std::vector< int > pivot_blocks
Definition dense_subspace_unused.hpp:288
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:286
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:292
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:290
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:289
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:287
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:291
Bunch-Kaufman factorization of a dense real symmetric-indefinite matrix.
Definition dense_subspace_unused.hpp:276
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:277
std::vector< int > pivot_blocks
Definition dense_subspace_unused.hpp:278
MatrixFill triangle
Definition dense_subspace_unused.hpp:279
Expert real SPD band linear solve result.
Definition dense_subspace_unused.hpp:216
char equilibration
Definition dense_subspace_unused.hpp:223
RealSymmetricPositiveDefiniteBandFactorization< Scalar > factorization
Definition dense_subspace_unused.hpp:218
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:224
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:221
std::vector< Scalar > scale
Definition dense_subspace_unused.hpp:222
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:219
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:217
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:220
Cholesky factorization of a real SPD band matrix.
Definition dense_subspace_unused.hpp:209
RealSymmetricPositiveDefiniteBandMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:210
LAPACK symmetric positive-definite band storage for a real square matrix.
Definition dense_subspace_unused.hpp:199
detail::ColumnMajorLapackMatrix< Scalar > storage
Definition dense_subspace_unused.hpp:200
std::size_t order
Definition dense_subspace_unused.hpp:201
std::size_t bandwidth
Definition dense_subspace_unused.hpp:202
MatrixFill triangle
Definition dense_subspace_unused.hpp:203
Dense real SPD equilibration factors.
Definition dense_subspace_unused.hpp:178
Scalar max_abs
Definition dense_subspace_unused.hpp:181
std::vector< Scalar > scale
Definition dense_subspace_unused.hpp:179
Scalar scale_condition
Definition dense_subspace_unused.hpp:180
Expert dense real SPD linear solve result.
Definition dense_subspace_unused.hpp:164
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:169
char equilibration
Definition dense_subspace_unused.hpp:171
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:168
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:165
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:167
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:166
std::vector< Scalar > scale
Definition dense_subspace_unused.hpp:170
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:172
Cholesky factorization of a dense real SPD matrix.
Definition dense_subspace_unused.hpp:187
detail::ColumnMajorLapackMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:188
MatrixFill triangle
Definition dense_subspace_unused.hpp:189
Expert real SPD tridiagonal linear solve result.
Definition dense_subspace_unused.hpp:253
Scalar reciprocal_condition
Definition dense_subspace_unused.hpp:256
bool reciprocal_condition_below_machine_precision
Definition dense_subspace_unused.hpp:259
RealSymmetricPositiveDefiniteTridiagonalFactorization< Scalar > factorization
Definition dense_subspace_unused.hpp:255
std::vector< Scalar > backward_error_bounds
Definition dense_subspace_unused.hpp:258
std::vector< Scalar > forward_error_bounds
Definition dense_subspace_unused.hpp:257
detail::ColumnMajorLapackMatrix< Scalar > solution
Definition dense_subspace_unused.hpp:254
Cholesky factorization of a real SPD tridiagonal matrix.
Definition dense_subspace_unused.hpp:246
RealSymmetricPositiveDefiniteTridiagonalMatrix< Scalar > factors
Definition dense_subspace_unused.hpp:247
Real symmetric positive-definite tridiagonal matrix storage.
Definition dense_subspace_unused.hpp:233
std::vector< Scalar > diagonal
Definition dense_subspace_unused.hpp:234
std::vector< Scalar > offdiagonal
Definition dense_subspace_unused.hpp:235
std::size_t order() const noexcept
Definition dense_subspace_unused.hpp:237
Compact real symmetric tridiagonal reduction of a dense symmetric matrix.
Definition dense_subspace_unused.hpp:426
std::vector< Scalar > tau
Definition dense_subspace_unused.hpp:431
std::vector< Scalar > offdiagonal
Definition dense_subspace_unused.hpp:430
MatrixFill triangle
Definition dense_subspace_unused.hpp:432
detail::ColumnMajorLapackMatrix< Scalar > reflectors
Definition dense_subspace_unused.hpp:428
detail::ColumnMajorLapackMatrix< Scalar > tridiagonal
Definition dense_subspace_unused.hpp:427
std::vector< Scalar > diagonal
Definition dense_subspace_unused.hpp:429
Eigenvalues and optional eigenvectors of a symmetric tridiagonal matrix.
Definition dense_subspace.hpp:22
std::vector< Scalar > eigenvalues
Definition dense_subspace.hpp:23
uni20::DenseMatrix< Scalar > eigenvectors
Definition dense_subspace.hpp:24
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16