hqr2 Subroutine

private subroutine hqr2(Nm, n, Low, Igh, h, Wr, Wi, z, Ierr)

Compute the eigenvalues and eigenvectors of a real upper Hessenberg matrix using QR method.

This subroutine is a translation of the ALGOL procedure HQR2, NUM. MATH. 16, 181-204(1970) by Peters and Wilkinson. HANDBOOK FOR AUTO. COMP., VOL.II-LINEAR ALGEBRA, 372-395(1971).

This subroutine finds the eigenvalues and eigenvectors of a REAL UPPER Hessenberg matrix by the QR method. The eigenvectors of a REAL GENERAL matrix can also be found if ELMHES and ELTRAN or ORTHES and ORTRAN have been used to reduce this general matrix to Hessenberg form and to accumulate the similarity transformations.

On Input

    NM must be set to the row dimension of the two-dimensional
      array parameters, H and Z, as declared in the calling
      program dimension statement.  NM is an INTEGER variable.

    N is the order of the matrix H.  N is an INTEGER variable.
      N must be less than or equal to NM.

    LOW and IGH are two INTEGER variables determined by the
      balancing subroutine  BALANC.  If  BALANC  has not been
      used, set LOW=1 and IGH equal to the order of the matrix, N.

    H contains the upper Hessenberg matrix.  H is a two-dimensional
      REAL array, dimensioned H(NM,N).

    Z contains the transformation matrix produced by  ELTRAN
      after the reduction by  ELMHES, or by  ORTRAN  after the
      reduction by  ORTHES, if performed.  If the eigenvectors
      of the Hessenberg matrix are desired, Z must contain the
      identity matrix.  Z is a two-dimensional REAL array,
      dimensioned Z(NM,M).

On Output

    H has been destroyed.

    WR and WI contain the real and imaginary parts, respectively,
      of the eigenvalues.  The eigenvalues are unordered except
      that complex conjugate pairs of values appear consecutively
      with the eigenvalue having the positive imaginary part first.
      If an error exit is made, the eigenvalues should be correct
      for indices IERR+1, IERR+2, ..., N.  WR and WI are one-
      dimensional REAL arrays, dimensioned WR(N) and WI(N).

    Z contains the real and imaginary parts of the eigenvectors.
      If the J-th eigenvalue is real, the J-th column of Z
      contains its eigenvector.  If the J-th eigenvalue is complex
      with positive imaginary part, the J-th and (J+1)-th
      columns of Z contain the real and imaginary parts of its
      eigenvector.  The eigenvectors are unnormalized.  If an
      error exit is made, none of the eigenvectors has been found.

    IERR is an INTEGER flag set to
      Zero       for normal return,
      J          if the J-th eigenvalue has not been
                 determined after a total of 30*N iterations.
                 The eigenvalues should be correct for indices
                 IERR+1, IERR+2, ..., N, but no eigenvectors are
                 computed.

 Calls CDIV for complex division.

Questions and comments should be directed to B. S. Garbow, APPLIED MATHEMATICS DIVISION, ARGONNE NATIONAL LABORATORY

References

  • B. T. Smith, J. M. Boyle, J. J. Dongarra, B. S. Garbow, Y. Ikebe, V. C. Klema and C. B. Moler, Matrix Eigen- system Routines - EISPACK Guide, Springer-Verlag, 1976.

Revision History

  • Smith, B. T., et al.
  • 760101 DATE WRITTEN
  • 890531 Changed all specific intrinsics to generic. (WRB)
  • 890831 Modified array declarations. (WRB)
  • 890831 REVISION DATE from Version 3.2
  • 891214 Prologue converted to Version 4.0 format. (BAB)
  • 920501 Reformatted the REFERENCES section. (WRB)

Arguments

Type IntentOptional Attributes Name
integer :: Nm
integer :: n
integer :: Low
integer :: Igh
real(kind=wp) :: h(Nm,*)
real(kind=wp) :: Wr(*)
real(kind=wp) :: Wi(*)
real(kind=wp) :: z(Nm,*)
integer :: Ierr

Calls

proc~~hqr2~~CallsGraph proc~hqr2 eispack_module::hqr2 proc~cdiv eispack_module::cdiv proc~hqr2->proc~cdiv

Called by

