Skip to contents

A generalized eigenvalue problem asks for nonzero vectors xx and scalars λ\lambda satisfying

Ax=λBx. A x = \lambda B x.

The second matrix BB 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 BB 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] TRUE

Here 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] TRUE

The 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 (α,β)(\alpha, \beta), where a finite eigenvalue is α/β\alpha / \beta.

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] TRUE

Homogeneous 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 3

Use 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] TRUE

Full-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").