Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
Eigen::KroneckerOperator< LhsMatrix, RhsMatrix > Class Template Reference

#include <contrib/Eigen/src/StructuredMatrices/KroneckerOperator.h>

Detailed Description

template<typename LhsMatrix, typename RhsMatrix>
class Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >

The Kronecker product \( A \otimes B \) as an implicit operator that is never materialized.

For A of size m1 x n1 and B of size m2 x n2, the Kronecker product is the m1*m2 x n1*n2 block matrix whose block (i,j) is A(i,j)*B.

Throughout, \( \mathrm{vec}(X) \) stacks the columns of X, whatever the storage order of the operands and independently of EIGEN_DEFAULT_TO_ROW_MAJOR: for X of size n2 x n1, entry j*n2+i of \( \mathrm{vec}(X) \) is X(i,j), and its inverse \( \mathrm{mat}(x) \) is x.reshaped(n2,n1). Under this convention the product is \( (A \otimes B)\,\mathrm{vec}(X) = \mathrm{vec}(B X A^T) \); with row-major stacking \( \mathrm{vec}_r \) of Z of size n1 x n2 the same product reads \( (A \otimes B)\,\mathrm{vec}_r(Z) = \mathrm{vec}_r(A Z B^T) \).

This class stores only the two factors and evaluates every operation through them:

  • the product uses the vec identity, costing O(m2 n2 n1 + m2 n1 m1) per right-hand side instead of the O(m1 m2 n1 n2) of a materialized product; right-hand sides are applied in cache-sized batches, one product with each factor per batch;
  • linear solves and inverse factor through decompositions of A and B ( \( (A \otimes B)^{-1} = A^{-1} \otimes B^{-1} \)); minimum-norm least-squares solves ( \( (A \otimes B)^+ = A^+ \otimes B^+ \)) use one complete orthogonal decomposition per factor, deciding each factor's rank on its own; rank goes through the factor SVDs, thresholding the pairwise singular-value products \( \sigma_i(A)\,\sigma_j(B) \) – the singular values of the Kronecker product – at the product level;
  • the eigendecomposition and the (thin) SVD are Kronecker products of the factor decompositions: the eigenvector and singular-vector matrices are returned as KroneckerOperator objects themselves, never materialized;
  • determinant uses \( \det(A \otimes B) = \det(A)^{n_2}\det(B)^{n_1} \), accumulated in an exponent-balanced form so it neither overflows nor underflows when the result is representable.

The class is closed under transpose, conjugate and adjoint ( \( (A \otimes B)^T = A^T \otimes B^T \)). operator* returns an Eigen product expression, so the operator plugs into the matrix-free iterative solvers, and it can be assigned to a dense matrix when an explicit representation is needed. As with any matrix-free operator, the iterative solvers must be instantiated with IdentityPreconditioner (e.g. ConjugateGradient<KroneckerOperator<MatrixXd,MatrixXd>,Lower|Upper,IdentityPreconditioner>): the default preconditioners read individual coefficients through col() or InnerIterator, which the structured operators do not expose.

In contrast to kroneckerProduct() (the KroneckerProduct module), which builds an expression meant to be evaluated into a dense matrix, this class is an operator meant to be applied and solved with, without ever forming the product.

Either factor may be a DiagonalMatrix. A diagonal factor is stored as its diagonal – O(n) instead of O(n^2) – its side of every product is a diagonal scaling instead of a GEMM, solve divides entrywise instead of factorizing, and transpose, conjugate, adjoint, inverse and determinant never leave diagonal form.

Either factor may also be an identity, passed as an Identity() expression (MatrixXd::Identity(p, p)): makeKroneckerOperator() stores it as an internal identity factor holding only its dimensions (assignable, unlike the expression), its side of a product is skipped, and its solves are the identity map. This covers the identity-Kronecker operators \( I \otimes A \) and \( A \otimes I \) ubiquitous in finite-difference and Sylvester/Lyapunov settings, e.g.

auto K = makeKroneckerOperator(MatrixXd::Identity(p, p), A); // I_p (x) A, y = K * x is one product with A
KroneckerOperator< typename internal::kron_factor_storage< LhsDerived >::type, typename internal::kron_factor_storage< RhsDerived >::type > makeKroneckerOperator(const EigenBase< LhsDerived > &a, const EigenBase< RhsDerived > &b)
Definition KroneckerOperator.h:1494

A rectangular Identity(m, n) is the m x n matrix with ones on the main diagonal. The unit DiagonalMatrix VectorXd::Ones(p).asDiagonal() describes the same operator but is applied as a scaling.

The decomposition family (eigenvalues, eigenvectors, singularValues, matrixU, matrixV, leastSquaresSolve, rank) currently materializes a diagonal or identity factor densely for the factor decomposition.

