SchurTransformer.java
- /*
 -  * Licensed to the Apache Software Foundation (ASF) under one or more
 -  * contributor license agreements.  See the NOTICE file distributed with
 -  * this work for additional information regarding copyright ownership.
 -  * The ASF licenses this file to You under the Apache License, Version 2.0
 -  * (the "License"); you may not use this file except in compliance with
 -  * the License.  You may obtain a copy of the License at
 -  *
 -  *      https://www.apache.org/licenses/LICENSE-2.0
 -  *
 -  * Unless required by applicable law or agreed to in writing, software
 -  * distributed under the License is distributed on an "AS IS" BASIS,
 -  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -  * See the License for the specific language governing permissions and
 -  * limitations under the License.
 -  */
 
- /*
 -  * This is not the original file distributed by the Apache Software Foundation
 -  * It has been modified by the Hipparchus project
 -  */
 
- package org.hipparchus.linear;
 
- import org.hipparchus.exception.LocalizedCoreFormats;
 - import org.hipparchus.exception.MathIllegalArgumentException;
 - import org.hipparchus.exception.MathIllegalStateException;
 - import org.hipparchus.util.FastMath;
 - import org.hipparchus.util.Precision;
 
- /**
 -  * Class transforming a general real matrix to Schur form.
 -  * <p>A m × m matrix A can be written as the product of three matrices: A = P
 -  * × T × P<sup>T</sup> with P an orthogonal matrix and T an quasi-triangular
 -  * matrix. Both P and T are m × m matrices.</p>
 -  * <p>Transformation to Schur form is often not a goal by itself, but it is an
 -  * intermediate step in more general decomposition algorithms like
 -  * {@link EigenDecompositionSymmetric eigen decomposition}. This class is therefore
 -  * intended for expert use. As a consequence of this explicitly limited scope,
 -  * many methods directly returns references to internal arrays, not copies.</p>
 -  * <p>This class is based on the method hqr2 in class EigenvalueDecomposition
 -  * from the <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.</p>
 -  *
 -  * @see <a href="http://mathworld.wolfram.com/SchurDecomposition.html">Schur Decomposition - MathWorld</a>
 -  * @see <a href="http://en.wikipedia.org/wiki/Schur_decomposition">Schur Decomposition - Wikipedia</a>
 -  * @see <a href="http://en.wikipedia.org/wiki/Householder_transformation">Householder Transformations</a>
 -  */
 - public class SchurTransformer {
 -     /** Maximum allowed iterations for convergence of the transformation. */
 -     private static final int MAX_ITERATIONS = 100;
 
-     /** P matrix. */
 -     private final double[][] matrixP;
 -     /** T matrix. */
 -     private final double[][] matrixT;
 -     /** Cached value of P. */
 -     private RealMatrix cachedP;
 -     /** Cached value of T. */
 -     private RealMatrix cachedT;
 -     /** Cached value of PT. */
 -     private RealMatrix cachedPt;
 
-     /** Epsilon criteria. */
 -     private final double epsilon;
 
-     /**
 -      * Build the transformation to Schur form of a general real matrix.
 -      *
 -      * @param matrix matrix to transform
 -      * @throws MathIllegalArgumentException if the matrix is not square
 -      */
 -     public SchurTransformer(final RealMatrix matrix) {
 -         /** Epsilon criteria taken from JAMA code (originally was 2^-52). */
 -         this(matrix, Precision.EPSILON);
 -     }
 
-     /**
 -      * Build the transformation to Schur form of a general real matrix.
 -      *
 -      * @param matrix matrix to transform
 -      * @param epsilon convergence criteria
 -      * @throws MathIllegalArgumentException if the matrix is not square
 -      * @since 3.0
 -      */
 -     public SchurTransformer(final RealMatrix matrix, final double epsilon) {
 -         if (!matrix.isSquare()) {
 -             throw new MathIllegalArgumentException(LocalizedCoreFormats.NON_SQUARE_MATRIX,
 -                                                    matrix.getRowDimension(), matrix.getColumnDimension());
 -         }
 -         this.epsilon = epsilon;
 
-         HessenbergTransformer transformer = new HessenbergTransformer(matrix);
 -         matrixT = transformer.getH().getData();
 -         matrixP = transformer.getP().getData();
 -         cachedT = null;
 -         cachedP = null;
 -         cachedPt = null;
 
-         // transform matrix
 -         transform();
 -     }
 
-     /**
 -      * Returns the matrix P of the transform.
 -      * <p>P is an orthogonal matrix, i.e. its inverse is also its transpose.</p>
 -      *
 -      * @return the P matrix
 -      */
 -     public RealMatrix getP() {
 -         if (cachedP == null) {
 -             cachedP = MatrixUtils.createRealMatrix(matrixP);
 -         }
 -         return cachedP;
 -     }
 
-     /**
 -      * Returns the transpose of the matrix P of the transform.
 -      * <p>P is an orthogonal matrix, i.e. its inverse is also its transpose.</p>
 -      *
 -      * @return the transpose of the P matrix
 -      */
 -     public RealMatrix getPT() {
 -         if (cachedPt == null) {
 -             cachedPt = getP().transpose();
 -         }
 
-         // return the cached matrix
 -         return cachedPt;
 -     }
 
-     /**
 -      * Returns the quasi-triangular Schur matrix T of the transform.
 -      *
 -      * @return the T matrix
 -      */
 -     public RealMatrix getT() {
 -         if (cachedT == null) {
 -             cachedT = MatrixUtils.createRealMatrix(matrixT);
 -         }
 
-         // return the cached matrix
 -         return cachedT;
 -     }
 
-     /**
 -      * Transform original matrix to Schur form.
 -      * @throws MathIllegalStateException if the transformation does not converge
 -      */
 -     private void transform() {
 -         final int n = matrixT.length;
 
-         // compute matrix norm
 -         final double norm = getNorm();
 
-         // shift information
 -         final ShiftInfo shift = new ShiftInfo();
 
-         // Outer loop over eigenvalue index
 -         int iteration = 0;
 -         int iu = n - 1;
 -         while (iu >= 0) {
 
-             // Look for single small sub-diagonal element
 -             final int il = findSmallSubDiagonalElement(iu, norm);
 
-             // Check for convergence
 -             if (il == iu) {
 -                 // One root found
 -                 matrixT[iu][iu] += shift.exShift;
 -                 iu--;
 -                 iteration = 0;
 -             } else if (il == iu - 1) {
 -                 // Two roots found
 -                 double p = (matrixT[iu - 1][iu - 1] - matrixT[iu][iu]) / 2.0;
 -                 double q = p * p + matrixT[iu][iu - 1] * matrixT[iu - 1][iu];
 -                 matrixT[iu][iu] += shift.exShift;
 -                 matrixT[iu - 1][iu - 1] += shift.exShift;
 
-                 if (q >= 0) {
 -                     double z = FastMath.sqrt(FastMath.abs(q));
 -                     if (p >= 0) {
 -                         z = p + z;
 -                     } else {
 -                         z = p - z;
 -                     }
 -                     final double x = matrixT[iu][iu - 1];
 -                     final double s = FastMath.abs(x) + FastMath.abs(z);
 -                     p = x / s;
 -                     q = z / s;
 -                     final double r = FastMath.sqrt(p * p + q * q);
 -                     p /= r;
 -                     q /= r;
 
-                     // Row modification
 -                     for (int j = iu - 1; j < n; j++) {
 -                         z = matrixT[iu - 1][j];
 -                         matrixT[iu - 1][j] = q * z + p * matrixT[iu][j];
 -                         matrixT[iu][j] = q * matrixT[iu][j] - p * z;
 -                     }
 
-                     // Column modification
 -                     for (int i = 0; i <= iu; i++) {
 -                         z = matrixT[i][iu - 1];
 -                         matrixT[i][iu - 1] = q * z + p * matrixT[i][iu];
 -                         matrixT[i][iu] = q * matrixT[i][iu] - p * z;
 -                     }
 
-                     // Accumulate transformations
 -                     for (int i = 0; i <= n - 1; i++) {
 -                         z = matrixP[i][iu - 1];
 -                         matrixP[i][iu - 1] = q * z + p * matrixP[i][iu];
 -                         matrixP[i][iu] = q * matrixP[i][iu] - p * z;
 -                     }
 -                 }
 -                 iu -= 2;
 -                 iteration = 0;
 -             } else {
 -                 // No convergence yet
 -                 computeShift(il, iu, iteration, shift);
 
-                 // stop transformation after too many iterations
 -                 ++iteration;
 -                 if (iteration > MAX_ITERATIONS) {
 -                     throw new MathIllegalStateException(LocalizedCoreFormats.CONVERGENCE_FAILED,
 -                                                         MAX_ITERATIONS);
 -                 }
 
-                 // the initial houseHolder vector for the QR step
 -                 final double[] hVec = new double[3];
 
-                 final int im = initQRStep(il, iu, shift, hVec);
 -                 performDoubleQRStep(il, im, iu, shift, hVec, norm);
 -             }
 -         }
 -     }
 
-     /**
 -      * Computes the L1 norm of the (quasi-)triangular matrix T.
 -      *
 -      * @return the L1 norm of matrix T
 -      */
 -     private double getNorm() {
 -         double norm = 0.0;
 -         for (int i = 0; i < matrixT.length; i++) {
 -             // as matrix T is (quasi-)triangular, also take the sub-diagonal element into account
 -             for (int j = FastMath.max(i - 1, 0); j < matrixT.length; j++) {
 -                 norm += FastMath.abs(matrixT[i][j]);
 -             }
 -         }
 -         return norm;
 -     }
 
-     /**
 -      * Find the first small sub-diagonal element and returns its index.
 -      *
 -      * @param startIdx the starting index for the search
 -      * @param norm the L1 norm of the matrix
 -      * @return the index of the first small sub-diagonal element
 -      */
 -     private int findSmallSubDiagonalElement(final int startIdx, final double norm) {
 -         int l = startIdx;
 -         while (l > 0) {
 -             double s = FastMath.abs(matrixT[l - 1][l - 1]) + FastMath.abs(matrixT[l][l]);
 -             if (s == 0.0) {
 -                 s = norm;
 -             }
 -             if (FastMath.abs(matrixT[l][l - 1]) < epsilon * s) {
 -                 break;
 -             }
 -             l--;
 -         }
 -         return l;
 -     }
 
-     /**
 -      * Compute the shift for the current iteration.
 -      *
 -      * @param l the index of the small sub-diagonal element
 -      * @param idx the current eigenvalue index
 -      * @param iteration the current iteration
 -      * @param shift holder for shift information
 -      */
 -     private void computeShift(final int l, final int idx, final int iteration, final ShiftInfo shift) {
 -         // Form shift
 -         shift.x = matrixT[idx][idx];
 -         shift.y = shift.w = 0.0;
 -         if (l < idx) {
 -             shift.y = matrixT[idx - 1][idx - 1];
 -             shift.w = matrixT[idx][idx - 1] * matrixT[idx - 1][idx];
 -         }
 
-         // Wilkinson's original ad hoc shift
 -         if (iteration == 10) {
 -             shift.exShift += shift.x;
 -             for (int i = 0; i <= idx; i++) {
 -                 matrixT[i][i] -= shift.x;
 -             }
 -             final double s = FastMath.abs(matrixT[idx][idx - 1]) + FastMath.abs(matrixT[idx - 1][idx - 2]);
 -             shift.x = 0.75 * s;
 -             shift.y = 0.75 * s;
 -             shift.w = -0.4375 * s * s;
 -         }
 
-         // MATLAB's new ad hoc shift
 -         if (iteration == 30) {
 -             double s = (shift.y - shift.x) / 2.0;
 -             s = s * s + shift.w;
 -             if (s > 0.0) {
 -                 s = FastMath.sqrt(s);
 -                 if (shift.y < shift.x) {
 -                     s = -s;
 -                 }
 -                 s = shift.x - shift.w / ((shift.y - shift.x) / 2.0 + s);
 -                 for (int i = 0; i <= idx; i++) {
 -                     matrixT[i][i] -= s;
 -                 }
 -                 shift.exShift += s;
 -                 shift.x = shift.y = shift.w = 0.964;
 -             }
 -         }
 -     }
 
-     /**
 -      * Initialize the householder vectors for the QR step.
 -      *
 -      * @param il the index of the small sub-diagonal element
 -      * @param iu the current eigenvalue index
 -      * @param shift shift information holder
 -      * @param hVec the initial houseHolder vector
 -      * @return the start index for the QR step
 -      */
 -     private int initQRStep(int il, final int iu, final ShiftInfo shift, double[] hVec) {
 -         // Look for two consecutive small sub-diagonal elements
 -         int im = iu - 2;
 -         while (im >= il) {
 -             final double z = matrixT[im][im];
 -             final double r = shift.x - z;
 -             double s = shift.y - z;
 -             hVec[0] = (r * s - shift.w) / matrixT[im + 1][im] + matrixT[im][im + 1];
 -             hVec[1] = matrixT[im + 1][im + 1] - z - r - s;
 -             hVec[2] = matrixT[im + 2][im + 1];
 
-             if (im == il) {
 -                 break;
 -             }
 
-             final double lhs = FastMath.abs(matrixT[im][im - 1]) * (FastMath.abs(hVec[1]) + FastMath.abs(hVec[2]));
 -             final double rhs = FastMath.abs(hVec[0]) * (FastMath.abs(matrixT[im - 1][im - 1]) +
 -                                                         FastMath.abs(z) +
 -                                                         FastMath.abs(matrixT[im + 1][im + 1]));
 
-             if (lhs < epsilon * rhs) {
 -                 break;
 -             }
 -             im--;
 -         }
 
-         return im;
 -     }
 
-     /**
 -      * Perform a double QR step involving rows l:idx and columns m:n
 -      *
 -      * @param il the index of the small sub-diagonal element
 -      * @param im the start index for the QR step
 -      * @param iu the current eigenvalue index
 -      * @param shift shift information holder
 -      * @param hVec the initial houseHolder vector
 -      * @param norm matrix norm
 -      */
 -     private void performDoubleQRStep(final int il, final int im, final int iu,
 -                                      final ShiftInfo shift, final double[] hVec,
 -                                      final double norm) {
 
-         final int n = matrixT.length;
 -         double p = hVec[0];
 -         double q = hVec[1];
 -         double r = hVec[2];
 
-         for (int k = im; k <= iu - 1; k++) {
 -             boolean notlast = k != (iu - 1);
 -             if (k != im) {
 -                 p = matrixT[k][k - 1];
 -                 q = matrixT[k + 1][k - 1];
 -                 r = notlast ? matrixT[k + 2][k - 1] : 0.0;
 -                 shift.x = FastMath.abs(p) + FastMath.abs(q) + FastMath.abs(r);
 -                 if (Precision.equals(shift.x, 0.0, epsilon * norm)) {
 -                     continue;
 -                 }
 -                 p /= shift.x;
 -                 q /= shift.x;
 -                 r /= shift.x;
 -             }
 -             double s = FastMath.sqrt(p * p + q * q + r * r);
 -             if (p < 0.0) {
 -                 s = -s;
 -             }
 -             if (s != 0.0) {
 -                 if (k != im) {
 -                     matrixT[k][k - 1] = -s * shift.x;
 -                 } else if (il != im) {
 -                     matrixT[k][k - 1] = -matrixT[k][k - 1];
 -                 }
 -                 p += s;
 -                 shift.x = p / s;
 -                 shift.y = q / s;
 -                 double z = r / s;
 -                 q /= p;
 -                 r /= p;
 
-                 // Row modification
 -                 for (int j = k; j < n; j++) {
 -                     p = matrixT[k][j] + q * matrixT[k + 1][j];
 -                     if (notlast) {
 -                         p += r * matrixT[k + 2][j];
 -                         matrixT[k + 2][j] -= p * z;
 -                     }
 -                     matrixT[k][j] -= p * shift.x;
 -                     matrixT[k + 1][j] -= p * shift.y;
 -                 }
 
-                 // Column modification
 -                 for (int i = 0; i <= FastMath.min(iu, k + 3); i++) {
 -                     p = shift.x * matrixT[i][k] + shift.y * matrixT[i][k + 1];
 -                     if (notlast) {
 -                         p += z * matrixT[i][k + 2];
 -                         matrixT[i][k + 2] -= p * r;
 -                     }
 -                     matrixT[i][k] -= p;
 -                     matrixT[i][k + 1] -= p * q;
 -                 }
 
-                 // Accumulate transformations
 -                 final int high = matrixT.length - 1;
 -                 for (int i = 0; i <= high; i++) {
 -                     p = shift.x * matrixP[i][k] + shift.y * matrixP[i][k + 1];
 -                     if (notlast) {
 -                         p += z * matrixP[i][k + 2];
 -                         matrixP[i][k + 2] -= p * r;
 -                     }
 -                     matrixP[i][k] -= p;
 -                     matrixP[i][k + 1] -= p * q;
 -                 }
 -             }  // (s != 0)
 -         }  // k loop
 
-         // clean up pollution due to round-off errors
 -         for (int i = im + 2; i <= iu; i++) {
 -             matrixT[i][i-2] = 0.0;
 -             if (i > im + 2) {
 -                 matrixT[i][i-3] = 0.0;
 -             }
 -         }
 -     }
 
-     /**
 -      * Internal data structure holding the current shift information.
 -      * Contains variable names as present in the original JAMA code.
 -      */
 -     private static class ShiftInfo {
 -         // CHECKSTYLE: stop all
 
-         /** x shift info */
 -         double x;
 -         /** y shift info */
 -         double y;
 -         /** w shift info */
 -         double w;
 -         /** Indicates an exceptional shift. */
 -         double exShift;
 
-         // CHECKSTYLE: resume all
 -     }
 - }