235 using Scalar =
typename LhsMatrix::Scalar;
237 using StorageIndex = int;
239 static_assert(std::is_same<Scalar, typename RhsMatrix::Scalar>::value,
240 "KroneckerSum requires both factors to have the same scalar type");
241 static_assert((internal::kron_factor_is_dense_matrix<LhsMatrix>::value ||
242 internal::kron_factor_kind<LhsMatrix>() != internal::kKronDenseFactor) &&
243 (internal::kron_factor_is_dense_matrix<RhsMatrix>::value ||
244 internal::kron_factor_kind<RhsMatrix>() != internal::kKronDenseFactor),
245 "KroneckerSum factors must be plain Matrix, DiagonalMatrix or SparseMatrix types, identity factors "
246 "(makeKroneckerSum stores an Identity() expression as one), KroneckerOperators or KroneckerSums "
247 "(owning their storage)");
250 using LhsOps = internal::kron_factor_ops<LhsMatrix>;
251 using RhsOps = internal::kron_factor_ops<RhsMatrix>;
252 using LhsSpectrum = internal::kron_factor_spectrum<LhsMatrix>;
253 using RhsSpectrum = internal::kron_factor_spectrum<RhsMatrix>;
256 using ComplexScalar = std::complex<RealScalar>;
260 static constexpr int RowsAtCompileTime =
261 internal::size_at_compile_time(LhsMatrix::RowsAtCompileTime, RhsMatrix::RowsAtCompileTime);
262 static constexpr int ColsAtCompileTime = RowsAtCompileTime;
263 static constexpr int MaxRowsAtCompileTime = RowsAtCompileTime;
264 static constexpr int MaxColsAtCompileTime = RowsAtCompileTime;
265 static constexpr int SizeAtCompileTime = internal::size_at_compile_time(RowsAtCompileTime, ColsAtCompileTime);
266 static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime;
267 static constexpr bool IsRowMajor =
false;
272 template <
typename LhsDerived,
typename RhsDerived>
274 eigen_assert(m_A.size() > 0 && m_B.size() > 0 &&
"KroneckerSum factors must be non-empty");
275 eigen_assert(m_A.rows() == m_A.cols() && m_B.rows() == m_B.cols() &&
"KroneckerSum factors must be square");
276 LhsOps::prepare(m_A);
277 RhsOps::prepare(m_B);
280 EIGEN_DEVICE_FUNC Index rows()
const {
return m_A.rows() * m_B.rows(); }
281 EIGEN_DEVICE_FUNC Index cols()
const {
return rows(); }
284 const LhsMatrix&
lhs()
const {
return m_A; }
286 const RhsMatrix&
rhs()
const {
return m_B; }
292 eigen_assert(row >= 0 && row < rows() && col >= 0 && col < cols());
294 return internal::kron_factor_ops<KroneckerSum>::coeffIfStored(*
this, row, col, value) ? value : Scalar(0);
299 return {LhsOps::transposed(m_A), RhsOps::transposed(m_B)};
307 return {LhsOps::adjointed(m_A), RhsOps::adjointed(m_B)};
313 template <
typename Rhs>
316 return solver.
solve(b);
334 const ComplexVector lambda = LhsSpectrum::eigenvalues(m_A), mu = RhsSpectrum::eigenvalues(m_B);
335 return (mu.replicate(
fix<1>, lambda.size()) + lambda.transpose().replicate(mu.size(),
fix<1>)).reshaped();
344 return {LhsSpectrum::eigenvectors(m_A), RhsSpectrum::eigenvectors(m_B)};
349 template <
typename Rhs>
351 EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
352 int(ColsAtCompileTime) ==
int(Rhs::RowsAtCompileTime),
353 INVALID_MATRIX_PRODUCT)
354 eigen_assert(x.rows() == cols() &&
"invalid product: dimensions do not match");
362 template <
typename Dest,
typename Rhs,
typename ProductScalar>
363 void addProduct(Dest& dst,
const Rhs&
rhs,
const ProductScalar& alpha)
const {
365 const Index n1 = m_A.rows(), n2 = m_B.rows(), r =
rhs.cols();
366 eigen_assert(
rhs.rows() == n1 * n2 &&
"invalid product: dimensions do not match");
367 typename internal::nested_eval<Rhs, 1>::type actualRhs(
rhs);
368 ProductMatrix X, Y, work;
369 const Index chunk = internal::kron_rhs_chunk<ProductScalar>(2 * n1 * n2);
370 for (Index k0 = 0; k0 < r; k0 += chunk) {
371 const Index c = numext::mini(chunk, r - k0);
373 const auto Xk = actualRhs.col(k0).reshaped(n2, n1);
374 auto Yk = dst.col(k0).reshaped(n2, n1);
375 RhsOps::addLeftProduct(Yk, alpha, m_B, Xk);
376 LhsOps::addRightProduct(Yk, alpha, Xk, m_A, work);
379 internal::kron_stack_columns(X, actualRhs.middleCols(k0, c), n2, n1);
380 Y.setZero(n2 * c, n1);
381 auto Yflat = Y.reshaped(n2, c * n1);
382 RhsOps::addLeftProduct(Yflat, ProductScalar(1), m_B, X.reshaped(n2, c * n1));
383 LhsOps::addRightProduct(Y, ProductScalar(1), X, m_A, work);
384 for (Index k = 0; k < c; ++k) dst.col(k0 + k).reshaped(n2, n1) += alpha * Y.middleRows(k * n2, n2);
391 template <
typename Dest>
392 void evalTo(Dest& dst)
const {
393 evalToImpl(dst, IsSparseDestination<Dest>());
397 template <
typename Dest>
398 void addTo(Dest& dst)
const {
399 addToImpl(dst, Scalar(1), IsSparseDestination<Dest>());
403 template <
typename Dest>
404 void subTo(Dest& dst)
const {
405 addToImpl(dst, Scalar(-1), IsSparseDestination<Dest>());
409 template <
typename Dest>
410 using IsSparseDestination = std::is_same<typename internal::traits<Dest>::StorageKind, Sparse>;
412 template <
typename Dest>
413 void evalToImpl(Dest& dst, std::false_type)
const {
415 addToImpl(dst, Scalar(1), std::false_type());
420 template <
typename Dest>
421 void addToImpl(Dest& dst,
const Scalar& s, std::false_type)
const {
422 const Index n1 = m_A.rows(), n2 = m_B.rows();
423 LhsOps::forEachNonZero(m_A, [&dst, &s, n2](
Index i,
Index j,
const Scalar& a) {
424 dst.block(i * n2, j * n2, n2, n2).diagonal().array() += s * a;
426 const auto& B = RhsOps::blockOperand(m_B);
427 for (
Index k = 0; k < n1; ++k) dst.block(k * n2, k * n2, n2, n2) += s * B;
435 template <
typename Dest>
436 void evalToImpl(Dest& S, std::true_type)
const {
437 using IndexVector = Matrix<Index, Dynamic, 1>;
438 using LhsVisitable = internal::kron_factor_visitable<LhsMatrix>;
439 using RhsVisitable = internal::kron_factor_visitable<RhsMatrix>;
440 using VisitedLhsOps = internal::kron_factor_ops<typename LhsVisitable::type>;
441 using VisitedRhsOps = internal::kron_factor_ops<typename RhsVisitable::type>;
442 const Index n1 = m_A.rows(), n2 = m_B.rows();
443 const auto& A = LhsVisitable::get(m_A);
444 const auto& B = RhsVisitable::get(m_B);
445 const IndexVector nnzA = VisitedLhsOps::innerNonZeros(A, Dest::IsRowMajor);
446 const IndexVector nnzB = VisitedRhsOps::innerNonZeros(B, Dest::IsRowMajor);
447 Dest SA(rows(), cols()), SB(rows(), cols());
448 SA.reserve(IndexVector(nnzA.transpose().replicate(n2,
fix<1>).reshaped()));
449 VisitedLhsOps::forEachNonZero(A, [&SA, n2](
Index i,
Index j,
const Scalar& a) {
450 for (
Index k = 0; k < n2; ++k) SA.insert(i * n2 + k, j * n2 + k) = a;
452 SB.reserve(IndexVector(nnzB.replicate(n1,
fix<1>)));
453 for (
Index k = 0; k < n1; ++k)
454 VisitedRhsOps::forEachNonZero(
455 B, [&SB, n2, k](
Index i,
Index j,
const Scalar& b) { SB.insert(k * n2 + i, k * n2 + j) = b; });
459 template <
typename Dest>
460 void addToImpl(Dest& dst,
const Scalar& s, std::true_type)
const {
461 typename Dest::PlainObject sum;
559 EIGEN_STATIC_ASSERT_NON_INTEGER(RealScalar)
560 using MatrixType = KroneckerSumType;
561 using ComplexScalar = std::complex<RealScalar>;
575 std::vector<DenseMatrix> leaves;
576 collectLeaves(op, leaves);
577 const std::size_t d = leaves.size();
581 m_triangular.clear();
582 m_triangularAdjoint.clear();
584 m_quasiTriangular.clear();
586 bool hermitian =
true;
587 for (std::size_t k = 0; k < d; ++k) {
588 m_sizes[k] = leaves[k].rows();
590 hermitian = hermitian && leaves[k] == leaves[k].adjoint();
593 m_path = Path::Diagonal;
595 m_path = Path::RealSchur;
597 m_path = Path::ComplexSchur;
599 for (std::size_t k = d; k-- > 0;) {
601 m_size *= m_sizes[k];
603 m_isInitialized =
true;
604 if (m_info !=
Success)
return *
this;
606 case Path::Diagonal: {
607 m_spectrum = RealVector::Zero(1);
609 for (std::size_t k = 0; k < d; ++k) {
615 const RealVector previous = m_spectrum;
617 (lambda.replicate(
fix<1>, previous.size()) + previous.transpose().replicate(lambda.size(),
fix<1>))
622 case Path::RealSchur:
625 case Path::ComplexSchur: {
626 ComplexSchur<ComplexMatrix> schur;
627 for (std::size_t k = 0; k < d; ++k) {
628 schur.
compute(leaves[k].
template cast<ComplexScalar>());
630 m_unitary.push_back(schur.
matrixU());
631 m_triangular.push_back(schur.
matrixT());
632 m_triangularAdjoint.emplace_back(m_triangular.back().adjoint());
640 Index rows() const noexcept {
return m_size; }
641 Index cols() const noexcept {
return m_size; }
646 eigen_assert(m_isInitialized &&
"BartelsStewart is not initialized.");
653 eigen_assert(m_isInitialized &&
"BartelsStewart is not initialized.");
654 return m_path == Path::Diagonal;
657#ifdef EIGEN_PARSED_BY_DOXYGEN
661 template <
typename Rhs>
665#ifndef EIGEN_PARSED_BY_DOXYGEN
666 template <
typename RhsType,
typename DstType>
667 void _solve_impl(
const RhsType& rhs, DstType& dst)
const {
668 solveImpl(rhs, dst,
false);
672 template <
bool Conjugate,
typename RhsType,
typename DstType>
673 void _solve_impl_transposed(
const RhsType& rhs, DstType& dst)
const {
675 solveImpl(rhs.template conjugateIf<ConjugateRhs>(), dst,
true);
676 if (ConjugateRhs) dst = dst.conjugate();
683 template <
typename RhsType,
typename DstType>
684 void solveImpl(
const RhsType& rhs, DstType& dst,
bool adjoint)
const {
687 dst.setConstant(Scalar(NumTraits<RealScalar>::quiet_NaN()));
691 case Path::Diagonal: {
693 applyBasis(W, m_basis,
true);
694 W.array().colwise() /= m_spectrum.array();
695 applyBasis(W, m_basis,
false);
699 case Path::RealSchur: {
701 solveRealSchur(W,
adjoint, internal::bool_constant<!NumTraits<Scalar>::IsComplex>());
705 case Path::ComplexSchur: {
706 ComplexMatrix W = rhs.template cast<ComplexScalar>();
707 applyBasis(W, m_unitary,
true);
708 for (
Index j = 0; j < W.cols(); ++j) {
710 adjointTriangularSolve(0, ComplexScalar(0), W.col(j).data());
712 triangularSolve(0, ComplexScalar(0), W.col(j).data());
714 applyBasis(W, m_unitary,
false);
715 dst = internal::structured_scalar_part_impl<Scalar>::run(W);
721 template <
typename Factor>
722 static void collectLeaves(
const Factor& f, std::vector<DenseMatrix>& leaves) {
723 collectLeaves(f, leaves, internal::kron_factor_is_kronecker_sum<Factor>());
725 template <
typename Factor>
726 static void collectLeaves(
const Factor& f, std::vector<DenseMatrix>& leaves, std::true_type) {
727 collectLeaves(f.lhs(), leaves);
728 collectLeaves(f.rhs(), leaves);
730 template <
typename Factor>
731 static void collectLeaves(
const Factor& f, std::vector<DenseMatrix>& leaves, std::false_type) {
732 leaves.push_back(DenseMatrix(internal::kron_factor_ops<Factor>::denseFactor(f)));
740 template <
typename Work,
typename Basis>
741 void applyBasis(Work& W,
const std::vector<Basis>& Q,
bool adjoint)
const {
742 using WorkMatrix = Matrix<typename Work::Scalar, Dynamic, Dynamic, ColMajor>;
744 for (std::size_t k = 0; k < Q.size(); ++k) {
745 const Index n = m_sizes[k], s = m_inner[k], blocks = W.size() / (n * s);
747 Map<WorkMatrix> U(W.data(), n, blocks);
749 T.noalias() = Q[k].adjoint() * U;
751 T.noalias() = Q[k] * U;
755 for (
Index b = 0; b < blocks; ++b) {
756 Map<WorkMatrix> V(W.data() + b * n * s, s, n);
758 T.noalias() = V * Q[k].conjugate();
760 T.noalias() = V * Q[k].transpose();
770 void triangularSolve(std::size_t k,
const ComplexScalar& sigma, ComplexScalar* y)
const {
771 const Index n = m_sizes[k], s = m_inner[k];
772 const TriangularMatrix& T = m_triangular[k];
773 if (k + 1 == m_sizes.size()) {
775 Map<Matrix<ComplexScalar, 1, Dynamic>> Y(y, n);
776 for (
Index i = n - 1; i >= 0; --i) {
777 const Index tail = n - 1 - i;
778 Y(i) = (Y(i) - Y.tail(tail).cwiseProduct(T.row(i).tail(tail)).sum()) / (sigma + T(i, i));
782 Map<ComplexMatrix> Y(y, s, n);
783 for (
Index i = n - 1; i >= 0; --i) {
784 const Index tail = n - 1 - i;
785 if (tail > 0) Y.col(i).noalias() -= Y.rightCols(tail) * T.row(i).tail(tail).transpose();
786 triangularSolve(k + 1, sigma + T(i, i), Y.col(i).data());
793 void adjointTriangularSolve(std::size_t k,
const ComplexScalar& sigma, ComplexScalar* y)
const {
794 const Index n = m_sizes[k], s = m_inner[k];
795 const TriangularMatrix& L = m_triangularAdjoint[k];
796 if (k + 1 == m_sizes.size()) {
798 Map<Matrix<ComplexScalar, 1, Dynamic>> Y(y, n);
799 for (
Index i = 0; i < n; ++i) Y(i) = (Y(i) - Y.head(i).cwiseProduct(L.row(i).head(i)).sum()) / (sigma + L(i, i));
802 Map<ComplexMatrix> Y(y, s, n);
803 for (
Index i = 0; i < n; ++i) {
804 if (i > 0) Y.col(i).noalias() -= Y.leftCols(i) * L.row(i).head(i).transpose();
805 adjointTriangularSolve(k + 1, sigma + L(i, i), Y.col(i).data());
810 static constexpr int kMaxRealFactors = 4;
811 static constexpr int kMaxCoupling = 1 << kMaxRealFactors;
812 using CouplingMatrix = Matrix<RealScalar, Dynamic, Dynamic, ColMajor, kMaxCoupling, kMaxCoupling>;
813 using CouplingVector = Matrix<RealScalar, Dynamic, 1, ColMajor, kMaxCoupling, 1>;
814 using RealMatrix = Matrix<RealScalar, Dynamic, Dynamic, ColMajor>;
816 void computeRealSchur(
const std::vector<DenseMatrix>& leaves, std::true_type) {
817 RealSchur<DenseMatrix> schur;
818 for (
const DenseMatrix& leaf : leaves) {
821 m_basis.push_back(schur.matrixU());
822 m_quasiTriangular.push_back(schur.matrixT());
825 void computeRealSchur(
const std::vector<DenseMatrix>&, std::false_type) {}
827 void solveRealSchur(DenseMatrix& W,
bool adjoint, std::true_type)
const {
828 applyBasis(W, m_basis,
true);
829 std::vector<RealMatrix> work(m_sizes.size());
830 const CouplingMatrix outer = CouplingMatrix::Zero(1, 1);
831 for (
Index j = 0; j < W.cols(); ++j)
832 quasiTriangularSolve(0, outer, Map<RealMatrix>(W.col(j).data(), W.rows(), 1), work,
adjoint);
833 applyBasis(W, m_basis,
false);
835 void solveRealSchur(DenseMatrix&,
bool, std::false_type)
const {}
843 void quasiTriangularSolve(std::size_t k,
const CouplingMatrix& Sigma, Map<RealMatrix> X,
844 std::vector<RealMatrix>& work,
bool adjoint)
const {
845 const Index n = m_sizes[k], s = m_inner[k], m = Sigma.rows();
846 const QuasiTriangularMatrix& T = m_quasiTriangular[k];
847 const bool last = k + 1 == m_sizes.size();
848 for (
Index done = 0; done < n;) {
853 p = i + 1 < n && T(i + 1, i) != RealScalar(0) ? 2 : 1;
855 const Index end = n - done;
856 p = end > 1 && T(end - 1, end - 2) != RealScalar(0) ? 2 : 1;
859 const Index tail = n - i - p;
861 const auto TII = T.block(i, i, p, p);
862 const auto colHead = T.block(0, i, i, p);
863 const auto rowTail = T.block(i, i + p, p, tail);
864 const CouplingMatrix coupled =
adjoint ? kroneckerSum(Sigma, TII.transpose()) : kroneckerSum(Sigma, TII);
867 auto XI = X.middleRows(i, p);
869 if (i > 0) XI.noalias() -= colHead.transpose().lazyProduct(X.topRows(i));
870 }
else if (tail > 0) {
871 XI.noalias() -= rowTail.lazyProduct(X.bottomRows(tail));
873 solveCoupled(coupled, XI);
875 for (
Index t = 0; t < m; ++t) {
876 auto Xt = X.col(t).reshaped(s, n);
877 auto XtI = Xt.middleCols(i, p);
879 if (i > 0) XtI.noalias() -= Xt.leftCols(i) * colHead;
880 }
else if (tail > 0) {
881 XtI.noalias() -= Xt.rightCols(tail) * rowTail.transpose();
885 quasiTriangularSolve(k + 1, coupled, Map<RealMatrix>(X.col(0).data() + i * s, s, p), work,
adjoint);
888 RealMatrix& Z = work[k];
890 Z.reshaped(p * s, m) = X.middleRows(i * s, p * s);
891 quasiTriangularSolve(k + 1, coupled, Map<RealMatrix>(Z.data(), s, m * p), work,
adjoint);
892 X.middleRows(i * s, p * s) = Z.reshaped(p * s, m);
900 template <
typename Block>
901 static CouplingMatrix kroneckerSum(
const CouplingMatrix& Sigma,
const Block& S) {
902 const Index m = Sigma.rows(), p = S.rows();
903 eigen_internal_assert(m * p <= kMaxCoupling);
904 CouplingMatrix R = CouplingMatrix::Zero(m * p, m * p);
905 for (
Index t = 0; t < m; ++t) {
906 for (
Index u = 0; u < m; ++u) R.block(t * p, u * p, p, p).diagonal().setConstant(Sigma(t, u));
907 R.block(t * p, t * p, p, p) += S;
913 template <
typename Block>
914 static void solveCoupled(
const CouplingMatrix& coupled, Block& XI) {
915 if (coupled.rows() == 1) {
916 XI(0, 0) /= coupled(0, 0);
919 CouplingVector z = XI.reshaped();
920 z = coupled.partialPivLu().solve(z);
924 std::vector<Index> m_sizes, m_inner;
926 std::vector<DenseMatrix> m_basis;
927 RealVector m_spectrum;
931 using TriangularMatrix = Matrix<ComplexScalar, Dynamic, Dynamic, RowMajor>;
932 using QuasiTriangularMatrix = Matrix<RealScalar, Dynamic, Dynamic, RowMajor>;
933 std::vector<ComplexMatrix> m_unitary;
934 std::vector<TriangularMatrix> m_triangular, m_triangularAdjoint;
935 std::vector<QuasiTriangularMatrix> m_quasiTriangular;
936 enum class Path { Diagonal, RealSchur, ComplexSchur };
937 Path m_path = Path::Diagonal;
938 bool m_isInitialized =
false;