Either factor may also be a SparseMatrix, stored compressed. Its side of a product is a sparse-dense product – O(nnz) instead of O(m n) per column of the reshaped right-hand side – solve factorizes it once with SparseLU and back-substitutes, transpose, conjugate and adjoint stay sparse, and determinant accumulates the SparseLU pivots in the same balanced form as the dense LU path, scaling small factors up before factorization to keep the elimination out of the subnormal range. Its inverse is dense (one SparseLU solve against the identity), and the decomposition family densifies it like a diagonal factor. A product with non-finite data propagates Inf/NaN as the two factor products do, not as a product with the materialized sparse matrix would. A sparse factor whose SparseLU factorization fails – an exactly zero or non-finite pivot column – solves and inverts to NaN, and has determinant 0 when exactly singular (NaN when non-finite). Assigning the operator to a SparseMatrix materializes the product sparsely, every inner vector reserved to its exact size. For a sparse A of size n with nnz(A) stored entries:

auto K = makeKroneckerOperator(MatrixXd::Identity(p, p), A); // I_p (x) A
VectorXd y = K * x; // O(p nnz(A)), no p n x p n matrix
VectorXd z = K.solve(b); // one SparseLU of A, then p column solves
M = K; // p nnz(A) stored entries, when the matrix itself is needed
Matrix< double, Dynamic, 1 > VectorXd

Finally, either factor may be a KroneckerOperator itself, so products of three or more factors stay implicit: \( A \otimes (B \otimes C) \) applies \( B \otimes C \) through its own vec identity, and solves, the determinant, the transposition family, materialization, eigenvalues, eigenvectors and the SVD (singularValues, matrixU, matrixV, whose vector matrices nest the same way) recurse into the nested factors; rank and leastSquaresSolve materialize a nested factor. makeKroneckerOperator(a, b, c, ...) builds this right-nested form; e.g. the middle term \( I_p \otimes A \otimes I_q \) of a 3-D finite-difference operator is

auto K = makeKroneckerOperator(MatrixXd::Identity(p, p), A, MatrixXd::Identity(q, q));
VectorXd y = K * x; // O(p q nnz(A)), A (x) I_q is never formed either
Template Parameters
LhsMatrixthe type of the left factor A: a dense Matrix, a DiagonalMatrix to exploit diagonal structure, a SparseMatrix to exploit sparsity, an identity factor (what makeKroneckerOperator() stores an Identity() expression as), a KroneckerOperator, or a KroneckerSum.
RhsMatrixthe type of the right factor B, under the same convention; its scalar type must match that of LhsMatrix.
See also
makeKroneckerOperator(), class Circulant, class Toeplitz
+ Inheritance diagram for Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >:

Public Member Functions

KroneckerOperator< typename LhsOps::TransposedFactor, typename RhsOps::TransposedFactor > adjoint () const
 
Scalar coeff (Index row, Index col) const
 
KroneckerOperator conjugate () const
 
Scalar determinant () const
 
ComplexVector eigenvalues () const
 
KroneckerOperator< typename LhsSpectrum::Eigenvectors, typename RhsSpectrum::Eigenvectors > eigenvectors () const
 
KroneckerOperator< typename LhsOps::InverseFactor, typename RhsOps::InverseFactor > inverse () const
 
template<typename LhsDerived, typename RhsDerived>
 KroneckerOperator (const EigenBase< LhsDerived > &a, const EigenBase< RhsDerived > &b)
 
template<typename Rhs>
Matrix< Scalar, ColsAtCompileTime, Rhs::ColsAtCompileTime > leastSquaresSolve (const MatrixBase< Rhs > &b) const
 
const LhsMatrix & lhs () const
 
KroneckerOperator< typename LhsSpectrum::SingularVectors, typename RhsSpectrum::SingularVectors > matrixU () const
 
KroneckerOperator< typename LhsSpectrum::SingularVectors, typename RhsSpectrum::SingularVectors > matrixV () const
 
template<typename Rhs>
Product< KroneckerOperator, Rhs > operator* (const MatrixBase< Rhs > &x) const
 
Index rank () const
 
const RhsMatrix & rhs () const
 
RealVector singularValues () const
 
template<typename Rhs>
Matrix< Scalar, ColsAtCompileTime, Rhs::ColsAtCompileTime > solve (const MatrixBase< Rhs > &b) const
 
KroneckerOperator< typename LhsOps::TransposedFactor, typename RhsOps::TransposedFactor > transpose () const
 

Constructor & Destructor Documentation

◆ KroneckerOperator()

template<typename LhsMatrix, typename RhsMatrix>
template<typename LhsDerived, typename RhsDerived>
Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::KroneckerOperator ( const EigenBase< LhsDerived > & a,
const EigenBase< RhsDerived > & b )
inline

