Algorithms for Structured Matrices

For matrices with some special structure, it is possible to derive versions of algorithms which are faster and/or more accurate than the standard algorithms.

Prerequisites

The reader should be familiar with concepts of eigenvalues and eigen vectors, singular values and singular vectors, related perturbation theory, and algorithms.

Competences

The reader should be able to recognise matrices which have rank-revealing decomposition and apply adequate algorithms, and to apply forward stable algorithms to arrowhead and diagonal-plus-rank-one matrices.

Rank revealing decompositions

For more details, see Z. Drmač, Computing Eigenvalues and Singular Values to High Relative Accuracy and J. Demmel et al, Computing the singular value decomposition with high relative accuracy and the references therein.

Let $A\in\mathbb{R}^{m\times n}$ with $\mathop{\mathrm{rank}}(A)=n$ (therefore, $m\geq n$) and $A=U\Sigma V^T$ its thin SVD.

Definitions

Let $A\in\mathbb{R}^{m\times n}$.

The singular values of $A$ are (perfectly) well determined to high relative accuracy if changing any entry $A_{kl}$ to $\theta A_{kl}$, $\theta \neq 0$, causes perturbations in singular values bounded by

$$ \min\{|\theta|,1/|\theta|\}\sigma_j \leq\tilde \sigma_j \leq \max\{|\theta|,1/|\theta|\}\sigma_j,\quad \forall j. $$

The sparsity pattern of $A$, $Struct(A)$, is the set of indices for which $A_{kl}$ is permitted to be non-zero.

The bipartite graph of the sparsity pattern $S$, $\mathcal{G}(S)$, is the graph with vertices partitioned into row vertices $r_1,\ldots,r_m$ and column vertices $c_1,\ldots,c_n$, where $r_k$ and $c_l$ are connected if and only if $(k,l)\in S$.

If $\mathcal{G}(S)$ is acyclic, matrices with sparsity pattern $S$ are biacyclic.

A decomposition $A=XDY^T$ with diagonal matrix $D$ is called a rank revealing decomposition (RRD) if $X$ and $Y$ are full-column rank well-conditioned matrices.

Hilbert matrix is a square matrix $H$ with elements $H_{ij}=\displaystyle\frac{1}{i+j-1}$.

Hankel matrix is a square matrix with constant elements along skew-diagonals.

Cauchy matrix is an $m\times n$ matrix $C$ with elements $C_{ij}=\displaystyle\frac{1}{x_i+y_j}$ with $x_i+y_j\neq 0$ for all $i,j$.

