0

I am implementing the Francis double step QR Iteration algorithm using the notes and psuedocode from lecture https://people.inf.ethz.ch/arbenz/ewp/Lnotes/chapter4.pdf - Algorithm 4.5

The psuedocode is provided in Matlab I believe. enter image description here

Below is the implementation of my code.

# compute upper hessenberg form of matrix 
def hessenberg(A):
    m,n = A.shape
    H = A.astype(np.float64)
    for k in range(n-2):
        x = H[k+1:, k]
        v = np.concatenate([np.array([np.sign(x[0]) * np.linalg.norm(x)]), x[1:]])
        v = v / np.linalg.norm(v)
        H[k+1:, k:] -= 2 * np.outer(v, np.dot(v, H[k+1:, k:]))
        H[:, k+1:] -= 2 * np.outer(np.dot(H[:, k+1:], v), v)
    return(H)

# compute first three elements of M
def first_three_M(T,s,t):
    x = T[0, 0]**2 + T[0, 1] * T[1, 0] - s * T[0, 0] + t
    y = T[1, 0] * (T[0, 0] + T[1, 1] - s)
    z = T[1, 0] * T[2, 1]
    return(x,y,z)

# householder reflection
def householder_reflection_step(x_1):
    v = x_1[0] + np.sign(x_1[0]) * np.linalg.norm(x_1)
    v = v / np.linalg.norm(v)
    P = np.eye(3) - 2 * np.outer(v, v)
    return(P)

# update elements of M
def update_M(T,k,p):
    x = T[k+1, k]
    y = T[k+2, k]
    if k < p - 3:
        z = T[k+3, k]
    else:
        z = 0
    return(x,y,z)

# givens rotation
def givens_step(T,x_2,x,y,p,q,n):
    # calculate c and s
    c = x / np.sqrt(x**2 + y**2)
    s = -y / np.sqrt(x**2 + y**2)
    P = np.array([[c, s], [-s, c]])
    T[q-1:p, p-3:n] = P.T @ T[q-1:p, p-3:n]
    T[0:p, p-2:p] = T[0:p, p-2:p] @ P
    return(T)

# deflation step
def deflation_step(T,p,q,epsilon):
    if abs(T[p-1, p-2]) < epsilon * (abs(T[p-2, p-2]) + abs(T[p-1, p-1])):
        T[p-1, p-2] = 0
        p = p - 1
        q = p - 1
    elif abs(T[p-2, p-3]) < epsilon * (abs(T[p-3, p-3]) + abs(T[p-2, p-2])):
        T[p-2, p-3] = 0
        p = p - 2
        q = p - 1
    return(T,p,q)

# francis qr step
def francis_step(H, epsilon=0.90):
    n = H.shape[0]
    T = H.copy().astype(np.float64)
    p = n - 1
    while p > 2:
        q = p - 1
        s = T[q, q] + T[p, p]
        t = T[q, q] * T[p, p] - T[q, p] * T[p, q]
        # Compute M
        x,y,z = first_three_M(T,s,t)
        x_1 = np.transpose([[x], [y], [z]])
        # Bulge chasing
        for k in range(p - 3):
            # Compute Householder reflector
            P = householder_reflection_step(x_1)
            r = max(1, k-1)
            T[k:k+3, r:] = P.T @ T[k:k+3, r:]
            r = min(k + 3, p)
            T[0:r, k:k+3] = T[0:r, k:k+3] @ P
            # Update M
            x,y,z = update_M(T,k,p)
        x_2 = np.transpose([[x], [y]])
        # Compute Givens rotation
        T = givens_step(T,x_2,x,y,p,q,n)
        # Check for convergence
        T,p,q = deflation_step(T,p,q,epsilon)
    return(T)

# francis qr iteration
def francis_qr_iteration(A):
    m,n = A.shape
    H = hessenberg(A)
    eigvals = []
    iters = 0
    max_iters = 100
    while iters<max_iters:
        # Perform Francis step
        T = francis_step(H)
        eigvals.append(np.diag(T))
        iters+=1
    return(eigvals)

# for quick testing
A = np.array([[2, 2, 3, 4, 2], 
               [1, 2, 4, 2, 3], 
               [4, 1, 2, 1, 5], 
               [5, 2, 5, 2, 1], 
               [3, 6, 3, 1, 4]])

eigenvals = francis_qr_iteration(A)

#comparing our method to scipy - final eigvals obtained
print(len(eigenvals))
print(sorted(eigenvals[-1]))
print(sorted(scipy.linalg.eig(A)[0].real)) 

And this is the output I am getting.

100
[-4.421235127393854, -0.909209110641351, -0.8342390091346807, 3.7552499102751575, 8.215454029003958]
[-3.0411228516834217, -1.143605409373778, -1.143605409373778, 3.325396565009845, 14.002937105421134]

The matrix T is not changing and hence it does not converge to the Schur form through which I can obtain the eigenvalues by using np.diag(T). I believe the error is coming either from the Givens rotation step or the Householder reflection step. It could be an indexing issue since I tried to work in python using matlab psuedocode. Please let me know where I am going wrong so I can improve the code and make it converge.

Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • 1
    That pseudo code is not at all MATLAB syntax, it's just [pseudo code](https://en.wikipedia.org/wiki/Pseudocode). MATLAB tag removed – Wolfie Feb 16 '23 at 17:00

0 Answers0