zlahqr function

void zlahqr(
  1. bool WANTT,
  2. bool WANTZ,
  3. int N,
  4. int ILO,
  5. int IHI,
  6. Matrix<Complex> H_,
  7. int LDH,
  8. Array<Complex> W_,
  9. int ILOZ,
  10. int IHIZ,
  11. Matrix<Complex> Z_,
  12. int LDZ,
  13. Box<int> INFO,
)

Implementation

void zlahqr(
  final bool WANTT,
  final bool WANTZ,
  final int N,
  final int ILO,
  final int IHI,
  final Matrix<Complex> H_,
  final int LDH,
  final Array<Complex> W_,
  final int ILOZ,
  final int IHIZ,
  final Matrix<Complex> Z_,
  final int LDZ,
  final Box<int> INFO,
) {
  final H = H_.having(ld: LDH);
  final W = W_.having();
  final Z = Z_.having(ld: LDZ);
  const RZERO = 0.0, HALF = 0.5;
  const DAT1 = 3.0 / 4.0;
  const KEXSH = 10;
  Complex H11, H11S, H22, SC, SUM, T, TEMP, U, V2, X, Y;
  double AA,
      AB,
      BA,
      BB,
      H10,
      H21,
      RTEMP,
      S,
      // SAFMAX,
      SAFMIN,
      SMLNUM,
      SX,
      T2,
      TST,
      ULP;
  int I, I1 = 0, I2 = 0, ITS, ITMAX, J, JHI, JLO, K, L, M, NH, NZ, KDEFL;
  final V = Array<Complex>(2);
  final T1 = Box(Complex.zero);

  INFO.value = 0;

  // Quick return if possible
  if (N == 0) return;
  if (ILO == IHI) {
    W[ILO] = H[ILO][ILO];
    return;
  }

  // clear out the trash
  for (J = ILO; J <= IHI - 3; J++) {
    H[J + 2][J] = Complex.zero;
    H[J + 3][J] = Complex.zero;
  }
  if (ILO <= IHI - 2) H[IHI][IHI - 2] = Complex.zero;
  // ensure that subdiagonal entries are real
  if (WANTT) {
    JLO = 1;
    JHI = N;
  } else {
    JLO = ILO;
    JHI = IHI;
  }
  for (I = ILO + 1; I <= IHI; I++) {
    if (H[I][I - 1].imaginary != RZERO) {
      // The following redundant normalization
      // avoids problems with both gradual and
      // sudden underflow in ABS(H(I,I-1))
      SC = H[I][I - 1] / H[I][I - 1].cabs1().toComplex();
      SC = SC.conjugate() / SC.abs().toComplex();
      H[I][I - 1] = H[I][I - 1].abs().toComplex();
      zscal(JHI - I + 1, SC, H(I, I).asArray(), LDH);
      zscal(min(JHI, I + 1) - JLO + 1, SC.conjugate(), H(JLO, I).asArray(), 1);
      if (WANTZ) {
        zscal(IHIZ - ILOZ + 1, SC.conjugate(), Z(ILOZ, I).asArray(), 1);
      }
    }
  }

  NH = IHI - ILO + 1;
  NZ = IHIZ - ILOZ + 1;

  // Set machine-dependent constants for the stopping criterion.
  SAFMIN = dlamch('SAFE MINIMUM');
  // SAFMAX = RONE / SAFMIN;
  ULP = dlamch('PRECISION');
  SMLNUM = SAFMIN * (NH / ULP);

  // I1 and I2 are the indices of the first row and last column of H
  // to which transformations must be applied. If eigenvalues only are
  // being computed, I1 and I2 are set inside the main loop.
  if (WANTT) {
    I1 = 1;
    I2 = N;
  }

  // ITMAX is the total number of QR iterations allowed.
  ITMAX = 30 * max(10, NH);

  // KDEFL counts the number of iterations since a deflation
  KDEFL = 0;

  // The main loop begins here. I is the loop index and decreases from
  // IHI to ILO in steps of 1. Each iteration of the loop works
  // with the active submatrix in rows and columns L to I.
  // Eigenvalues I+1 to IHI have already converged. Either L = ILO, or
  // H(L,L-1) is negligible so that the matrix splits.
  I = IHI;
  while (true) {
    if (I < ILO) break;

    // Perform QR iterations on rows and columns ILO to I until a
    // submatrix of order 1 splits off at the bottom because a
    // subdiagonal element has become negligible.
    L = ILO;
    var converged = false;
    for (ITS = 0; ITS <= ITMAX; ITS++) {
      // Look for a single small subdiagonal element.
      for (K = I; K >= L + 1; K--) {
        if (H[K][K - 1].cabs1() <= SMLNUM) break;
        TST = H[K - 1][K - 1].cabs1() + H[K][K].cabs1();
        if (TST == RZERO) {
          if (K - 2 >= ILO) TST += (H[K - 1][K - 2].real.abs());
          if (K + 1 <= IHI) TST += (H[K + 1][K].real.abs());
        }
        // The following is a conservative small subdiagonal
        // deflation criterion due to Ahues & Tisseur (LAWN 122,
        // 1997). It has better mathematical foundation and
        // improves accuracy in some examples.
        if (H[K][K - 1].real.abs() <= ULP * TST) {
          AB = max(H[K][K - 1].cabs1(), H[K - 1][K].cabs1());
          BA = min(H[K][K - 1].cabs1(), H[K - 1][K].cabs1());
          AA = max(H[K][K].cabs1(), (H[K - 1][K - 1] - H[K][K]).cabs1());
          BB = min(H[K][K].cabs1(), (H[K - 1][K - 1] - H[K][K]).cabs1());
          S = AA + AB;
          if (BA * (AB / S) <= max(SMLNUM, ULP * (BB * (AA / S)))) break;
        }
      }
      L = K;
      if (L > ILO) {
        // H(L,L-1) is negligible
        H[L][L - 1] = Complex.zero;
      }

      // Exit from loop if a submatrix of order 1 has split off.
      if (L >= I) {
        converged = true;
        break;
      }
      KDEFL++;

      // Now the active submatrix is in rows and columns L to I. If
      // eigenvalues only are being computed, only the active submatrix
      // need be transformed.
      if (!WANTT) {
        I1 = L;
        I2 = I;
      }

      if ((KDEFL % (2 * KEXSH)) == 0) {
        // Exceptional shift.
        S = DAT1 * H[I][I - 1].real.abs();
        T = S.toComplex() + H[I][I];
      } else if ((KDEFL % KEXSH) == 0) {
        // Exceptional shift.
        S = DAT1 * H[L + 1][L].real.abs();
        T = S.toComplex() + H[L][L];
      } else {
        // Wilkinson's shift.
        T = H[I][I];
        U = H[I - 1][I].sqrt() * H[I][I - 1].sqrt();
        S = U.cabs1();
        if (S != RZERO) {
          X = HALF.toComplex() * (H[I - 1][I - 1] - T);
          SX = X.cabs1();
          S = max(S, X.cabs1());
          Y = S.toComplex() *
              ((X / S.toComplex()).pow(2) + (U / S.toComplex()).pow(2)).sqrt();
          if (SX > RZERO) {
            if ((X / SX.toComplex()).real * Y.real +
                    (X / SX.toComplex()).imaginary * Y.imaginary <
                RZERO) Y = -Y;
          }
          T -= U * zladiv(U, (X + Y));
        }
      }

      // Look for two consecutive small subdiagonal elements.
      var found = false;
      for (M = I - 1; M >= L + 1; M--) {
        // Determine the effect of starting the single-shift QR
        // iteration at row M, and see if this would make H(M,M-1)
        // negligible.
        H11 = H[M][M];
        H22 = H[M + 1][M + 1];
        H11S = H11 - T;
        H21 = H[M + 1][M].real;
        S = H11S.cabs1() + H21.abs();
        H11S /= S.toComplex();
        H21 /= S;
        V[1] = H11S;
        V[2] = H21.toComplex();
        H10 = H[M][M - 1].real;
        if (H10.abs() * H21.abs() <=
            ULP * (H11S.cabs1() * (H11.cabs1() + H22.cabs1()))) {
          found = true;
          break;
        }
      }
      if (!found) {
        H11 = H[L][L];
        H22 = H[L + 1][L + 1];
        H11S = H11 - T;
        H21 = H[L + 1][L].real;
        S = H11S.cabs1() + H21.abs();
        H11S /= S.toComplex();
        H21 /= S;
        V[1] = H11S;
        V[2] = H21.toComplex();
      }

      // Single-shift QR step
      for (K = M; K <= I - 1; K++) {
        // The first iteration of this loop determines a reflection G
        // from the vector V and applies it from left and right to H,
        // thus creating a nonzero bulge below the subdiagonal.

        // Each subsequent iteration determines a reflection G to
        // restore the Hessenberg form in the (K-1)th column, and thus
        // chases the bulge one step toward the bottom of the active
        // submatrix.

        // V(2) is always real before the call to ZLARFG, and hence
        // after the call T2 ( = T1*V(2) ) is also real.
        if (K > M) zcopy(2, H(K, K - 1).asArray(), 1, V, 1);
        zlarfg(2, V(1), V(2), 1, T1);
        if (K > M) {
          H[K][K - 1] = V[1];
          H[K + 1][K - 1] = Complex.zero;
        }
        V2 = V[2];
        T2 = (T1.value * V2).real;

        // Apply G from the left to transform the rows of the matrix
        // in columns K to I2.
        for (J = K; J <= I2; J++) {
          SUM = T1.value.conjugate() * H[K][J] + T2.toComplex() * H[K + 1][J];
          H[K][J] -= SUM;
          H[K + 1][J] -= SUM * V2;
        }

        // Apply G from the right to transform the columns of the
        // matrix in rows I1 to min(K+2,I).
        for (J = I1; J <= min(K + 2, I); J++) {
          SUM = T1.value * H[J][K] + T2.toComplex() * H[J][K + 1];
          H[J][K] -= SUM;
          H[J][K + 1] -= SUM * V2.conjugate();
        }

        if (WANTZ) {
          // Accumulate transformations in the matrix Z
          for (J = ILOZ; J <= IHIZ; J++) {
            SUM = T1.value * Z[J][K] + T2.toComplex() * Z[J][K + 1];
            Z[J][K] -= SUM;
            Z[J][K + 1] -= SUM * V2.conjugate();
          }
        }

        if (K == M && M > L) {
          // If the QR step was started at row M > L because two
          // consecutive small subdiagonals were found, then extra
          // scaling must be performed to ensure that H(M,M-1) remains
          // real.
          TEMP = Complex.one - T1.value;
          TEMP /= TEMP.abs().toComplex();
          H[M + 1][M] *= TEMP.conjugate();
          if (M + 2 <= I) H[M + 2][M + 1] *= TEMP;
          for (J = M; J <= I; J++) {
            if (J != M + 1) {
              if (I2 > J) zscal(I2 - J, TEMP, H(J, J + 1).asArray(), LDH);
              zscal(J - I1, TEMP.conjugate(), H(I1, J).asArray(), 1);
              if (WANTZ) {
                zscal(NZ, TEMP.conjugate(), Z(ILOZ, J).asArray(), 1);
              }
            }
          }
        }
      }

      // Ensure that H(I,I-1) is real.
      TEMP = H[I][I - 1];
      if (TEMP.imaginary != RZERO) {
        RTEMP = TEMP.abs();
        H[I][I - 1] = RTEMP.toComplex();
        TEMP /= RTEMP.toComplex();
        if (I2 > I) zscal(I2 - I, TEMP.conjugate(), H(I, I + 1).asArray(), LDH);
        zscal(I - I1, TEMP, H(I1, I).asArray(), 1);
        if (WANTZ) {
          zscal(NZ, TEMP, Z(ILOZ, I).asArray(), 1);
        }
      }
    }

    if (!converged) {
      // Failure to converge in remaining number of iterations
      INFO.value = I;
      return;
    }

    // H(I,I-1) is negligible: one eigenvalue has converged.
    W[I] = H[I][I];
    // reset deflation counter
    KDEFL = 0;

    // return to start of the main loop with new value of I.
    I = L - 1;
  }
}