proc~~hqr2~~CalledByGraph proc~hqr2 eispack_module::hqr2 proc~rg eispack_module::rg proc~rg->proc~hqr2 proc~compute_eigenvalues_and_eigenvectors eispack_module::compute_eigenvalues_and_eigenvectors proc~compute_eigenvalues_and_eigenvectors->proc~rg proc~compute_real_eigenvalues_and_normalized_eigenvectors eispack_module::compute_real_eigenvalues_and_normalized_eigenvectors proc~compute_real_eigenvalues_and_normalized_eigenvectors->proc~compute_eigenvalues_and_eigenvectors proc~eispack_test eispack_module::eispack_test proc~eispack_test->proc~compute_eigenvalues_and_eigenvectors

Source Code

 subroutine hqr2(Nm, n, Low, Igh, h, Wr, Wi, z, Ierr)
    implicit none

    integer :: i, j, k, l, m, n, en, ii, jj, ll, mm, na, Nm, &
               nn, gt
    integer :: Igh, itn, its, Low, mp2, enm2, Ierr
    real(wp) :: h(Nm, *), Wr(*), Wi(*), z(Nm, *)
    real(wp) :: p, q, r, s, t, w, x, y, ra, sa, vi, vr, zz, &
                norm, s1, s2
    logical :: notlas

    gt = 0
    Ierr = 0
    norm = 0.0_wp
    k = 1
    ! STORE ROOTS ISOLATED BY BALANC
    ! AND COMPUTE MATRIX NORM
    do i = 1, n
       do j = k, n
          norm = norm + abs(h(i, j))
       enddo
       k = i
       if (i < Low .or. i > Igh) then
          Wr(i) = h(i, i)
          Wi(i) = 0.0_wp
       endif
    enddo

    en = Igh
    t = 0.0_wp
    itn = 30*n
    ! SEARCH FOR NEXT EIGENVALUES
    do while (en >= Low)
       gt = 0
       its = 0
       na = en - 1
       enm2 = na - 1
       ! LOOK FOR SINGLE SMALL SUB-DIAGONAL ELEMENT
       ! FOR L=EN STEP -1 UNTIL LOW DO --
       do while (.true.)
          do ll = Low, en
             l = en + Low - ll
             if (l == Low) exit
             s = abs(h(l - 1, l - 1)) + abs(h(l, l))
             if (s == 0.0_wp) s = norm
             s2 = s + abs(h(l, l - 1))
             if (s2 == s) exit
          enddo
          ! FORM SHIFT
          x = h(en, en)
          if (l == en) then
             ! ONE ROOT FOUND
             h(en, en) = x + t
             Wr(en) = h(en, en)
             Wi(en) = 0.0_wp
             en = na
             gt = 1
             exit
          else
             y = h(na, na)
             w = h(en, na)*h(na, en)
             if (l == na) exit
             if (itn == 0) then
                ! SET ERROR -- NO CONVERGENCE TO AN
                ! EIGENVALUE AFTER 30*N ITERATIONS
                Ierr = en
                return
             else
                if (its == 10 .or. its == 20) then
                   ! FORM EXCEPTIONAL SHIFT
                   t = t + x
                   do i = Low, en
                      h(i, i) = h(i, i) - x
                   enddo
                   s = abs(h(en, na)) + abs(h(na, enm2))
                   x = 0.75_wp*s
                   y = x
                   w = -0.4375_wp*s*s
                endif
                its = its + 1
                itn = itn - 1
                ! LOOK FOR TWO CONSECUTIVE SMALL
                ! SUB-DIAGONAL ELEMENTS.
                ! FOR M=EN-2 STEP -1 UNTIL L DO --
                do mm = l, enm2
                   m = enm2 + l - mm
                   zz = h(m, m)
                   r = x - zz
                   s = y - zz
                   p = (r*s - w)/h(m + 1, m) + h(m, m + 1)
                   q = h(m + 1, m + 1) - zz - r - s
                   r = h(m + 2, m + 1)
                   s = abs(p) + abs(q) + abs(r)
                   p = p/s
                   q = q/s
                   r = r/s
                   if (m == l) exit
                   s1 = abs(p)*(abs(h(m - 1, m - 1)) + abs(zz) + abs(h(m + 1, m + 1)))
                   s2 = s1 + abs(h(m, m - 1))*(abs(q) + abs(r))
                   if (s2 == s1) exit
                enddo
                mp2 = m + 2
                do i = mp2, en
                   h(i, i - 2) = 0.0_wp
                   if (i /= mp2) h(i, i - 3) = 0.0_wp
                enddo
                ! DOUBLE QR STEP INVOLVING ROWS L TO EN AND
                ! COLUMNS M TO EN
                do k = m, na
                   notlas = k /= na
                   if (k /= m) then
                      p = h(k, k - 1)
                      q = h(k + 1, k - 1)
                      r = 0.0_wp
                      if (notlas) r = h(k + 2, k - 1)
                      x = abs(p) + abs(q) + abs(r)
                      if (x == 0.0_wp) cycle
                      p = p/x
                      q = q/x
                      r = r/x
                   endif
                   s = sign(sqrt(p*p + q*q + r*r), p)
                   if (k == m) then
                      if (l /= m) h(k, k - 1) = -h(k, k - 1)
                   else
                      h(k, k - 1) = -s*x
                   endif
                   p = p + s
                   x = p/s
                   y = q/s
                   zz = r/s
                   q = q/p
                   r = r/p
                   ! ROW MODIFICATION
                   do j = k, n
                      p = h(k, j) + q*h(k + 1, j)
                      if (notlas) then
                         p = p + r*h(k + 2, j)
                         h(k + 2, j) = h(k + 2, j) - p*zz
                      endif
                      h(k + 1, j) = h(k + 1, j) - p*y
                      h(k, j) = h(k, j) - p*x
                   enddo
                   j = min(en, k + 3)
                   ! COLUMN MODIFICATION
                   do i = 1, j
                      p = x*h(i, k) + y*h(i, k + 1)
                      if (notlas) then
                         p = p + zz*h(i, k + 2)
                         h(i, k + 2) = h(i, k + 2) - p*r
                      endif
                      h(i, k + 1) = h(i, k + 1) - p*q
                      h(i, k) = h(i, k) - p
                   enddo
                   ! ACCUMULATE TRANSFORMATIONS
                   do i = Low, Igh
                      !
                      p = x*z(i, k) + y*z(i, k + 1)
                      if (notlas) then
                         p = p + zz*z(i, k + 2)
                         z(i, k + 2) = z(i, k + 2) - p*r
                      endif
                      z(i, k + 1) = z(i, k + 1) - p*q
                      z(i, k) = z(i, k) - p
                      !
                   enddo
                enddo
             endif
          endif
       enddo
       if (gt == 1) cycle
       ! TWO ROOTS FOUND
       p = (y - x)/2.0_wp
       q = p*p + w
       zz = sqrt(abs(q))
       h(en, en) = x + t
       x = h(en, en)
       h(na, na) = y + t
       if (q < 0.0_wp) then
          ! COMPLEX PAIR
          Wr(na) = x + p
          Wr(en) = x + p
          Wi(na) = zz
          Wi(en) = -zz
       else
          ! REAL PAIR
          zz = p + sign(zz, p)
          Wr(na) = x + zz
          Wr(en) = Wr(na)
          if (zz /= 0.0_wp) Wr(en) = x - w/zz
          Wi(na) = 0.0_wp
          Wi(en) = 0.0_wp
          x = h(en, na)
          s = abs(x) + abs(zz)
          p = x/s
          q = zz/s
          r = sqrt(p*p + q*q)
          p = p/r
          q = q/r
          ! ROW MODIFICATION
          do j = na, n
             zz = h(na, j)
             h(na, j) = q*zz + p*h(en, j)
             h(en, j) = q*h(en, j) - p*zz
          enddo
          ! COLUMN MODIFICATION
          do i = 1, en
             zz = h(i, na)
             h(i, na) = q*zz + p*h(i, en)
             h(i, en) = q*h(i, en) - p*zz
          enddo
          ! ACCUMULATE TRANSFORMATIONS
          do i = Low, Igh
             zz = z(i, na)
             z(i, na) = q*zz + p*z(i, en)
             z(i, en) = q*z(i, en) - p*zz
          enddo
       endif
       en = enm2
    enddo
    ! ALL ROOTS FOUND.  BACKSUBSTITUTE TO FIND
    ! VECTORS OF UPPER TRIANGULAR FORM
    if (norm /= 0.0_wp) then
       ! FOR EN=N STEP -1 UNTIL 1 DO --
       do nn = 1, n
          en = n + 1 - nn
          p = Wr(en)
          q = Wi(en)
          na = en - 1
          if (q < 0) then
             ! END COMPLEX VECTOR
             ! COMPLEX VECTOR
             m = na
             ! LAST VECTOR COMPONENT CHOSEN IMAGINARY SO THAT
             ! EIGENVECTOR MATRIX IS TRIANGULAR
             if (abs(h(en, na)) <= abs(h(na, en))) then
                call cdiv(0.0_wp, -h(na, en), h(na, na) - p, q, h(na, na), h(na, en))
             else
                h(na, na) = q/h(en, na)
                h(na, en) = -(h(en, en) - p)/h(en, na)
             endif
             h(en, na) = 0.0_wp
             h(en, en) = 1.0_wp
             enm2 = na - 1
             if (enm2 /= 0) then
                ! FOR I=EN-2 STEP -1 UNTIL 1 DO --
                do ii = 1, enm2
                   i = na - ii
                   w = h(i, i) - p
                   ra = 0.0_wp
                   sa = h(i, en)
                   !
                   do j = m, na
                      ra = ra + h(i, j)*h(j, na)
                      sa = sa + h(i, j)*h(j, en)
                   enddo
                   !
                   if (Wi(i) >= 0.0_wp) then
                      m = i
                      if (Wi(i) == 0.0_wp) then
                         call cdiv(-ra, -sa, w, q, h(i, na), h(i, en))
                      else
                         ! SOLVE COMPLEX EQUATIONS
                         x = h(i, i + 1)
                         y = h(i + 1, i)
                         vr = (Wr(i) - p)*(Wr(i) - p) + Wi(i)*Wi(i) - q*q
                         vi = (Wr(i) - p)*2.0_wp*q
                         if (vr == 0.0_wp .and. vi == 0.0_wp) then
                            s1 = norm*(abs(w) + abs(q) + abs(x) + abs(y) + abs(zz))
                            vr = s1
                            do while (.true.)
                               vr = 0.5_wp*vr
                               if (s1 + vr <= s1) exit
                            enddo
                            vr = 2.0_wp*vr
                         endif
                         call cdiv(x*r - zz*ra + q*sa, x*s - zz*sa - q*ra, vr, vi, h(i, na), &
                                   h(i, en))
                         if (abs(x) <= abs(zz) + abs(q)) then
                            call cdiv(-r - y*h(i, na), -s - y*h(i, en), zz, q, h(i + 1, na), &
                                      h(i + 1, en))
                         else
                            h(i + 1, na) = (-ra - w*h(i, na) + q*h(i, en))/x
                            h(i + 1, en) = (-sa - w*h(i, en) - q*h(i, na))/x
                         endif
                      endif
                   else
                      zz = w
                      r = ra
                      s = sa
                   endif
                enddo
             endif
          elseif (q == 0) then
             ! REAL VECTOR
             m = en
             h(en, en) = 1.0_wp
             if (na /= 0) then
                ! FOR I=EN-1 STEP -1 UNTIL 1 DO --
                do ii = 1, na
                   i = en - ii
                   w = h(i, i) - p
                   r = h(i, en)
                   if (m <= na) then
                      do j = m, na
                         r = r + h(i, j)*h(j, en)
                      enddo
                   endif
                   if (Wi(i) >= 0.0_wp) then
                      ! END REAL VECTOR
                      m = i
                      if (Wi(i) == 0.0_wp) then
                         t = w
                         if (t == 0.0_wp) then
                            t = norm
                            do while (.true.)
                               t = 0.5_wp*t
                               if (norm + t <= norm) exit
                            enddo
                            t = 2.0_wp*t
                         endif
                         h(i, en) = -r/t
                      else
                         ! SOLVE REAL EQUATIONS
                         x = h(i, i + 1)
                         y = h(i + 1, i)
                         q = (Wr(i) - p)*(Wr(i) - p) + Wi(i)*Wi(i)
                         t = (x*s - zz*r)/q
                         h(i, en) = t
                         if (abs(x) <= abs(zz)) then
                            h(i + 1, en) = (-s - y*t)/zz
                         else
                            h(i + 1, en) = (-r - w*t)/x
                         endif
                      endif
                   else
                      zz = w
                      s = r
                   endif
                enddo
             endif
          endif
       enddo
       ! END BACK SUBSTITUTION.
       ! VECTORS OF ISOLATED ROOTS
       do i = 1, n
          if (i < Low .or. i > Igh) then
             do j = i, n
                z(i, j) = h(i, j)
             enddo
          endif
       enddo
       ! MULTIPLY BY TRANSFORMATION MATRIX TO GIVE
       ! VECTORS OF ORIGINAL FULL MATRIX.
       ! FOR J=N STEP -1 UNTIL LOW DO --
       do jj = Low, n
          j = n + Low - jj
          m = min(j, Igh)
          do i = Low, Igh
             zz = 0.0_wp
             do k = Low, m
                zz = zz + z(i, k)*h(k, j)
             enddo
             z(i, j) = zz
          enddo
       enddo
    endif
 end subroutine hqr2