370 static RealScalar scaledBlockDeterminant(
const RealScalar& d11,
const RealScalar& d22,
const RealScalar& d21);
376 RealScalar determinantD()
const;
379 RealScalar m_l1_norm;
380 TranspositionType m_transpositions;
381 TmpVectorType m_subdiag;
382 WorkspaceType m_workspace;
386 bool m_isInitialized;
395template <
typename RealScalar>
396EIGEN_DEVICE_FUNC
inline RealScalar bunch_kaufman_alpha() {
398 return (RealScalar(1) + sqrt(RealScalar(17))) / RealScalar(8);
403template <
typename Scalar>
404inline Index bunch_kaufman_blocksize() {
405#ifdef EIGEN_BUNCHKAUFMAN_BLOCKSIZE
406 return Index(EIGEN_BUNCHKAUFMAN_BLOCKSIZE);
416struct bunch_kaufman<
Lower> {
421 template <
typename MatrixType>
422 static void apply_symmetric_pivot(MatrixType& mat, Index kfirst, Index kk, Index kp, Index kstep) {
423 using Scalar =
typename MatrixType::Scalar;
424 const Index n = mat.rows();
425 const Index s = n - kp - 1;
426 if (s > 0) mat.col(kk).tail(s).swap(mat.col(kp).tail(s));
427 for (Index i = kk + 1; i < kp; ++i) {
428 Scalar tmp = mat.coeff(i, kk);
429 mat.coeffRef(i, kk) = numext::conj(mat.coeff(kp, i));
430 mat.coeffRef(kp, i) = numext::conj(tmp);
432 numext::swap(mat.coeffRef(kk, kk), mat.coeffRef(kp, kp));
433 EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex) {
434 mat.coeffRef(kp, kk) = numext::conj(mat.coeff(kp, kk));
436 if (kfirst > 0) mat.row(kk).head(kfirst).swap(mat.row(kp).head(kfirst));
438 numext::swap(mat.coeffRef(kfirst + 1, kfirst), mat.coeffRef(kp, kfirst));
450 template <
typename MatrixType,
typename TranspositionType,
typename SubDiagType>
451 static Index unblocked(MatrixType& mat, TranspositionType& transpositions, SubDiagType& subdiag, Index k0 = 0) {
453 using Scalar =
typename MatrixType::Scalar;
454 using RealScalar =
typename MatrixType::RealScalar;
455 using StorageIndex =
typename TranspositionType::StorageIndex;
456 const Index n = mat.rows();
457 const RealScalar alpha = bunch_kaufman_alpha<RealScalar>();
464 const RealScalar absakk = abs(numext::real(mat.coeff(k, k)));
468 RealScalar colmax(0);
471 colmax = mat.col(k).tail(n - k - 1).cwiseAbs().maxCoeff(&rel);
475 if (numext::is_exactly_zero((numext::maxi)(absakk, colmax)) || (numext::isnan)(absakk)) {
478 if (info == 0) info = k + 1;
479 mat.coeffRef(k, k) = Scalar(numext::real(mat.coeff(k, k)));
480 }
else if (absakk >= alpha * colmax) {
485 RealScalar rowmax = mat.row(imax).segment(k, imax - k).cwiseAbs().maxCoeff();
487 RealScalar rowmax2 = mat.col(imax).tail(n - imax - 1).cwiseAbs().maxCoeff();
488 rowmax = (numext::maxi)(rowmax, rowmax2);
490 if (absakk >= alpha * colmax * (colmax / rowmax)) {
493 }
else if (abs(numext::real(mat.coeff(imax, imax))) >= alpha * rowmax) {
503 const Index kk = k + kstep - 1;
504 if (kp != kk) apply_symmetric_pivot(mat, k, kk, kp, kstep);
507 transpositions.coeffRef(k) = StorageIndex(kp);
508 subdiag.coeffRef(k) = Scalar(0);
510 const RealScalar dkk = numext::real(mat.coeff(k, k));
511 mat.coeffRef(k, k) = Scalar(dkk);
512 const Index rs = n - k - 1;
514 if (!numext::is_exactly_zero(dkk)) {
516 auto w = mat.col(k).tail(rs);
517 mat.block(k + 1, k + 1, rs, rs).template selfadjointView<Lower>().rankUpdate(w, RealScalar(-1) / dkk);
519 }
else if (info == 0) {
525 transpositions.coeffRef(k) = StorageIndex(k);
526 transpositions.coeffRef(k + 1) = StorageIndex(kp);
528 const RealScalar d11 = numext::real(mat.coeff(k, k));
529 const RealScalar d22 = numext::real(mat.coeff(k + 1, k + 1));
530 const Scalar d21 = mat.coeff(k + 1, k);
531 mat.coeffRef(k, k) = Scalar(d11);
532 mat.coeffRef(k + 1, k + 1) = Scalar(d22);
540 const Scalar cjd = numext::conj(d21);
541 const Scalar ak = numext::divide(d22, d21);
542 const Scalar akm1 = numext::divide(d11, cjd);
543 const RealScalar denom = numext::real(ak * akm1) - RealScalar(1);
549 if (info == 0 && !(denom < RealScalar(0) && denom > RealScalar(-2))) info = k + 1;
551 const Index rs = n - k - 2;
566 const RealScalar t = RealScalar(1) / denom;
567 auto c0 = mat.col(k).tail(rs);
568 auto c1 = mat.col(k + 1).tail(rs);
569 for (Index j = 0; j < rs; ++j) {
570 const Scalar u0 = c0.coeff(j);
571 const Scalar u1 = c1.coeff(j);
572 const Scalar l0 = t * numext::divide(ak * u0 - u1, cjd);
573 const Scalar l1 = t * numext::divide(akm1 * u1 - u0, d21);
574 const Index len = rs - j;
575 mat.col(k + 2 + j).tail(len) -= numext::conj(l0) * c0.tail(len) + numext::conj(l1) * c1.tail(len);
580 EIGEN_IF_CONSTEXPR (NumTraits<Scalar>::IsComplex) {
581 mat.coeffRef(k + 2 + j, k + 2 + j) = Scalar(numext::real(mat.coeff(k + 2 + j, k + 2 + j)));
586 subdiag.coeffRef(k) = d21;
587 subdiag.coeffRef(k + 1) = Scalar(0);
588 mat.coeffRef(k + 1, k) = Scalar(0);
601 template <
typename MatrixType,
typename WorkspaceType,
typename TranspositionType,
typename SubDiagType>
602 static Index partial_factor(MatrixType& mat, Index k0, Index nb, WorkspaceType& W, TranspositionType& transpositions,
603 SubDiagType& subdiag, Index& info) {
605 using Scalar =
typename MatrixType::Scalar;
606 using RealScalar =
typename MatrixType::RealScalar;
607 using StorageIndex =
typename TranspositionType::StorageIndex;
608 const Index n = mat.rows();
609 const RealScalar alpha = bunch_kaufman_alpha<RealScalar>();
610 constexpr bool is_complex = NumTraits<Scalar>::IsComplex;
614 const Index jc = k0 + j;
615 const Index h = n - jc;
619 W.col(j).segment(jc, h) = mat.col(jc).segment(jc, h);
620 EIGEN_IF_CONSTEXPR (is_complex) W.coeffRef(jc, j) = Scalar(numext::real(W.coeff(jc, j)));
622 W.col(j).segment(jc, h).noalias() -= mat.block(jc, k0, h, j) * W.row(jc).head(j).adjoint();
623 EIGEN_IF_CONSTEXPR (is_complex) W.coeffRef(jc, j) = Scalar(numext::real(W.coeff(jc, j)));
628 const RealScalar absakk = abs(numext::real(W.coeff(jc, j)));
630 RealScalar colmax(0);
633 colmax = W.col(j).segment(jc + 1, n - jc - 1).cwiseAbs().maxCoeff(&rel);
637 if (numext::is_exactly_zero((numext::maxi)(absakk, colmax)) || (numext::isnan)(absakk)) {
639 if (info == 0) info = jc + 1;
640 }
else if (absakk >= alpha * colmax) {
645 const Index hr = imax - jc;
646 if (hr > 0) W.col(j + 1).segment(jc, hr) = mat.row(imax).segment(jc, hr).adjoint();
647 W.col(j + 1).segment(imax, n - imax) = mat.col(imax).segment(imax, n - imax);
648 EIGEN_IF_CONSTEXPR (is_complex) W.coeffRef(imax, j + 1) = Scalar(numext::real(W.coeff(imax, j + 1)));
650 W.col(j + 1).segment(jc, n - jc).noalias() -= mat.block(jc, k0, n - jc, j) * W.row(imax).head(j).adjoint();
651 EIGEN_IF_CONSTEXPR (is_complex) W.coeffRef(imax, j + 1) = Scalar(numext::real(W.coeff(imax, j + 1)));
654 RealScalar rowmax(0);
655 if (imax > jc) rowmax = W.col(j + 1).segment(jc, imax - jc).cwiseAbs().maxCoeff();
657 RealScalar rowmax2 = W.col(j + 1).segment(imax + 1, n - imax - 1).cwiseAbs().maxCoeff();
658 rowmax = (numext::maxi)(rowmax, rowmax2);
660 if (absakk >= alpha * colmax * (colmax / rowmax)) {
662 }
else if (abs(numext::real(W.coeff(imax, j + 1))) >= alpha * rowmax) {
665 W.col(j).segment(jc, n - jc) = W.col(j + 1).segment(jc, n - jc);
673 if (kstep == 2 && j + 1 >= nb)
break;
675 const Index kk = jc + kstep - 1;
677 apply_symmetric_pivot(mat, jc, kk, kp, kstep);
678 const Index nwc = j + kstep;
679 W.row(kk).head(nwc).swap(W.row(kp).head(nwc));
683 transpositions.coeffRef(jc) = StorageIndex(kp);
684 subdiag.coeffRef(jc) = Scalar(0);
685 const RealScalar dval = numext::real(W.coeff(jc, j));
686 mat.coeffRef(jc, jc) = Scalar(dval);
687 const Index rs = n - jc - 1;
689 if (!numext::is_exactly_zero(dval)) {
690 mat.col(jc).tail(rs) = W.col(j).segment(jc + 1, rs) / dval;
695 mat.col(jc).tail(rs).setZero();
696 if (info == 0) info = jc + 1;
701 transpositions.coeffRef(jc) = StorageIndex(jc);
702 transpositions.coeffRef(jc + 1) = StorageIndex(kp);
703 const RealScalar d11 = numext::real(W.coeff(jc, j));
704 const Scalar d21 = W.coeff(jc + 1, j);
705 const RealScalar d22 = numext::real(W.coeff(jc + 1, j + 1));
706 mat.coeffRef(jc, jc) = Scalar(d11);
707 mat.coeffRef(jc + 1, jc + 1) = Scalar(d22);
712 const Scalar cjd = numext::conj(d21);
713 const Scalar ak = numext::divide(d22, d21);
714 const Scalar akm1 = numext::divide(d11, cjd);
715 const RealScalar denom = numext::real(ak * akm1) - RealScalar(1);
716 if (info == 0 && !(denom < RealScalar(0) && denom > RealScalar(-2))) info = jc + 1;
717 const Index rs = n - jc - 2;
722 const RealScalar t = RealScalar(1) / denom;
723 auto w0 = W.col(j).segment(jc + 2, rs);
724 auto w1 = W.col(j + 1).segment(jc + 2, rs);
725 mat.col(jc).tail(rs) = t * ((ak * w0 - w1) / cjd);
726 mat.col(jc + 1).tail(rs) = t * ((akm1 * w1 - w0) / d21);
728 subdiag.coeffRef(jc) = d21;
729 subdiag.coeffRef(jc + 1) = Scalar(0);
730 mat.coeffRef(jc + 1, jc) = Scalar(0);
738 template <
typename MatrixType,
typename TranspositionType,
typename SubDiagType,
typename WorkspaceType>
739 static Index blocked(MatrixType& mat, TranspositionType& transpositions, SubDiagType& subdiag,
740 WorkspaceType& workspace) {
741 const Index n = mat.rows();
742 const Index nb = bunch_kaufman_blocksize<typename MatrixType::Scalar>();
743 if (nb < 2 || n <= nb)
return unblocked(mat, transpositions, subdiag, 0);
746 workspace.resize(n, nb + 1);
751 const Index kb = partial_factor(mat, k, nb, workspace, transpositions, subdiag, info);
752 const Index rs = n - k - kb;
755 mat.block(k + kb, k + kb, rs, rs).template triangularView<Lower>() -=
756 mat.block(k + kb, k, rs, kb) * workspace.block(k + kb, 0, rs, kb).adjoint();
759 info = (info == 0) ? unblocked(mat, transpositions, subdiag, k) : info;
764 const Index info2 = unblocked(mat, transpositions, subdiag, k);
765 if (info == 0) info = info2;
774struct bunch_kaufman<
Upper> {
775 template <
typename MatrixType,
typename TranspositionType,
typename SubDiagType>
776 static EIGEN_STRONG_INLINE Index unblocked(MatrixType& mat, TranspositionType& transpositions, SubDiagType& subdiag,
778 Transpose<MatrixType> matt(mat);
779 return bunch_kaufman<Lower>::unblocked(matt, transpositions, subdiag, k0);
782 template <
typename MatrixType,
typename TranspositionType,
typename SubDiagType,
typename WorkspaceType>
783 static EIGEN_STRONG_INLINE Index blocked(MatrixType& mat, TranspositionType& transpositions, SubDiagType& subdiag,
784 WorkspaceType& workspace) {
785 Transpose<MatrixType> matt(mat);
786 return bunch_kaufman<Lower>::blocked(matt, transpositions, subdiag, workspace);
790template <
typename MatrixType>
791struct BunchKaufman_Traits<MatrixType,
Lower> {
792 using MatrixL =
const TriangularView<const MatrixType, UnitLower>;
793 using MatrixU =
const TriangularView<const typename MatrixType::AdjointReturnType, UnitUpper>;
794 static inline MatrixL getL(
const MatrixType& m) {
return MatrixL(m); }
795 static inline MatrixU getU(
const MatrixType& m) {
return MatrixU(m.adjoint()); }
798template <
typename MatrixType>
799struct BunchKaufman_Traits<MatrixType,
Upper> {
800 using MatrixL =
const TriangularView<const typename MatrixType::AdjointReturnType, UnitLower>;
801 using MatrixU =
const TriangularView<const MatrixType, UnitUpper>;
802 static inline MatrixL getL(
const MatrixType& m) {
return MatrixL(m.adjoint()); }
803 static inline MatrixU getU(
const MatrixType& m) {
return MatrixU(m); }
808template <
typename MatrixType,
int UpLo_>
809template <
typename Derived>
811 const Index n = m_matrix.rows();
814 if (k + 1 < n && !numext::is_exactly_zero(m_subdiag.coeff(k))) {
815 const RealScalar d11 = numext::real(m_matrix.coeff(k, k));
816 const RealScalar d22 = numext::real(m_matrix.coeff(k + 1, k + 1));
817 const Scalar d21 = m_subdiag.coeff(k);
818 for (Index j = 0; j < x.cols(); ++j) {
819 const Scalar x0 = x.coeff(k, j);
820 const Scalar x1 = x.coeff(k + 1, j);
821 x.coeffRef(k, j) = d11 * x0 + numext::conj(d21) * x1;
822 x.coeffRef(k + 1, j) = d21 * x0 + d22 * x1;
826 x.row(k) *= numext::real(m_matrix.coeff(k, k));
832template <
typename MatrixType,
int UpLo_>
833template <
bool Conjugate,
typename Derived>
836 const Index n = m_matrix.rows();
839 const RealScalar tol = (std::numeric_limits<RealScalar>::min)();
842 if (k + 1 < n && !numext::is_exactly_zero(m_subdiag.coeff(k))) {
843 const RealScalar d11 = numext::real(m_matrix.coeff(k, k));
844 const RealScalar d22 = numext::real(m_matrix.coeff(k + 1, k + 1));
846 const Scalar d21 = Conjugate ? m_subdiag.coeff(k) : numext::conj(m_subdiag.coeff(k));
851 const Scalar cjd = numext::conj(d21);
852 const Scalar ak = numext::divide(d22, d21);
853 const Scalar akm1 = numext::divide(d11, cjd);
854 const RealScalar t = RealScalar(1) / (numext::real(ak * akm1) - RealScalar(1));
855 for (Index j = 0; j < x.cols(); ++j) {
856 const Scalar bk = numext::divide(x.coeff(k + 1, j), d21);
857 const Scalar bkm1 = numext::divide(x.coeff(k, j), cjd);
858 x.coeffRef(k, j) = t * (ak * bkm1 - bk);
859 x.coeffRef(k + 1, j) = t * (akm1 * bk - bkm1);
863 const RealScalar dk = numext::real(m_matrix.coeff(k, k));
878template <
typename MatrixType,
int UpLo_>
880 const RealScalar& d11,
const RealScalar& d22,
const RealScalar& d21) {
881 const RealScalar q = (d11 / d21) * (d22 / d21);
882 return (q > RealScalar(-1) && q < RealScalar(1)) ? q - RealScalar(1) : RealScalar(-1);
885template <
typename MatrixType,
int UpLo_>
886void BunchKaufman<MatrixType, UpLo_>::computeInertia() {
887 const Index n = m_matrix.rows();
888 m_n_pos = m_n_neg = m_n_zero = 0;
891 if (k + 1 < n && !numext::is_exactly_zero(m_subdiag.coeff(k))) {
892 const RealScalar d11 = numext::real(m_matrix.coeff(k, k));
893 const RealScalar d22 = numext::real(m_matrix.coeff(k + 1, k + 1));
894 const RealScalar d21 = numext::abs(m_subdiag.coeff(k));
895 const RealScalar denom = scaledBlockDeterminant(d11, d22, d21);
896 if (denom < RealScalar(0)) {
900 }
else if (numext::is_exactly_zero(denom)) {
901 const RealScalar tr = d11 + d22;
902 if (tr > RealScalar(0))
904 else if (tr < RealScalar(0))
911 if (d11 + d22 > RealScalar(0))
918 const RealScalar dk = numext::real(m_matrix.coeff(k, k));
919 if (dk > RealScalar(0))
921 else if (dk < RealScalar(0))
932template <
typename MatrixType,
int UpLo_>
933template <
typename InputType>
935 eigen_assert(a.rows() == a.cols());
936 m_matrix = a.derived();
937 return computeInPlace();
941template <
typename MatrixType,
int UpLo_>
943 eigen_assert(m_matrix.rows() == m_matrix.cols());
944 const Index size = m_matrix.rows();
947 m_l1_norm = m_matrix.template selfadjointView<UpLo_>().l1Norm();
949 m_transpositions.resize(size);
950 m_subdiag.resize(size);
951 m_isInitialized =
false;
953 Index info = internal::bunch_kaufman<UpLo>::blocked(m_matrix, m_transpositions, m_subdiag, m_workspace);
959 EIGEN_IF_CONSTEXPR (
int(UpLo) ==
int(
Upper) && NumTraits<Scalar>::IsComplex) {
960 m_subdiag = m_subdiag.conjugate();
965 m_isInitialized =
true;
969#ifndef EIGEN_PARSED_BY_DOXYGEN
970template <
typename MatrixType_,
int UpLo_>
971template <
typename RhsType,
typename DstType>
973 _solve_impl_transposed<true>(rhs, dst);
976template <
typename MatrixType_,
int UpLo_>
977template <
bool Conjugate,
typename RhsType,
typename DstType>
980 dst = m_transpositions * rhs;
981 matrixL().template conjugateIf<!Conjugate>().solveInPlace(dst);
982 solveInPlaceD<Conjugate>(dst);
983 matrixL().transpose().template conjugateIf<Conjugate>().solveInPlace(dst);
984 dst = m_transpositions.transpose() * dst;
994template <
typename MatrixType,
int UpLo_>
995template <
typename Derived>
997 eigen_assert(m_isInitialized &&
"BunchKaufman is not initialized.");
998 eigen_assert(m_matrix.rows() == bAndX.rows());
999 bAndX = this->solve(bAndX);
1003template <
typename MatrixType,
int UpLo_>
1005 const Index n = m_matrix.rows();
1009 RealScalar mantissa(1);
1013 RealScalar blockM, blockE;
1014 if (k + 1 < n && !numext::is_exactly_zero(m_subdiag.coeff(k))) {
1015 const RealScalar d11 = numext::real(m_matrix.coeff(k, k));
1016 const RealScalar d22 = numext::real(m_matrix.coeff(k + 1, k + 1));
1017 const RealScalar d21 = numext::abs(m_subdiag.coeff(k));
1019 const RealScalar m21 = internal::pfrexp<RealScalar>(d21, e21);
1020 blockM = m21 * m21 * scaledBlockDeterminant(d11, d22, d21);
1021 blockE = RealScalar(2) * e21;
1024 blockM = internal::pfrexp<RealScalar>(numext::real(m_matrix.coeff(k, k)), blockE);
1030 mantissa = internal::pfrexp<RealScalar>(mantissa * blockM, renorm);
1031 exponent += Index(blockE) + Index(renorm);
1035 const Index limit = Index(NumTraits<RealScalar>::max_exponent()) - Index(NumTraits<RealScalar>::min_exponent()) +
1036 Index(NumTraits<RealScalar>::digits());
1037 return numext::ldexp(mantissa,
int(numext::mini(numext::maxi(exponent, -limit), limit)));
1042template <
typename MatrixType,
int UpLo_>
1044 eigen_assert(m_isInitialized &&
"BunchKaufman is not initialized.");
1045 return Scalar(determinantD());
1048template <
typename MatrixType,
int UpLo_>
1050 eigen_assert(m_isInitialized &&
"BunchKaufman is not initialized.");
1051 return numext::abs(determinantD());
1054template <
typename MatrixType,
int UpLo_>
1056 eigen_assert(m_isInitialized &&
"BunchKaufman is not initialized.");
1057 const Index n = m_matrix.rows();
1058 RealScalar result(0);
1061 if (k + 1 < n && !numext::is_exactly_zero(m_subdiag.coeff(k))) {
1063 const RealScalar d11 = numext::real(m_matrix.coeff(k, k));
1064 const RealScalar d22 = numext::real(m_matrix.coeff(k + 1, k + 1));
1065 const RealScalar d21 = numext::abs(m_subdiag.coeff(k));
1066 const RealScalar scaled = scaledBlockDeterminant(d11, d22, d21);
1067 result += RealScalar(2) * numext::log(d21) + numext::log(numext::abs(scaled));
1070 result += numext::log(numext::abs(numext::real(m_matrix.coeff(k, k))));
1077template <
typename MatrixType,
int UpLo_>
1079 eigen_assert(m_isInitialized &&
"BunchKaufman is not initialized.");
1080 if (m_n_zero > 0)
return Scalar(0);
1081 return Scalar((m_n_neg % 2 == 0) ? 1 : -1);
1086template <
typename MatrixType,
int UpLo_>
1088 eigen_assert(m_isInitialized &&
"BunchKaufman is not initialized.");
1089 const Index
size = m_matrix.rows();
1106template <
typename MatrixType,
unsigned int UpLo>
1116template <
typename Derived>