Eigenvalues and eigenvectors
#include <flint/acb_mat.h>int acb_mat_approx_eig_qr(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, const mag_t tol, slong maxiter, slong prec)sourceComputes floating-point approximations of the n eigenvalues of the n \times n matrix A (in no particular order) in E, and optionally of the corresponding left eigenvectors in the rows of L and right eigenvectors in the columns of R (either may be NULL), without error bounds. Returns nonzero if the QR iteration converged numerically; this is only a heuristic, and the output may be accurate even if zero is returned.
The matrix is reduced to Hessenberg form by Householder reflections and the eigenvalues are computed by the implicitly shifted QR algorithm, working on the midpoints at prec bits. tol (default 2^{-\mathit{prec}} / (100n) if NULL) is the tolerance for negligible subdiagonal entries, and maxiter (default 14n + \mathit{prec}/10 if 0 or negative) bounds the number of iterations. The eigenvectors are computed from the triangular Schur form by back-substitution and transformed back with the accumulated unitary transformation.
void acb_mat_eig_global_enclosure(mag_t eps, const acb_mat_t A, acb_srcptr E, const acb_mat_t R, slong prec)sourceGiven approximations E of the eigenvalues and R of the right eigenvectors of the n \times n matrix A, computes \varepsilon such that every eigenvalue of A lies within distance \varepsilon of some \hat\lambda_k \in E, by Theorem 1 of [Miy2010], an extension of the Bauer–Fike theorem. If \|YR - I\|_\infty \ge 1 for the approximate inverse Y of R, eps is set to infinity.
The union of the disks \{z : |z - \hat\lambda_k| \le \varepsilon\} encloses the spectrum, but a single disk need not contain exactly one eigenvalue; when the approximations are good, the disks usually correspond to the eigenvalues one to one. The bound is
with D = \operatorname{diag}(E) and Y \approx R^{-1}. It certifies regions of the complex plane (half-planes, disks, annuli) that contain the whole spectrum, cheaply and without assumptions on A.
void acb_mat_eig_enclosure_rump(acb_t lambda, acb_mat_t J, acb_mat_t R, const acb_mat_t A, const acb_t lambda_approx, const acb_mat_t R_approx, slong prec)sourceGiven an approximate eigenvalue lambda_approx of A and an n \times k matrix R_approx of approximate eigenvectors (a basis of the invariant subspace of a presumed cluster of k eigenvalues), computes a ball lambda that contains at least k eigenvalues of A (counted with multiplicity) and an n \times k matrix R that contains a basis of the corresponding invariant subspace. If J is not NULL, it is set to a k \times k matrix J \approx \lambda I such that AR - RJ contains zero; the Jordan form of A then has a k \times k block equal to the Jordan form of a matrix in J. For k = 1, AR - R \lambda contains zero. On failure, the outputs are set to indeterminate values.
The algorithm is that of Section 13.4 of [Rum2010] (the INTLAB function verifyeig): with a preconditioner R_0 \approx ((A - \tilde\lambda I) U U^T - \tilde X V^T)^{-1}, where the rows selected by V (for k = 1 the row of \tilde X of largest absolute value, otherwise the last k rows) are frozen, a fixed point iteration with \varepsilon-inflation is run for at most 5 + b steps, where b is the bit length of prec, until the image of the candidate set lies in its interior, as Rump's theorem requires (each entry is tested with acb_contains_interior). See Eigenvalue enclosures.
int acb_mat_eig_simple_rump(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, slong prec)sourceint acb_mat_eig_simple_vdhoeven_mourrain(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, slong prec)sourceint acb_mat_eig_simple(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, slong prec)sourceAttempt to prove that the n \times n matrix A has n simple eigenvalues, given approximations of the eigenvalues and right eigenvectors. Return 1 on success, writing pairwise disjoint enclosures of the eigenvalues to E (in no particular order), and if L or R is not NULL, enclosures of the corresponding left eigenvectors (rows of L) and right eigenvectors (columns of R), normalised so that L = R^{-1}, which gives a diagonalisation LAR = D. Return 0 otherwise.
The rump version certifies the eigenpairs one by one with acb_mat_eig_enclosure_rump and fails as soon as an enclosure overlaps a previous one; L is obtained by inverting R; the cost is O(n^4). The vdhoeven_mourrain version certifies all eigenpairs at once with the algorithm of [HM2017] at a cost of O(n^3), and is the default. These functions do not try to compute clusters; use acb_mat_eig_multiple for matrices that may have multiple or clustered eigenvalues.
int acb_mat_eig_multiple_rump(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, slong prec)sourceint acb_mat_eig_multiple(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, slong prec)sourceCompute enclosures of all n eigenvalues of A with multiplicities, given approximations of the eigenvalues and right eigenvectors. On success (return value 1), the n entries of E are either disjoint or identical, identical entries are consecutive, and a run of k identical entries is a ball that contains exactly k eigenvalues (a cluster that could not be separated at this precision) and is disjoint from the other n - k eigenvalues. Return 0 otherwise.
The rump version groups the approximate eigenvalues that lie within M 2^{-3\mathit{prec}/4} of each other, where M is the largest approximate eigenvalue (and, in a second attempt, within M 2^{-\mathit{prec}/2}), validates each group with acb_mat_eig_enclosure_rump and checks that the enclosures are disjoint; for m clusters it costs O(m n^3). The default acb_mat_eig_multiple first tries acb_mat_eig_simple_vdhoeven_mourrain and falls back to the rump version.
Computing certified eigenvalues of a complex matrix A in FLINT takes two steps. First, acb_mat_approx_eig_qr computes floating-point approximations of the eigenvalues and eigenvectors by the QR algorithm, without error bounds. Then one of the certification functions turns these approximations into rigorous enclosures:
acb_mat_eig_simpleproves that A has n distinct eigenvalues and returns disjoint enclosures of them, together with enclosures of the left and right eigenvectors if wanted;acb_mat_eig_multipleallows multiple or clustered eigenvalues and returns enclosures with multiplicities;acb_mat_eig_global_enclosurereturns a single radius \varepsilon such that every eigenvalue lies within \varepsilon of one of the approximations, which is the cheapest way to certify statements about the whole spectrum;acb_mat_eig_enclosure_rumpcertifies one eigenvalue (or one cluster) and its eigenvector (or invariant subspace).
The certification functions accept approximations from any source, and their output is valid whatever the quality of the approximations; poor approximations only make them fail or give wide enclosures. For a real matrix, convert it with acb_mat_set_arb_mat first.
Approximation by the QR algorithm#
acb_mat_approx_eig_qr(E, L, R, A, tol, maxiter, prec) works on the midpoint matrix at prec bits: Householder reduction to Hessenberg form, then the implicitly shifted QR iteration until the subdiagonal entries are below tol (by default 2^{-\mathit{prec}}/(100n) relative to their neighbours) or maxiter iterations (by default 14n + \mathit{prec}/10) have been done, and finally back-substitution in the triangular Schur form for the eigenvectors. The return value only says whether the iteration converged numerically. The cost is O(n^3) operations at prec bits.
Certification#
All eigenvalues, simple. acb_mat_eig_simple uses by default the algorithm of van der Hoeven and Mourrain [HM2017]: with T the midpoint of the approximate eigenvector matrix, it computes D = T^{-1} A T in ball arithmetic (by acb_mat_solve), splits it into its diagonal \Delta and off-diagonal part H, and checks with the separation \sigma = \min_{i \ne j} |\delta_i - \delta_j| and \mu = \|\Delta\|_\infty that \|H\|_\infty is small enough compared to \sigma and \mu for a perturbation argument to apply. The eigenvalues are then the diagonal entries of D inflated by \|H\|_\infty, and enclosures of the eigenvectors follow from a correction of T. The cost is O(n^3). The alternative acb_mat_eig_simple_rump certifies the eigenpairs one by one with acb_mat_eig_enclosure_rump at a cost of O(n^4). Both fail (return 0) if two enclosures overlap, which happens for multiple eigenvalues and for eigenvalues closer than the attainable accuracy.
Clusters. acb_mat_eig_multiple first tries acb_mat_eig_simple_vdhoeven_mourrain; if that fails, it groups approximate eigenvalues that agree to about 3/4 (and then 1/2) of the precision into clusters, certifies each cluster of size k with acb_mat_eig_enclosure_rump (a ball containing at least k eigenvalues), and succeeds if the cluster enclosures are pairwise disjoint, since they then contain exactly k eigenvalues each. The output repeats each cluster enclosure k times.
One eigenpair or cluster. acb_mat_eig_enclosure_rump follows Section 13.4 of [Rum2010]: the eigenproblem AX = X\lambda (or AX = XM for a cluster) is written as a fixed point problem for the correction to the approximation, in which k rows of the eigenvector are frozen to normalise it; a preconditioned fixed point iteration with \varepsilon-inflation looks for a set of corrections that is mapped into itself, which proves the existence of an eigenvalue (or of k eigenvalues) in the resulting enclosure. For a cluster, the output matrix J satisfies AR - RJ \ni 0, so that the Jordan structure of the cluster is that of a matrix in J.
The whole spectrum. acb_mat_eig_global_enclosure implements Theorem 1 of Miyajima [Miy2010]: with D the diagonal matrix of approximate eigenvalues, R the approximate eigenvectors and Y \approx R^{-1}, every eigenvalue of A lies within
of a diagonal entry of D, provided that \|YR - I\|_\infty < 1. This generalises the Bauer–Fike theorem to approximate eigenvectors. It needs only a few matrix products, but it cannot tell which disk contains how many eigenvalues.
The algorithms and proofs are in Eigenvalue enclosures.
Practical advice#
- Compute the approximations at the same precision as the certification, or a little higher; the certification cannot produce enclosures much tighter than the approximations are accurate.
- If certification fails, increase the precision. If it keeps failing, the matrix may have multiple eigenvalues (use
acb_mat_eig_multiple) or be defective (non-diagonalisable), in which case only cluster enclosures exist, and their width is about \varepsilon^{1/m} for a Jordan block of size m and an input accuracy \varepsilon. - Badly scaled matrices may need balancing first, and a similarity transformation by a random matrix sometimes helps when an algorithm fails to converge on a well-scaled matrix.
- The left eigenvectors computed by
acb_mat_eig_simpleare normalised so that L = R^{-1}, which gives a certified diagonalisation LAR = D.
Examples#
A 4 \times 4 nonsymmetric matrix with four real eigenvalues. The global enclosure gives a tiny radius for the spectrum as a whole; acb_mat_eig_simple isolates the eigenvalues individually (their imaginary parts are only known to be small: certification in complex arithmetic cannot prove that an eigenvalue is real):
#include <flint/acb.h>
#include <flint/acb_mat.h>
int main(void)
{
acb_mat_t A, L, R;
acb_ptr E, F;
mag_t eps;
slong i, j, n = 4, prec = 64;
acb_mat_init(A, n, n);
acb_mat_init(L, n, n);
acb_mat_init(R, n, n);
E = _acb_vec_init(n);
F = _acb_vec_init(n);
mag_init(eps);
/* the Hilbert matrix with the top right entry replaced by -1 */
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
acb_set_ui(acb_mat_entry(A, i, j), i + j + 1);
acb_inv(acb_mat_entry(A, i, j), acb_mat_entry(A, i, j), prec);
}
acb_set_si(acb_mat_entry(A, 0, n - 1), -1);
acb_mat_approx_eig_qr(E, NULL, R, A, NULL, 0, prec);
acb_mat_eig_global_enclosure(eps, A, E, R, prec);
flint_printf("global enclosure radius: ");
mag_printd(eps, 3);
flint_printf("\n");
if (acb_mat_eig_simple(F, L, R, A, E, R, prec))
{
_acb_vec_sort_pretty(F, n);
for (i = 0; i < n; i++)
{
acb_printn(F + i, 12, 0);
flint_printf("\n");
}
}
acb_mat_clear(A);
acb_mat_clear(L);
acb_mat_clear(R);
_acb_vec_clear(E, n);
_acb_vec_clear(F, n);
mag_clear(eps);
return 0;
}global enclosure radius: 3.24e-18 [-0.0392285082075 +/- 2.44e-14] + [+/- 1.32e-16]*I [-0.00114669579895 +/- 3.57e-15] + [+/- 1.33e-16]*I [0.558734852147 +/- 1.64e-13] + [+/- 1.36e-16]*I [1.15783082805 +/- 1.11e-13] + [+/- 1.39e-16]*I
The matrix A = T \operatorname{diag}(1, 1, 2) T^{-1} below has the double eigenvalue 1. acb_mat_eig_simple fails, and acb_mat_eig_multiple returns the cluster enclosure twice:
#include <flint/fmpq.h>
#include <flint/fmpq_mat.h>
#include <flint/acb.h>
#include <flint/acb_mat.h>
int main(void)
{
fmpq_mat_t T, Tinv, D, Q;
acb_mat_t A, R;
acb_ptr E, F;
slong i, n = 3, prec = 64;
fmpq_mat_init(T, n, n);
fmpq_mat_init(Tinv, n, n);
fmpq_mat_init(D, n, n);
fmpq_mat_init(Q, n, n);
acb_mat_init(A, n, n);
acb_mat_init(R, n, n);
E = _acb_vec_init(n);
F = _acb_vec_init(n);
/* A = T diag(1, 1, 2) T^-1 has the double eigenvalue 1 */
fmpq_set_si(fmpq_mat_entry(T, 0, 0), 1, 1);
fmpq_set_si(fmpq_mat_entry(T, 0, 1), 1, 1);
fmpq_set_si(fmpq_mat_entry(T, 1, 1), 1, 1);
fmpq_set_si(fmpq_mat_entry(T, 1, 2), 1, 1);
fmpq_set_si(fmpq_mat_entry(T, 2, 0), 1, 1);
fmpq_set_si(fmpq_mat_entry(T, 2, 2), 1, 1);
fmpq_set_si(fmpq_mat_entry(D, 0, 0), 1, 1);
fmpq_set_si(fmpq_mat_entry(D, 1, 1), 1, 1);
fmpq_set_si(fmpq_mat_entry(D, 2, 2), 2, 1);
fmpq_mat_inv(Tinv, T);
fmpq_mat_mul(Q, T, D);
fmpq_mat_mul(Q, Q, Tinv);
acb_mat_set_fmpq_mat(A, Q, prec);
acb_mat_approx_eig_qr(E, NULL, R, A, NULL, 0, prec);
flint_printf("simple: %d\n", acb_mat_eig_simple(F, NULL, NULL, A, E, R, prec));
flint_printf("multiple: %d\n", acb_mat_eig_multiple(F, A, E, R, prec));
for (i = 0; i < n; i++)
{
acb_printn(F + i, 10, 0);
flint_printf("\n");
}
fmpq_mat_clear(T);
fmpq_mat_clear(Tinv);
fmpq_mat_clear(D);
fmpq_mat_clear(Q);
acb_mat_clear(A);
acb_mat_clear(R);
_acb_vec_clear(E, n);
_acb_vec_clear(F, n);
return 0;
}simple: 0 multiple: 1 [1.000000000 +/- 4.41e-19] + [+/- 2.17e-19]*I [1.000000000 +/- 4.41e-19] + [+/- 2.17e-19]*I [2.000000000 +/- 3.26e-18] + [+/- 1.63e-18]*I
Since this matrix is diagonalisable, the cluster enclosure is about as tight as the enclosure of a simple eigenvalue; for a Jordan block, it would be much wider.
References#
- [Miy2010] S. Miyajima, "Fast enclosure for all eigenvalues in generalized eigenvalue problems", Journal of Computational and Applied Mathematics, 233 (2010), 2994-3004, https://doi.org/10.1016/j.cam.2009.11.048
- [Rum2010] S. M. Rump, "Verification methods: Rigorous results using floating-point arithmetic", Acta Numerica 19 (2010), 287-449.
- [HM2017] J. van der Hoeven and B. Mourrain. "Efficient certification of numeric solutions to eigenproblems", MACIS 2017, 81-94, (2017), https://hal.archives-ouvertes.fr/hal-01579079
The FLINT team, “Eigenvalues and eigenvectors (acb_mat.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/acb_mat/eigenvalues.html (generated 2026-10-20).
@misc{flint-doc-acb-mat-eigenvalues,
author = {{The FLINT team}},
title = {{Eigenvalues and eigenvectors (acb\_mat.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/acb_mat/eigenvalues.html},
note = {Generated 2026-10-20}
}