Facts

  1. The singular values of $A$ are perfectly well determined to high relative accuracy if and only if the bipartite graph $\mathcal{G}(S)$ is acyclic (forest of trees). Examples are bidiagonal and arrowhead matrices. Sparsity pattern $S$ of acyclic bipartite graph allows at most $m+n-1$ nonzero entries. A bisection algorithm computes all singular values of biacyclic matrices to high relative accuracy.

  2. An RRD of $A$ can be given or computed to high accuracy by some method. Typical methods are Gaussian elimination with complete pivoting or QR factorization with complete pivoting.

  3. Let $\hat X \hat D \hat Y^T$ be the computed RRD of $A$ satisfying \begin{align*} |D_{jj}-\hat D_{jj}|&\leq O(\varepsilon)|D_{jj}|,\\ \| X-\hat X\|&\leq O(\varepsilon) \|X\|,\\ \| Y-\hat Y\|&\leq O(\varepsilon) \|Y\|. \end{align*} The following algorithm computes the EVD of $A$ with high relative accuracy:

    1. Perform QR factorization with pivoting to get $\hat X\hat D=QRP$, where $P$ is a permutation matrix. Thus $A=QRP\hat Y^T$.
    2. Multiply $W=RP\hat Y^T$ (NOT Strassen's multiplication). Thus $A=QW$ and $W$ is well-scaled from the left.
    3. Compute the SVD of $W^T=V\Sigma^T \bar U^T$ using one-sided Jacobi method. Thus $A=Q\bar U \Sigma V^T$.
    4. Multiply $U=Q\bar U$. Thus $A=U\Sigma V^T$ is the computed SVD of $A$.
  4. Let $R=D'R'$, where $D'$ is such that the rows of $R'$ have unit norms. Then the following error bounds hold: $$ \frac{|\sigma_j-\tilde\sigma_j|}{\sigma_j}\leq O(\varepsilon \kappa(R')\cdot \max\{\kappa(X),\kappa(Y)\})\leq O(\varepsilon n^{3/2}\kappa(X)\cdot \max\{\kappa(X),\kappa(Y)\}). $$

  5. Hilbert matrix is Hankel matrix and Cauchy matrix, it is symmetric positive definite and very ill-conditioned.

  6. Every sumbatrix of a Cauchy matrix is itself a Cauchy matrix.

  7. Determinant of a square Cauchy matrix is $$ \det(C)=\frac{\prod_{1\leq i<j\leq n}(x_j-x_i)(y_j-y_i)} {\prod_{1\leq i,j\leq n} (x_i+y_j)}. $$ It is computed with elementwise high relative accuracy.

  8. Let $A$ be square and nonsingular and let $A=LDR$ be its decomposition with diagonal $D$, lower unit-triangular $L$, and upper unit-triangular $R$. The closed formulas using quotients of minors are (see A. S. Householder, The Theory of Matrices in Numerical Analysis):

\begin{align*} D_{11}&=A_{11}, \\ D_{jj}&=\frac{\det(A_{1:j,1:j})}{\det(A_{1:j-1,1:j-1})}, \quad j=2,\ldots,n, \\ L_{jj}&=1, \\ L_{ij}&=\frac{\det(A_{[1,2,\ldots,j-1,i],[1:j]}\, )} {\det(A_{1:j,1:j})}, \quad j < i, \\ R_{jj}&=1, \\ R_{ji}&=\frac{\det(A_{[1,2,\ldots,j],[1,2, \ldots,j-1,i]}\, )} {\det(A_{1:j,1:j})}, \quad i > j, \end{align*}

Example - Positive definite matrix

Let $A=DA_S D$ be strongly scaled symmetric positive definite matrix. Then Cholesky factorization with complete (diagonal) pivoting is an RRD. Consider the following three step algorithm:

  1. Compute $P^T A P=LL^T$ (Cholesky factorization with complete pivoting).
  2. Compute the $L=\bar U\Sigma V^T$ (one-sided Jacobi, V is not needed).
  3. Set $\Lambda=\Sigma^2$ and $U=P\bar U$. Thus $A=U\Lambda U^T$ is an EVD of $A$.

The Cholesky factorization with pivoting can be implemented very fast with block algorithm (see C. Lucas, LAPack-Style Codes for Level 2 and 3 Pivoted Cholesky Factorizations).

The eigenvalues $\tilde \lambda_j$ computed using the above algorithm satisfy relative error bounds: $$ \frac{|\lambda_j-\tilde\lambda_j|}{\lambda_j} \leq O(n\varepsilon \|A_S\|_2^{-1}). $$


In [1]:
include("./ModuleB.jl")


Out[1]:
Main.ModuleB

In [2]:
using .ModuleB

In [3]:
using LinearAlgebra

In [4]:
n=20
import Random
Random.seed!(421)
B=randn(n,n)
# Scaled matrix
As=Matrix(Symmetric(B'*B))
# Scaling
D=exp.(50*(rand(n).-0.5))
# Parentheses are necessary!
A=[As[i,j]*(D[i]*D[j]) for i=1:n, j=1:n]
issymmetric(A), cond(As), cond(A)


Out[4]:
(true, 1794.7674834322888, 4.7974903675033356e39)

In [5]:
?cholesky;

We will not use the Cholesky factorization with complete pivoting. Instead, we will just sort the diagonal of $A$ in advance, which is sufficient for this example.

Write the function for Cholesky factorization with complete pivoting as an excercise.


In [6]:
?sortperm;

In [7]:
p=sortperm(diag(A), rev=true)
L=cholesky(A[p,p])


Out[7]:
Cholesky{Float64,Array{Float64,2}}
U factor:
20×20 UpperTriangular{Float64,Array{Float64,2}}:
 7.88685e8  -2.1039e7    15062.9  -290.504  …  -3.44776e-11  -4.31924e-12
  ⋅          1.93457e8   20859.7  -111.39      -2.33051e-10  -6.10259e-13
  ⋅           ⋅         647180.0   108.871      5.37826e-11   1.45358e-11
  ⋅           ⋅               ⋅   1115.06       3.92092e-10  -5.36617e-12
  ⋅           ⋅               ⋅       ⋅        -1.01533e-10   1.1563e-11
  ⋅           ⋅               ⋅       ⋅     …  -7.96891e-12  -1.02093e-11
  ⋅           ⋅               ⋅       ⋅        -1.50375e-10  -5.12743e-12
  ⋅           ⋅               ⋅       ⋅        -2.3185e-12   -7.73426e-12
  ⋅           ⋅               ⋅       ⋅        -3.32446e-10  -3.29658e-12
  ⋅           ⋅               ⋅       ⋅        -1.69797e-10   2.50981e-11
  ⋅           ⋅               ⋅       ⋅     …  -1.61957e-10  -3.35054e-12
  ⋅           ⋅               ⋅       ⋅        -1.02456e-10   1.96947e-11
  ⋅           ⋅               ⋅       ⋅         2.31466e-10   1.39367e-11
  ⋅           ⋅               ⋅       ⋅        -5.43022e-10  -1.66566e-11
  ⋅           ⋅               ⋅       ⋅        -1.31544e-10   2.3234e-11
  ⋅           ⋅               ⋅       ⋅     …   3.40047e-10   4.28435e-11
  ⋅           ⋅               ⋅       ⋅         1.73146e-10   1.99322e-13
  ⋅           ⋅               ⋅       ⋅        -1.15019e-10   2.62641e-12
  ⋅           ⋅               ⋅       ⋅         4.34304e-10  -6.22212e-12
  ⋅           ⋅               ⋅       ⋅          ⋅            1.13816e-11

In [8]:
U,σ,V=myJacobiR(Matrix(L.L));

In [9]:
methods(myJacobiR)


Out[9]:
# 1 method for generic function myJacobiR:

In [10]:
λ=σ.^2
U₁=U[invperm(p),:]
λ


Out[10]:
20-element Array{Float64,1}:
      6.224952755235023e17
      3.739734905182646e16
      4.188415095416874e11
      1.2460394812028985e6
 190728.85050142332
   3078.7029371467984
    943.3368212353645
    303.624000994283
      0.03248293939014995
      0.0029098134590819264
      5.868398431419658e-7
      3.735404817578556e-10
      1.6742469514795042e-11
      8.056762319984436e-13
      2.033859058099946e-13
      6.093390841906992e-16
      1.0392699953225118e-17
      6.2444106975902545e-19
      1.8305199074258027e-19
      1.2951329074914234e-22

In [11]:
U'*A[p,p]*U


Out[11]:
20×20 Array{Float64,2}:
   6.22495e17   -0.70847      -0.0931937    …   0.0610662     0.00999545
   1.34018       3.73973e16    0.00288199      -0.101356     -0.00714252
  -0.0937641     0.00245858    4.18842e11       5.46604e-5    9.76219e-6
   0.00286982    0.00673238    2.93179e-8       3.48398e-12  -7.42396e-13
   0.000840361   0.000311774   5.21817e-9      -9.12969e-13   3.20558e-13
  -1.06914e-5   -3.29366e-5    1.58978e-9   …   8.14459e-23   2.8094e-12
   1.25794e-5    4.3568e-5    -2.56598e-9       2.61087e-15  -5.27696e-16
  -3.52879e-6    3.56028e-5    1.01736e-9       1.17891e-15   2.63558e-18
  -1.59249e-7   -6.53107e-7    2.38425e-5      -5.65085e-20   3.63978e-21
   1.73829e-7   -1.57251e-9    2.94553e-8      -5.6944e-22    3.54294e-24
  -5.76505e-9   -3.23178e-9    6.60496e-8   …   8.62764e-24   1.53997e-24
   3.57949e-5    1.80388e-7   -2.56019e-9       2.68843e-24   4.80637e-25
  -0.0727832    -0.00182909   -5.45527e-10     -2.18273e-21  -8.19359e-22
 -21.8359        0.682556      8.98052e-11     -3.99197e-18  -4.80982e-19
  60.0456       12.7945       -3.17079e-11     -2.87859e-17  -1.47947e-18
 -10.0303        3.23745      -9.59354e-10  …  -9.75826e-18  -7.79378e-19
   2.21946      -0.437917     -9.99335e-7       1.40446e-18   1.19253e-19
   0.372882     -0.185034      9.70223e-5       5.50729e-19   4.35884e-20
   0.0610662    -0.101356      5.46604e-5       4.70876e-19   2.16126e-20
   0.00999545   -0.00714252    9.76219e-6       2.16126e-20   1.8817e-21

In [12]:
# Due to large condition number, this is not
# as accurate as expected
C=U₁'*A*U₁


Out[12]:
20×20 Array{Float64,2}:
   6.22495e17   -0.18755      -0.0947112    …   0.0610662     0.00999545
   1.58015       3.73973e16    0.00225244      -0.101356     -0.00714252
  -0.0970856     0.00240602    4.18842e11       5.46604e-5    9.76219e-6
   0.00285716    0.00673102    2.26899e-8       3.48398e-12  -7.42396e-13
   0.000884084   0.000311888   1.68231e-9      -9.12969e-13   3.20558e-13
  -1.07605e-5   -3.29096e-5    9.28433e-10  …   8.72288e-23   2.8094e-12
   1.37333e-5    4.34536e-5   -8.43749e-10      2.61087e-15  -5.27696e-16
  -3.36893e-6    3.55029e-5    9.71202e-10      1.17891e-15   2.63558e-18
  -1.48646e-7   -6.53639e-7    2.38425e-5      -5.65083e-20   3.63978e-21
   1.82761e-7   -2.65804e-10   2.94551e-8      -5.69425e-22   3.54383e-24
  -6.0057e-9    -3.21174e-9    6.60497e-8   …   8.62805e-24   1.53993e-24
   3.57949e-5    1.80388e-7   -2.56019e-9       2.68843e-24   4.80636e-25
  -0.0727832    -0.00182909   -5.45527e-10     -2.18273e-21  -8.19359e-22
 -21.8359        0.682556      8.98052e-11     -3.99197e-18  -4.80982e-19
  60.0456       12.7945       -3.17079e-11     -2.87859e-17  -1.47947e-18
 -10.0303        3.23745      -9.59354e-10  …  -9.75826e-18  -7.79378e-19
   2.21946      -0.437917     -9.99335e-7       1.40446e-18   1.19253e-19
   0.372882     -0.185034      9.70223e-5       5.50729e-19   4.35884e-20
   0.0610662    -0.101356      5.46604e-5       4.70876e-19   2.16126e-20
   0.00999545   -0.00714252    9.76219e-6       2.16126e-20   1.8817e-21

In [13]:
# Orthogonality
norm(U₁'*U₁-I)


Out[13]:
3.330032502108985e-15

In [14]:
Dc=sqrt.(diag(C))
Cs=[C[i,j]/(Dc[i]*Dc[j]) for i=1:n, j=1:n]


Out[14]:
20×20 Array{Float64,2}:
  1.0          -1.22922e-18  -1.85485e-16  …   0.112792      0.292051
  1.03564e-17   1.0           1.79973e-17     -0.763794     -0.851444
 -1.90135e-16   1.92244e-17   1.0              0.123082      0.347734
  3.24415e-15   3.11813e-14   3.14082e-17      4.54838e-6   -1.53319e-5
  2.56577e-15   3.69292e-15   5.95212e-18     -3.04645e-6    1.69209e-5
 -2.458e-16    -3.06703e-15   2.58549e-17  …   2.29099e-15   0.00116722
  5.66727e-16   7.31598e-15  -4.24478e-17      1.23879e-7   -3.96073e-7
 -2.4505e-16    1.0536e-14    8.61225e-17      9.85958e-8    3.48685e-9
 -1.04534e-15  -1.87538e-14   2.04409e-10     -4.56911e-10   4.65556e-10
  4.29421e-15  -2.54806e-17   8.4373e-13      -1.53833e-11   1.51448e-12
 -9.93656e-15  -2.16801e-14   1.33225e-10  …   1.64134e-11   4.63411e-11
  2.34738e-9    4.82635e-11  -2.04682e-10      2.02711e-10   5.73288e-10
 -2.25452e-5   -2.31156e-6   -2.06007e-10     -7.77387e-7   -4.61625e-6
 -0.0308186     0.00393032    1.54521e-10     -0.00647805   -0.0123471
  0.164687      0.143169     -1.0602e-10      -0.0907759    -0.0738032
 -0.392102      0.51634      -4.57201e-8   …  -0.438603     -0.554149
  0.581107     -0.467788     -0.00031898       0.422799      0.567898
  0.353662     -0.716006      0.112184         0.600579      0.751937
  0.112792     -0.763794      0.123082         1.0           0.726069
  0.292051     -0.851444      0.347734         0.726069      1.0

In [15]:
K=U₁*Diagonal(σ)
K'*K


Out[15]:
20×20 Array{Float64,2}:
  6.22495e17    0.469379     -7.84249e-5   …   3.31146e-20   1.44176e-22
  0.469379      3.73973e16    4.68424e-6      -2.24242e-19  -4.20328e-22
 -7.84249e-5    4.68424e-6    4.18842e11       3.61356e-20   1.71664e-22
  4.10619e-9    3.88544e-8   -2.3475e-11       1.33536e-24  -7.5688e-27
  4.98538e-10   7.15053e-10   7.77502e-13     -8.94406e-25   8.35326e-27
 -7.70856e-13  -9.48588e-12   1.38482e-13  …   8.04935e-35   5.76218e-25
  5.66117e-13   6.89736e-12   2.17019e-14      3.63697e-26  -1.95527e-28
 -1.14732e-13   3.23159e-12  -7.56646e-16      2.89467e-26   1.72134e-30
 -3.9013e-17   -6.07388e-16   6.6398e-12      -1.41535e-28   1.94731e-31
  1.18956e-17   3.35668e-19   2.45502e-15     -4.54705e-30   6.02133e-34
 -5.59609e-21  -1.28497e-20   7.81817e-17  …  -1.15451e-36  -1.50231e-37
  8.76843e-19   1.80283e-20  -7.6457e-20      -6.89466e-35  -1.56744e-38
 -3.77462e-16  -3.87013e-17  -3.44906e-21     -1.37984e-35  -1.53721e-38
 -2.48418e-14   3.1681e-15    1.24554e-22     -6.23581e-35   3.63859e-38
  3.43221e-14   2.98376e-14  -2.20954e-23     -1.67284e-36   2.40574e-38
 -3.13816e-16   4.13249e-16  -3.65918e-23  …  -5.16032e-36  -3.44068e-38
  9.06867e-18  -7.30022e-18  -4.97795e-21      1.62085e-36  -7.92831e-39
  3.73464e-19  -7.56095e-19   1.18466e-19     -1.90057e-35  -5.12707e-39
  3.31146e-20  -2.24242e-19   3.61356e-20      1.83052e-19   2.29808e-39
  1.44176e-22  -4.20328e-22   1.71664e-22      2.29808e-39   1.29513e-22

In [16]:
# Explain why is the residual so large.
norm(A*U₁-U₁*Diagonal(λ))


Out[16]:
67.99890719481296

In [17]:
# Relative residual is percfect
norm(A*U₁-U₁*Diagonal(λ))/norm(A)


Out[17]:
1.0903942665642334e-16

In [18]:
[λ sort(eigvals(A),rev=true)]


Out[18]:
20×2 Array{Float64,2}:
      6.22495e17        6.22495e17
      3.73973e16        3.73973e16
      4.18842e11        4.18842e11
      1.24604e6         1.24604e6
 190729.0          190729.0
   3078.7            3078.7
    943.337           943.337
    303.624           303.624
      0.0324829        23.8341
      0.00290981        0.0324829
      5.8684e-7         0.00290981
      3.7354e-10        5.86835e-7
      1.67425e-11       3.70478e-10
      8.05676e-13       6.56641e-11
      2.03386e-13       8.31129e-13
      6.09339e-16       6.7895e-16
      1.03927e-17       1.03439e-18
      6.24441e-19      -4.55219e-13
      1.83052e-19      -2.84635e-5
      1.29513e-22      -1.65989

Example - Hilbert matrix

We need the newest version of the package SpecialMatrices.jl.


In [19]:
# Pkg.checkout("SpecialMatrices")
using SpecialMatrices

In [20]:
varinfo(SpecialMatrices)


Out[20]:
name size summary
Cauchy 40 bytes UnionAll
Circulant 40 bytes UnionAll
Companion 40 bytes UnionAll
Frobenius 40 bytes UnionAll
Hankel 40 bytes UnionAll
Hilbert 40 bytes UnionAll
InverseHilbert 40 bytes UnionAll
Kahan 80 bytes UnionAll
Riemann 40 bytes UnionAll
SpecialMatrices 144.030 KiB Module
Strang 40 bytes UnionAll
Toeplitz 40 bytes UnionAll
Vandermonde 40 bytes UnionAll
embed 0 bytes typeof(embed)

In [21]:
C=Cauchy([1,2,3,4,5],[0,1,2,3,4])


Out[21]:
5×5 Cauchy{Int64}:
 1.0       0.5       0.333333  0.25      0.2
 0.5       0.333333  0.25      0.2       0.166667
 0.333333  0.25      0.2       0.166667  0.142857
 0.25      0.2       0.166667  0.142857  0.125
 0.2       0.166667  0.142857  0.125     0.111111

In [22]:
H=Hilbert(5)


Out[22]:
5×5 Hilbert{Rational{Int64}}:
 1//1  1//2  1//3  1//4  1//5
 1//2  1//3  1//4  1//5  1//6
 1//3  1//4  1//5  1//6  1//7
 1//4  1//5  1//6  1//7  1//8
 1//5  1//6  1//7  1//8  1//9

In [23]:
Hf=Matrix(H)


Out[23]:
5×5 Array{Rational{Int64},2}:
 1//1  1//2  1//3  1//4  1//5
 1//2  1//3  1//4  1//5  1//6
 1//3  1//4  1//5  1//6  1//7
 1//4  1//5  1//6  1//7  1//8
 1//5  1//6  1//7  1//8  1//9

In [24]:
# This is exact
det(Hf)


Out[24]:
1//266716800000

In [25]:
# Exact formula for the determinant of a Cauchy matrix from Fact 7.
import LinearAlgebra.det
function det(C::Cauchy{T}) where T
    n=length(C.x)
    F=triu([(C.x[j]-C.x[i])*(C.y[j]-C.y[i]) for i=1:n, j=1:n],1)
    num=prod(F[findall(!iszero,F)])
    den=prod([(C.x[i]+C.y[j]) for i=1:n, j=1:n])
    if all(isinteger,C.x)&all(isinteger,C.y)
        return num//den
    else
        return num/den
    end
end


Out[25]:
det (generic function with 31 methods)

In [26]:
det(C)


Out[26]:
1//266716800000

We now compute componentwise highly accurate $A=LDL ^T$ factorization of a Hilbert (Cauchy) matrix. Using Rational numbers gives high accuracy.


In [27]:
# Exact LDLT factorization from Fact 8, no pivoting.
function myLDLT(C::Cauchy)
    n=length(C.x)
    T=typeof(C.x[1])
    D=Array{Rational{T}}(undef,n)
    L=Matrix{Rational{T}}(I,n,n)
    δ=[det(Cauchy(C.x[1:j],C.y[1:j])) for j=1:n]
    D[1]=map(Rational{T},C[1,1])
    D[2:n]=δ[2:n]./δ[1:n-1]
    for i=2:n
        for j=1:i-1
            L[i,j]=det(Cauchy( C.x[[1:j-1;i]], C.y[1:j])) / δ[j]
        end
    end
    L,D
end


Out[27]:
myLDLT (generic function with 1 method)

In [28]:
L,D=myLDLT(C)
L


Out[28]:
5×5 Array{Rational{Int64},2}:
 1//1  0//1    0//1  0//1  0//1
 1//2  1//1    0//1  0//1  0//1
 1//3  1//1    1//1  0//1  0//1
 1//4  9//10   3//2  1//1  0//1
 1//5  4//5   12//7  2//1  1//1

In [29]:
D


Out[29]:
5-element Array{Rational{Int64},1}:
 1//1
 1//12
 1//180
 1//2800
 1//44100

In [30]:
L*Diagonal(D)*L' # -Matrix(H)


Out[30]:
5×5 Array{Rational{Int64},2}:
 1//1  1//2  1//3  1//4  1//5
 1//2  1//3  1//4  1//5  1//6
 1//3  1//4  1//5  1//6  1//7
 1//4  1//5  1//6  1//7  1//8
 1//5  1//6  1//7  1//8  1//9

In [31]:
# L*D*L' is an RRD
cond(L)


Out[31]:
11.858245826148897

In [32]:
cond(C)


Out[32]:
476607.25024259434

We now compute the accurate EVD of the Hilbert matrix of order $n=100$. We cannot use the function myLDLT() since the computation of determinant causes overflow and there is no pivoting. Instead, we use Algorithm 3 from J. Demmel, Computing the singular value decomposition with high relative accuracy.


In [33]:
function myGECP(C::Cauchy)
    n=length(C.x)
    G=Matrix(C)
    x=copy(C.x)
    y=copy(C.y)
    pr=collect(1:n)
    pc=collect(1:n)
    # Find the maximal element
    for k=1:n-1
        i,j=Tuple(argmax(abs.(G[k:n,k:n])))
        i+=k-1
        j+=k-1
        if i!=k || j!=k
            G[[i,k],:]=G[[k,i],:]
            G[:, [j,k]]=G[:, [k,j]]
            x[[k,i]]=x[[i,k]]
            y[[k,j]]=y[[j,k]]
            pr[[i,k]]=pr[[k,i]]
            pc[[j,k]]=pc[[k,j]]
        end
        for r=k+1:n
            for s=k+1:n
                G[r,s]=G[r,s]*(x[r]-x[k])*(y[s]-y[k])/
                ((x[k]+y[s])*(x[r]+y[k]))
            end
        end
        G=Matrix(Symmetric(G))
    end
    D=diag(G)
    X=tril(G,-1)*Diagonal(1.0./D)+I
    Y=Diagonal(1.0./D)*triu(G,1)+I
    X,D,Y', pr,pc
end


Out[33]:
myGECP (generic function with 1 method)

In [34]:
# First a smaller test
l=8
C=Cauchy(collect(1:l),collect(0:l-1))


Out[34]:
8×8 Cauchy{Int64}:
 1.0       0.5       0.333333  0.25       …  0.166667   0.142857   0.125
 0.5       0.333333  0.25      0.2           0.142857   0.125      0.111111
 0.333333  0.25      0.2       0.166667      0.125      0.111111   0.1
 0.25      0.2       0.166667  0.142857      0.111111   0.1        0.0909091
 0.2       0.166667  0.142857  0.125         0.1        0.0909091  0.0833333
 0.166667  0.142857  0.125     0.111111   …  0.0909091  0.0833333  0.0769231
 0.142857  0.125     0.111111  0.1           0.0833333  0.0769231  0.0714286
 0.125     0.111111  0.1       0.0909091     0.0769231  0.0714286  0.0666667

In [35]:
X,D,Y,pr,pc=myGECP(C)


Out[35]:
([1.0 0.0 … 0.0 0.0; 0.3333333333333333 1.0 … 0.0 0.0; … ; 0.14285714285714285 0.7142857142857142 … 1.0 0.0; 0.16666666666666666 0.78125 … 0.7129629629629629 1.0], [1.0, 0.08888888888888889, 0.012760416666666665, 0.0023148148148148147, 9.070294784580499e-5, 7.378984651711925e-7, 6.619407626563736e-8, 2.3529734559445584e-10], [1.0 0.0 … 0.0 0.0; 0.3333333333333333 1.0 … 0.0 0.0; … ; 0.14285714285714285 0.7142857142857142 … 1.0 0.0; 0.16666666666666666 0.78125 … 0.7129629629629629 1.0], [1, 3, 8, 2, 5, 4, 7, 6], [1, 3, 8, 2, 5, 4, 7, 6])

In [36]:
norm((X*Diagonal(D)*Y')-Matrix(C)[pr,pc])


Out[36]:
1.1527756336890508e-16

In [37]:
norm(X[invperm(pr),:]*Diagonal(D)*Y[invperm(pc),:]'-Matrix(C))


Out[37]:
1.1527756336890508e-16

In [38]:
# Now the big test.
n=100
H=Hilbert(n)
C=Cauchy(collect(1:n), collect(0:n-1))


Out[38]:
100×100 Cauchy{Int64}:
 1.0        0.5         0.333333    …  0.0102041   0.010101    0.01
 0.5        0.333333    0.25           0.010101    0.01        0.00990099
 0.333333   0.25        0.2            0.01        0.00990099  0.00980392
 0.25       0.2         0.166667       0.00990099  0.00980392  0.00970874
 0.2        0.166667    0.142857       0.00980392  0.00970874  0.00961538
 0.166667   0.142857    0.125       …  0.00970874  0.00961538  0.00952381
 0.142857   0.125       0.111111       0.00961538  0.00952381  0.00943396
 0.125      0.111111    0.1            0.00952381  0.00943396  0.00934579
 0.111111   0.1         0.0909091      0.00943396  0.00934579  0.00925926
 0.1        0.0909091   0.0833333      0.00934579  0.00925926  0.00917431
 0.0909091  0.0833333   0.0769231   …  0.00925926  0.00917431  0.00909091
 0.0833333  0.0769231   0.0714286      0.00917431  0.00909091  0.00900901
 0.0769231  0.0714286   0.0666667      0.00909091  0.00900901  0.00892857
 ⋮                                  ⋱                          
 0.011236   0.0111111   0.010989       0.00537634  0.00534759  0.00531915
 0.0111111  0.010989    0.0108696      0.00534759  0.00531915  0.00529101
 0.010989   0.0108696   0.0107527   …  0.00531915  0.00529101  0.00526316
 0.0108696  0.0107527   0.0106383      0.00529101  0.00526316  0.0052356
 0.0107527  0.0106383   0.0105263      0.00526316  0.0052356   0.00520833
 0.0106383  0.0105263   0.0104167      0.0052356   0.00520833  0.00518135
 0.0105263  0.0104167   0.0103093      0.00520833  0.00518135  0.00515464
 0.0104167  0.0103093   0.0102041   …  0.00518135  0.00515464  0.00512821
 0.0103093  0.0102041   0.010101       0.00515464  0.00512821  0.00510204
 0.0102041  0.010101    0.01           0.00512821  0.00510204  0.00507614
 0.010101   0.01        0.00990099     0.00510204  0.00507614  0.00505051
 0.01       0.00990099  0.00980392     0.00507614  0.00505051  0.00502513

We need a function to compute RRD from myGECP()


In [39]:
function myRRD(C::Cauchy)
    X,D,Y,pr,pc=myGECP(C)
    X[invperm(pr),:], D, Y[invperm(pc),:]
end


Out[39]:
myRRD (generic function with 1 method)

In [40]:
X,D,Y=myRRD(C);

In [41]:
# Check
norm((X*Diagonal(D)*Y')-Matrix(C))


Out[41]:
3.063076649054158e-16

In [42]:
cond(C)


Out[42]:
2.209807011860762e19

In [43]:
# Is this RRD? here X=Y
cond(X), cond(Y)


Out[43]:
(72.24644120521842, 72.24644120521842)

In [44]:
# Algorithm from Fact 3
function myRRDSVD(X,D,Y)
    Q,R,p=qr(X*Diagonal(D),Val(true))
    W=R[:,p]*Y'
    V,σ,U₁=myJacobiR(W')
    U=Q*U₁
    U,σ,V
end


Out[44]:
myRRDSVD (generic function with 1 method)

In [45]:
U,σ,V=myRRDSVD(X,D,Y);

In [46]:
# Check residual and orthogonality
norm(Matrix(C)*V-U*Diagonal(σ)), norm(U'*U-I), norm(V'*V-I)


Out[46]:
(4.344717174330543e-5, 1.5597318971917562e-14, 2.8150313546075273e-5)

In [47]:
# Observe the difference!!
[sort(σ) sort(svdvals(C)) sort(eigvals(Matrix(C)))]


Out[47]:
100×3 Array{Float64,2}:
 5.7797e-151   9.87732e-20  -4.33965e-16
 1.29735e-147  2.43276e-19  -3.88646e-16
 1.44439e-144  3.81207e-19  -3.2957e-16
 1.06342e-141  4.37274e-19  -2.36428e-16
 5.82434e-139  4.9596e-19   -1.52442e-16
 2.5311e-136   6.63107e-19  -1.22891e-16
 9.09071e-134  8.3494e-19   -1.0907e-16
 2.77536e-131  8.97432e-19  -8.46881e-17
 7.35195e-129  1.06175e-18  -6.79789e-17
 1.71656e-126  1.18149e-18  -6.2771e-17
 3.57648e-124  1.26493e-18  -5.90783e-17
 6.71629e-122  1.45426e-18  -5.19094e-17
 1.14619e-119  1.4791e-18   -4.34161e-17
 ⋮                          
 2.41265e-8    2.41265e-8    2.41265e-8
 1.78872e-7    1.78872e-7    1.78872e-7
 1.26617e-6    1.26617e-6    1.26617e-6
 8.53628e-6    8.53628e-6    8.53628e-6
 5.46453e-5    5.46453e-5    5.46453e-5
 0.000330868   0.000330868   0.000330868
 0.00188506    0.00188506    0.00188506
 0.0100318     0.0100318     0.0100318
 0.0492923     0.0492923     0.0492923
 0.218596      0.218596      0.218596
 0.821446      0.821446      0.821446
 2.1827        2.1827        2.1827

In [48]:
# Plot the eigenvalues (singular values) and left singular vectors
using Plots

In [49]:
plot(σ,yscale = :log10)


Out[49]:

In [52]:
# using SparseArrays
# spy(abs.(sparse(U)))

In [50]:
# Better spy
# some options :bluesreds,clim=(-1.0,1.0)
import Plots.spy
spy(A)=heatmap(A, yflip=true, color=:bluesreds, aspectratio=1)


Out[50]:
spy (generic function with 2 methods)

In [51]:
spy(U)


Out[51]:

Definitions

An arrowhead matrix is a real symmetric matrix of order $n$ of the form $A=\begin{bmatrix} D & z \\ z^{T} & \alpha \end{bmatrix}$, where $D=\mathop{\mathrm{diag}}(d_{1},d_{2},\ldots ,d_{n-1})$, $z=\begin{bmatrix} \zeta _{1} & \zeta _{2} & \cdots & \zeta _{n-1} \end{bmatrix}^T$ is a vector, and $\alpha$ is a scalar.

An arrowhead matrix is irreducible if $\zeta _{i}\neq 0$ for all $i$ and $d_{i}\neq d_{j}$ for all $i\neq j$.

A diagonal-plus-rank-one matrix (DPR1 matrix) is a real symmetric matrix of order $n$ of the form $A= D +\rho z z^T$, where $D=\mathop{\mathrm{diag}}(d_{1},d_{2},\ldots ,d_{n})$, $z=\begin{bmatrix} \zeta _{1} & \zeta _{2} & \cdots & \zeta _{n} \end{bmatrix}^T$ is a vector, and $\rho \neq 0$ is a scalar.

A DPR1 matrix is irreducible if $\zeta _{i}\neq 0$ for all $i$ and $d_{i}\neq d_{j}$ for all $i\neq j$.

Facts on arrowhead matrices

Let $A$ be an arrowhead matrix of order $n$ and let $A=U\Lambda U^T$ be its EVD.

  1. If $d_i$ and $\lambda_i$ are nonincreasingy ordered, the Cauchy Interlace Theorem implies $$\lambda _{1}\geq d_{1}\geq \lambda _{2}\geq d_{2}\geq \cdots \geq d_{n-2}\geq\lambda _{n-1}\geq d_{n-1}\geq \lambda _{n}. $$

  2. If $\zeta _{i}=0$ for some $i$, then $d_{i}$ is an eigenvalue whose corresponding eigenvector is the $i$-th unit vector, and we can reduce the size of the problem by deleting the $i$-th row and column of the matrix. If $d_{i}=d_{j}$, then $d_{i}$ is an eigenvalue of $A$ (this follows from the interlacing property) and we can reduce the size of the problem by annihilating $\zeta_j$ with a Givens rotation in the $(i,j)$-plane.

  3. If $A$ is irreducible, the interlacing property holds with strict inequalities.

  4. The eigenvalues of $A$ are the zeros of the Pick function (also, secular equation) $$ f(\lambda )=\alpha -\lambda -\sum_{i=1}^{n-1}\frac{\zeta _{i}^{2}}{% d_{i}-\lambda }=\alpha -\lambda -z^{T}(D-\lambda I)^{-1}z, $$ and the corresponding eigenvectors are $$ U_{:,i}=\frac{x_{i}}{\left\Vert x_{i}\right\Vert _{2}},\quad x_{i}=\begin{bmatrix} \left( D-\lambda _{i}I\right) ^{-1}z \\ -1% \end{bmatrix}, \quad i=1,\ldots ,n. $$

  5. Let $A$ be irreducible and nonsingular. If $d_i\neq 0$ for all $i$, then $A^{-1}$ is a DPR1 matrix $$ A^{-1}=\begin{bmatrix} D^{-1} & \\ & 0 \end{bmatrix} + \rho uu^{T}, $$ where $u=\begin{bmatrix} D^{-1}z \\ -1 \end{bmatrix}$, and $\rho =\displaystyle\frac{1}{\alpha-z^{T}D^{-1}z}$. If $d_i=0$, then $A^{-1}$ is a permuted arrowhead matrix, $$ A^{-1}\equiv \begin{bmatrix} D_{1} & 0 & 0 & z_{1} \\ 0 & 0 & 0 & \zeta _{i} \\ 0 & 0 & D_{2} & z_{2} \\ z_{1}^{T} & \zeta _{i} & z_{2}^{T} & \alpha \end{bmatrix}^{-1} = \begin{bmatrix} D_{1}^{-1} & w_{1} & 0 & 0 \\ w_{1}^{T} & b & w_{2}^{T} & 1/\zeta _{i} \\ 0 & w_{2} & D_{2}^{-1} & 0 \\ 0 & 1/\zeta _{i} & 0 & 0 \end{bmatrix}, $$ where \begin{align*} w_{1}&=-D_{1}^{-1}z_{1}\displaystyle\frac{1}{\zeta _{i}},\\ w_{2}&=-D_{2}^{-1}z_{2}\displaystyle\frac{1}{\zeta _{i}},\\ b&= \displaystyle\frac{1}{\zeta _{i}^{2}}\left(-\alpha +z_{1}^{T}D_{1}^{-1}z_{1}+z_{2}^{T}D_{2}^{-1}z_{2}\right). \end{align*}

  6. The algorithm based on the following approach computes all eigenvalues and all components of the corresponding eigenvectors in a forward stable manner to almost full accuracy in $O(n)$ operations per eigenpair:

    1. Shift the irreducible $A$ to $d_i$ which is closer to $\lambda_i$ (one step of bisection on $f(\lambda)$).
    2. Invert the shifted matrix.
    3. Compute the absolutely largest eigenvalue of the inverted shifted matrix and the corresponding eigenvector.
  7. The algorithm is implemented in the package Arrowhead.jl. In certain cases, $b$ or $\rho$ need to be computed with extended precision. For this, we use the functions from file DoubleDouble.jl, originally from the the package DoubleDouble.jl.


In [57]:
using Arrowhead
a=2.0
b=3.0


Out[57]:
3.0

In [58]:
sqrt(a),sqrt(3.0)


Out[58]:
(1.4142135623730951, 1.7320508075688772)

In [59]:
sqrt(BigFloat(a)),sqrt(BigFloat(3.0))


Out[59]:
(1.414213562373095048801688724209698078569671875376948073176679737990732478462102, 1.732050807568877293527446341505872366942805253810380628055806979451933016908798)

In [60]:
# Doible numbers according to Dekker, 1971
ad=Arrowhead.Double(a);bd=Arrowhead.Double(b)


Out[60]:
Arrowhead.Double{Float64}(3.0, 0.0)

In [61]:
ra=sqrt(ad)


Out[61]:
Arrowhead.Double{Float64}(1.4142135623730951, -9.667293313452912e-17)

In [62]:
rb=sqrt(bd)


Out[62]:
Arrowhead.Double{Float64}(1.7320508075688772, 1.0035084221806903e-16)

In [63]:
BigFloat(rb.hi)+BigFloat(rb.lo)


Out[63]:
1.732050807568877293527446341505873862897052827199890577222072998286178657778578

In [64]:
sqrt(BigFloat(a))*sqrt(BigFloat(3.0))


Out[64]:
2.449489742783178098197284074705891391965947480656670128432692567250960377457299

In [65]:
p=ra*rb


Out[65]:
Arrowhead.Double{Float64}(2.449489742783178, 2.1686165181032463e-16)

In [66]:
BigFloat(p.hi)+BigFloat(p.lo)


Out[66]:
2.449489742783178098197284074705907803819254866955487023606866147165672664698377

Example - Random arrowhead matrix


In [67]:
# pkg> add Arrowhead#master
using Arrowhead

In [68]:
varinfo(Arrowhead)


Out[68]:
name size summary
Arrowhead 264.212 KiB Module
GenHalfArrow 0 bytes typeof(GenHalfArrow)
GenSymArrow 0 bytes typeof(GenSymArrow)
GenSymDPR1 0 bytes typeof(GenSymDPR1)
HalfArrow 40 bytes UnionAll
SymArrow 40 bytes UnionAll
SymDPR1 40 bytes UnionAll
bisect 0 bytes typeof(bisect)
eigen 0 bytes typeof(eigen)
inv 0 bytes typeof(inv)
rootsWDK 0 bytes typeof(rootsWDK)
rootsah 0 bytes typeof(rootsah)
svd 0 bytes typeof(svd)
tdc 0 bytes typeof(tdc)

In [69]:
methods(GenSymArrow)


Out[69]:
# 1 method for generic function GenSymArrow:

In [70]:
n=10
A=GenSymArrow(n,n)


Out[70]:
10×10 SymArrow{Float64}:
 0.907205  0.0       0.0       0.0       …  0.0         0.0       0.665268
 0.0       0.447435  0.0       0.0          0.0         0.0       0.803103
 0.0       0.0       0.51316   0.0          0.0         0.0       0.842735
 0.0       0.0       0.0       0.451461     0.0         0.0       0.915498
 0.0       0.0       0.0       0.0          0.0         0.0       0.959933
 0.0       0.0       0.0       0.0       …  0.0         0.0       0.603399
 0.0       0.0       0.0       0.0          0.0         0.0       0.156622
 0.0       0.0       0.0       0.0          0.00191603  0.0       0.0212465
 0.0       0.0       0.0       0.0          0.0         0.196058  0.625239
 0.665268  0.803103  0.842735  0.915498     0.0212465   0.625239  0.425763

In [71]:
# Elements of the type SymArrow
A.D, A.z, A.a, A.i


Out[71]:
([0.9072049658671713, 0.4474351455891854, 0.5131598884478406, 0.4514611754936739, 0.5817339665165502, 0.4217004660835766, 0.4535220759801628, 0.0019160335761105873, 0.1960580795966944], [0.6652683391054359, 0.8031031078352624, 0.8427349111354892, 0.9154976555238339, 0.9599329519984365, 0.603399360682966, 0.15662248820004332, 0.021246517891142513, 0.6252385379110899], 0.42576310091885805, 10)

In [72]:
(λ,U),info=eigen(A)
norm(A*U-U*Diagonal(λ)), norm(U'*U-I)


Out[72]:
(1.5254143728024154e-15, 7.787532852671366e-16)

In [73]:
# Timings - notice the O(n^2)
@time eigen(GenSymArrow(1000,1000));
@time eigen(GenSymArrow(2000,2000));


  0.076491 seconds (20.06 k allocations: 47.662 MiB, 24.58% gc time)
  0.216757 seconds (42.76 k allocations: 187.438 MiB, 5.85% gc time)

Example - Numerically demanding matrix


In [74]:
A=SymArrow( [ 1e10+1.0/3.0, 4.0, 3.0, 2.0, 1.0 ], 
    [ 1e10 - 1.0/3.0, 1.0, 1.0, 1.0, 1.0 ], 1e10, 6 )


Out[74]:
6×6 SymArrow{Float64}:
 1.0e10  0.0  0.0  0.0  0.0  1.0e10
 0.0     4.0  0.0  0.0  0.0  1.0
 0.0     0.0  3.0  0.0  0.0  1.0
 0.0     0.0  0.0  2.0  0.0  1.0
 0.0     0.0  0.0  0.0  1.0  1.0
 1.0e10  1.0  1.0  1.0  1.0  1.0e10

In [75]:
(λ,U),info=eigen(A)
[sort(λ) sort(eigvals(Matrix(A))) sort(λ)-sort(eigvals(Matrix(A)))]


Out[75]:
6×3 Array{Float64,2}:
 -0.348142  -0.348142  -1.09478e-7
  1.26185    1.26185   -1.64565e-8
  2.22325    2.22325   -1.26997e-8
  3.18832    3.18832   -9.62226e-9
  4.17472    4.17472   -8.77525e-9
  2.0e10     2.0e10    -7.62939e-6

Facts on DPR1 matrices

The properties of DPR1 matrices are very similar to those of arrowhead matrices. Let $A$ be a DPR1 matrix of order $n$ and let $A=U\Lambda U^T$ be its EVD.

  1. If $d_i$ and $\lambda_i$ are nonincreasingy ordered and $\rho>0$, then $$\lambda _{1}\geq d_{1}\geq \lambda _{2}\geq d_{2}\geq \cdots \geq d_{n-2}\geq\lambda _{n-1}\geq d_{n-1}\geq \lambda _{n}\geq d_n. $$ If $A$ is irreducible, the inequalities are strict.

  2. Facts 2 on arrowhead matrices holds.

  3. The eigenvalues of $A$ are the zeros of the secular equation $$ f(\lambda )=1+\rho\sum_{i=1}^{n}\frac{\zeta _{i}^{2}}{d_{i}-\lambda } =1 +\rho z^{T}(D-\lambda I)^{-1}z=0, $$ and the corresponding eigenvectors are $$ U_{:,i}=\frac{x_{i}}{\left\Vert x_{i}\right\Vert _{2}},\quad x_{i}=( D-\lambda _{i}I) ^{-1}z. $$

  4. Let $A$ be irreducible and nonsingular. If $d_i\neq 0$ for all $i$, then $$ A^{-1}=D^{-1} +\gamma uu^{T},\quad u=D^{-1}z, \quad \gamma =-\frac{\rho}{1+\rho z^{T}D^{-1}z}, $$ is also a DPR1 matrix. If $d_i=0$, then $A^{-1}$ is a permuted arrowhead matrix, $$ A^{-1}\equiv \left(\begin{bmatrix} D_{1} & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & D_{2} \end{bmatrix} +\rho \begin{bmatrix} z_{1} \\ \zeta _{i} \\ z_{2} \end{bmatrix} \begin{bmatrix} z_{1}^{T} & \zeta _{i} & z_{2}^{T} \end{bmatrix}\right)^{-1}= \begin{bmatrix} D_{1}^{-1} & w_{1} & 0 \\ w_{1}^{T} & b & w_{2}^{T} \\ 0 & w_{2} & D_{2}^{-1} \end{bmatrix}, $$ where \begin{align*} w_{1}&=-D_{1}^{-1}z_{1}\displaystyle\frac{1}{\zeta _{i}},\\ w_{2}&=-D_{2}^{-1}z_{2}\displaystyle\frac{1}{\zeta _{i}},\\ b &=\displaystyle\frac{1}{\zeta _{i}^{2}}\left( \frac{1}{\rho}+z_{1}^{T}D_{1}^{-1}z_{1}+z_{2}^{T}D_{2}^{-1}z_{2}\right). \end{align*}

  5. The algorithm based on the same approach as above, computes all eigenvalues and all components of the corresponding eigenvectors in a forward stable manner to almost full accuracy in $O(n)$ operations per eigenpair. The algorithm is implemented in the package Arrowhead.jl. In certain cases, $b$ or $\gamma$ need to be computed with extended precision.

Example - Random DPR1 matrix


In [76]:
n=10
A=GenSymDPR1(n)


Out[76]:
10×10 SymDPR1{Float64}:
 0.759058   0.122689   0.230849   …  0.332683   0.354039  0.330674
 0.122689   0.445457   0.111958      0.161345   0.171703  0.160371
 0.230849   0.111958   1.04265       0.303584   0.323072  0.301751
 0.0742093  0.0359903  0.0677184     0.0975908  0.103856  0.0970015
 0.0911901  0.0442257  0.0832141     0.119922   0.12762   0.119198
 0.300777   0.145872   0.274469   …  0.395545   0.420936  0.393156
 0.117327   0.0569017  0.107065      0.154294   0.164199  0.153362
 0.332683   0.161345   0.303584      0.98846    0.465588  0.434861
 0.354039   0.171703   0.323072      0.465588   1.3614    0.462776
 0.330674   0.160371   0.301751      0.434861   0.462776  0.873527

In [77]:
# Elements of the type SymDPR1
A.D, A.u, A.r


Out[77]:
([0.5060823315126595, 0.38595531759167234, 0.8319901614626721, 0.13824008502785756, 0.9091574852406379, 0.5513097674698335, 0.39061868754101114, 0.5509579066061177, 0.8659252075552291, 0.44129248214547223], [0.6635167822279997, 0.3217945181601809, 0.6054812127387672, 0.19463934349206125, 0.23917753369343453, 0.7888918506681526, 0.30773088429633955, 0.8725744065670942, 0.9285885343595512, 0.8673055822442022], 0.5746132127505752)

In [78]:
(λ,U),info=eigen(A)
norm(A*U-U*Diagonal(λ)), norm(U'*U-I)


Out[78]:
(4.862148607940548e-16, 5.397025288610338e-16)

Example - Numerically demanding matrix


In [79]:
A=SymDPR1( [ 10.0/3.0, 2.0+1e-7, 2.0-1e-7, 1.0 ], [ 2.0, 1e-7, 1e-7, 2.0], 1.0 )
A=SymDPR1( [ 1e10, 5.0, 4e-3, 0.0, -4e-3,-5.0 ], [ 1e10, 1.0, 1.0, 1e-7, 1.0,1.0 ], 1.0 )


Out[79]:
6×6 SymDPR1{Float64}:
    1.0e20  1.0e10  1.0e10  1000.0      1.0e10   1.0e10
    1.0e10  6.0     1.0        1.0e-7   1.0      1.0
    1.0e10  1.0     1.004      1.0e-7   1.0      1.0
 1000.0     1.0e-7  1.0e-7     1.0e-14  1.0e-7   1.0e-7
    1.0e10  1.0     1.0        1.0e-7   0.996    1.0
    1.0e10  1.0     1.0        1.0e-7   1.0     -4.0

In [80]:
(λ,U),info=eigen(A)
println([sort(λ) sort(eigvals(Matrix(A)))])
norm(A*U-U*Diagonal(λ)), norm(U'*U-I)


[-4.9999999999 -4.999999999900006; -0.003999999900000001 -5.742148820240562e-14; 9.999999998999997e-25 5.6271169015955564e-14; 0.004000000100000001 0.0040000000999998505; 5.0000000001 5.0000000001; 1.0000000001e20 1.0000000001000002e20]
Out[80]:
(3.038182039544697e-6, 3.1401849173675503e-16)