Builds the operator A (x) B from the two factors, evaluated into the operator's factor types: a dense expression into a Matrix, a diagonal one into a DiagonalMatrix (stored as its diagonal), a sparse one into a compressed SparseMatrix, an Identity() expression into an identity factor holding its dimensions; a KroneckerOperator is copied as it is.

Member Function Documentation

◆ adjoint()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator< typename LhsOps::TransposedFactor, typename RhsOps::TransposedFactor > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::adjoint ( ) const
inline
Returns
the adjoint \( A^H \otimes B^H \), itself a Kronecker operator. A diagonal factor stays diagonal (its adjoint is its conjugate).

◆ coeff()

template<typename LhsMatrix, typename RhsMatrix>
Scalar Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::coeff ( Index row,
Index col ) const
inline
Returns
the coefficient at row row and column col.

◆ conjugate()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::conjugate ( ) const
inline
Returns
the conjugate \( \bar A \otimes \bar B \), itself a Kronecker operator.

◆ determinant()

template<typename LhsMatrix, typename RhsMatrix>
Scalar Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::determinant ( ) const
inline
Returns
the determinant \( \det(A)^{n_2} \det(B)^{n_1} \) for square factors A of size n1 and B of size n2. The product is accumulated from the factor LU diagonals (the SparseLU pivots for a sparse factor, the diagonal itself for a diagonal factor, skipping the LU; 1 for an identity; recursively for a nested Kronecker factor, whose own factors must then be square; the LU of the materialized matrix for a KroneckerSum factor) in the balanced form m * 2^e – every factor and the running product are renormalized to unit magnitude with the power of two tracked separately – so the partial products (in particular det(A) and det(B) themselves, which can overflow or underflow on their own) never leave the representable range when the determinant itself is representable.

◆ eigenvalues()

template<typename LhsMatrix, typename RhsMatrix>
ComplexVector Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::eigenvalues ( ) const
inline
Returns
the eigenvalues for square factors, in Kronecker order: entry i*n2 + j is \( \lambda_i(A)\,\mu_j(B) \), matching column i*n2 + j of eigenvectors. The set is not sorted – there is no canonical eigenvalue order, and sorting would break the Kronecker structure of the eigenvector matrix. A nested Kronecker or KroneckerSum factor contributes the eigenvalues of its own factors – the factors of every nested Kronecker product must then be square – with the accuracy KroneckerSum::eigenvalues() describes for a sum.

◆ eigenvectors()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator< typename LhsSpectrum::Eigenvectors, typename RhsSpectrum::Eigenvectors > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::eigenvectors ( ) const
inline
Returns
the matrix of eigenvectors \( V_A \otimes V_B \) for square factors – itself a Kronecker operator, never materialized. Column i*n2 + j is \( v_i(A) \otimes v_j(B) \) and matches eigenvalues()[i*n2 + j]. Assign it to a dense matrix to materialize.

◆ inverse()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator< typename LhsOps::InverseFactor, typename RhsOps::InverseFactor > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::inverse ( ) const
inline
Returns
the inverse \( A^{-1} \otimes B^{-1} \), itself a Kronecker operator, for square invertible factors. A diagonal factor's inverse stays diagonal (entrywise reciprocals); a sparse factor's inverse is a dense matrix, computed by one SparseLU solve against the identity (NaN when the factorization fails).

◆ leastSquaresSolve()

template<typename LhsMatrix, typename RhsMatrix>
template<typename Rhs>
Matrix< Scalar, ColsAtCompileTime, Rhs::ColsAtCompileTime > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::leastSquaresSolve ( const MatrixBase< Rhs > & b) const
inline
Returns
the minimum-norm least-squares solution of (*this) * x = b, from one complete orthogonal decomposition per factor. Since \( (A \otimes B)^+ = A^+ \otimes B^+ \), the solution is \( X = B^+ \mathrm{mat}(b)\,(A^+)^T \), applied as one multi-right-hand-side solve with each factor's decomposition per batch, no SVD needed. Handles rectangular and rank-deficient factors. Supports multiple right-hand sides, applied in cache-sized batches.

The numerical rank is decided per factor, from the diagonal of the pivoted QR inside CompleteOrthogonalDecomposition (so, as for any column-pivoted QR, a near-deficiency of the Kahan type can go undetected). This is backward stable: the decompositions perturb A and B separately, so each product singular value \( \sigma_i(A)\,\sigma_j(B) \) inherits only its factors' relative errors. It therefore need not agree with rank, which thresholds the pairwise singular-value products at the product level: factors that are each full rank can form modes that rank drops but this method inverts.

Each factor is scaled by an exact power of two before its decomposition, so factor magnitudes alone cannot over- or underflow the decompositions or the intermediates; the right-hand side is not rescaled. A non-finite factor solves to NaN.

◆ lhs()

template<typename LhsMatrix, typename RhsMatrix>
const LhsMatrix & Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::lhs ( ) const
inline
Returns
the left factor A.

