Generalized eigenvalue problems with eigencore
Source:vignettes/generalized-eigenproblems.Rmd
generalized-eigenproblems.RmdA generalized eigenvalue problem asks for nonzero vectors and scalars satisfying
The second matrix may define a positive-definite metric, or it may be part of a more general matrix pencil. eigencore supports both cases, along with partial solves when you need only a few eigenpairs and QZ decompositions when you need the full structure of a dense pencil.
Start with a positive-definite metric
When A is symmetric (or Hermitian) and B is
symmetric positive definite, pass B directly to
eig_full(). The returned vectors are normalized in the
metric.
A <- diag(c(2, 8, 18))
B <- diag(c(1, 2, 3))
fit <- eig_full(A, B = B)
values(fit)
#> [1] 2 4 6
certificate(fit)$passed
#> [1] TRUEHere the generalized eigenvalues are 2, 4, and 6: each diagonal entry
of A is divided by the corresponding entry of
B.
Compute only part of the spectrum
Use eig_partial() when you need only a few eigenpairs. A
target such as smallest() states which part of the spectrum
to compute.
part <- eig_partial(A, B = B, k = 2, target = smallest())
values(part)
#> [1] 2 4
part$method
#> [1] "native dense generalized SPD LAPACK fallback"
certificate(part)$passed
#> [1] TRUEThe main choice is the structure of the problem and how much of its spectrum you need:
| Problem | Function |
|---|---|
Symmetric/Hermitian A with positive-definite
B, full spectrum |
eig_full(A, B = B) |
Symmetric/Hermitian A with positive-definite
B, partial spectrum |
eig_partial(A, B = B, k = ...) |
| Dense general pencil | eig_full(A, B = B, structure = general()) |
| Dense generalized Schur decomposition | generalized_schur(A, B) |
Handle a dense general pencil
Use structure = general() when B is
indefinite, singular, nonsymmetric, or when the pair does not satisfy
the symmetric/Hermitian positive-definite contract. This path represents
eigenvalues by homogeneous coordinates
,
where a finite eigenvalue is
.
A_general <- matrix(c(1, 4, 2, 3), 2, 2)
B_general <- matrix(c(2, 1, 0, -1), 2, 2)
pencil <- eig_full(A_general, B = B_general, structure = general())
values(pencil)
#> [1] -0.75+1.391941i -0.75-1.391941i
alpha_beta(pencil)$classification
#> [1] "finite" "finite"
certificate(pencil)$passed
#> [1] TRUEHomogeneous coordinates are especially useful when B is
singular. eigencore classifies finite, infinite, and undefined
eigenvalues instead of forcing every pair into an ordinary numeric
ratio.
singular <- eig_full(
diag(c(2, 3, 0)),
B = diag(c(1, 0, 0)),
structure = general()
)
alpha_beta(singular)$classification
#> [1] "finite" "infinite" "undefined"
certificate(singular)$failed_indices
#> [1] 2 3Use QZ when you need the Schur form
generalized_schur() computes a dense generalized Schur,
or QZ, decomposition. Use values() for the finite ratios
and alpha_beta() when you need the homogeneous coordinates
or finite/infinite classification.
qz <- generalized_schur(A_general, B_general)
values(qz)
#> [1] -0.75+1.391941i -0.75-1.391941i
alpha_beta(qz)$classification
#> [1] "finite" "finite"
qz$method
#> [1] "native dense generalized Schur QZ LAPACK full"For pencils with singular B,
sort = "finite" or sort = "infinite" moves the
requested class to the leading part of the decomposition.
qz_singular <- generalized_schur(
diag(c(2, 3, 0)),
diag(c(1, 0, 0)),
sort = "infinite"
)
alpha_beta(qz_singular)$classification
#> [1] "infinite" "finite" "undefined"Keep sparse partial problems sparse
Sparse symmetric/Hermitian problems with a positive-definite metric
use eig_partial(). Set
allow_dense_fallback = "never" when preserving sparsity is
a hard requirement.
A_sparse <- Diagonal(x = c(1, 4, 9, 16, 25, 36))
B_sparse <- Diagonal(x = c(1, 2, 3, 4, 5, 6))
sparse_fit <- eig_partial(
A_sparse,
B = B_sparse,
k = 3,
target = smallest(),
method = lanczos(max_subspace = 6),
allow_dense_fallback = "never"
)
values(sparse_fit)
#> [1] 1 2 3
sparse_fit$method
#> [1] "native transformed generalized SPD B-orthogonal Lanczos"
certificate(sparse_fit)$passed
#> [1] TRUEFull-spectrum functions accept base dense matrices and reject sparse or operator inputs rather than silently converting them to dense storage.
A note for older code
The retired geigen package exposed related operations,
but eigencore is not a namespace-compatible replacement. When
maintaining older code, translate the mathematical operation: use
eig_full() for a full generalized eigensolve,
generalized_schur() for QZ, and alpha_beta()
to inspect homogeneous eigenvalue coordinates.
For more on result validation, see
vignette("certificates").