◆ matrixU()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator< typename LhsSpectrum::SingularVectors, typename RhsSpectrum::SingularVectors > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::matrixU ( ) const
inline
Returns
the left singular vectors \( U_A \otimes U_B \) of the thin SVD, itself a Kronecker operator with orthonormal columns; column i*k_B + j matches singularValues()[i*k_B + j].

◆ matrixV()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator< typename LhsSpectrum::SingularVectors, typename RhsSpectrum::SingularVectors > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::matrixV ( ) const
inline
Returns
the right singular vectors \( V_A \otimes V_B \) of the thin SVD, itself a Kronecker operator with orthonormal columns; column i*k_B + j matches singularValues()[i*k_B + j].

◆ operator*()

template<typename LhsMatrix, typename RhsMatrix>
template<typename Rhs>
Product< KroneckerOperator, Rhs > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::operator* ( const MatrixBase< Rhs > & x) const
inline
Returns
the product expression (*this) * x, evaluated through the vec identity without materializing the Kronecker product. The expression carries the default product tag, so assigning it behaves like any dense product: a temporary resolves aliasing between the destination and x, and .noalias() skips it.

◆ rank()

template<typename LhsMatrix, typename RhsMatrix>
Index Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::rank ( ) const
inline
Returns
the numerical rank: the number of pairwise singular-value products \( \sigma_i(A)\,\sigma_j(B) \) – the singular values of the Kronecker product – that reach the threshold min(rows(),cols()) * epsilon * sigma_max(A) * sigma_max(B) (the SVDBase convention), and that reach the smallest normal number (the SVDBase threshold clamp, so subnormal products count as exact zeros). The relative comparison is made in ratio space, \( (\sigma_i(A)/\sigma_{max}(A))(\sigma_j(B)/\sigma_{max}(B)) \) against min(rows(),cols()) * epsilon, and the clamp in exponent space, so that neither the thresholds nor the products can spuriously under- or overflow. Thresholding the products matters: factors that are each full rank against their own threshold can still form pairwise products that are negligible at the product level, so the rank can be smaller than the product of the factor ranks. This costs two SVDs; leastSquaresSolve decides each factor's rank on its own instead.

◆ rhs()

template<typename LhsMatrix, typename RhsMatrix>
const RhsMatrix & Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::rhs ( ) const
inline
Returns
the right factor B.

◆ singularValues()

template<typename LhsMatrix, typename RhsMatrix>
RealVector Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::singularValues ( ) const
inline
Returns
the singular values of the thin SVD \( A \otimes B = U \Sigma V^H \) in Kronecker order: entry i*k_B + j is \( \sigma_i(A)\,\sigma_j(B) \) with k_A, k_B the factor thin ranks min(rows, cols), matching the columns of matrixU and matrixV. The values are not sorted (sorting would break the Kronecker structure of U and V); for rectangular shapes the full SVD pads this set with min(rows(),cols()) - k_A*k_B structural zeros.

◆ solve()

template<typename LhsMatrix, typename RhsMatrix>
template<typename Rhs>
Matrix< Scalar, ColsAtCompileTime, Rhs::ColsAtCompileTime > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::solve ( const MatrixBase< Rhs > & b) const
inline
Returns
the solution of (*this) * x = b for square factors, obtained from one LU decomposition per dense factor and one SparseLU per sparse factor (a diagonal factor is solved by entrywise division instead): reshaping b column-wise as mat(b) of size n2 x n1, the system reads \( B X A^T = \mathrm{mat}(b) \), so \( X = B^{-1} \mathrm{mat}(b) A^{-T} \). Right-hand sides are solved in cache-sized batches, one solve per factor per batch, at O(n1^3 + n2^3 + nrhs (n1 + n2) n1 n2) total cost for dense factors; a diagonal factor contributes only O(nrhs n1 n2), a sparse one its factorization plus its substitutions. As in any chain of solves, \( B^{-1} \mathrm{mat}(b) \) can overflow (B tiny) or underflow (B huge, A tiny) on extreme factor magnitudes even when the solution is representable.
Warning
Both factors must be invertible, like in PartialPivLU: a singular dense factor substitutes Inf/NaN through the solution, and a sparse factor whose SparseLU factorization fails solves to NaN. Use leastSquaresSolve for rank-deficient or rectangular factors.

◆ transpose()

template<typename LhsMatrix, typename RhsMatrix>
KroneckerOperator< typename LhsOps::TransposedFactor, typename RhsOps::TransposedFactor > Eigen::KroneckerOperator< LhsMatrix, RhsMatrix >::transpose ( ) const
inline
Returns
the transpose \( A^T \otimes B^T \), itself a Kronecker operator. A diagonal factor stays diagonal (it is its own transpose).

The documentation for this class was generated from the following file: