EigenDecomposition.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.math4.legacy.linear;

  18. import org.apache.commons.numbers.complex.Complex;
  19. import org.apache.commons.numbers.core.Precision;
  20. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  21. import org.apache.commons.math4.legacy.exception.MathArithmeticException;
  22. import org.apache.commons.math4.legacy.exception.MathUnsupportedOperationException;
  23. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  24. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  25. import org.apache.commons.math4.core.jdkmath.JdkMath;

  26. /**
  27.  * Calculates the eigen decomposition of a real matrix.
  28.  * <p>
  29.  * The eigen decomposition of matrix A is a set of two matrices:
  30.  * V and D such that A = V &times; D &times; V<sup>T</sup>.
  31.  * A, V and D are all m &times; m matrices.
  32.  * <p>
  33.  * This class is similar in spirit to the {@code EigenvalueDecomposition}
  34.  * class from the <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a>
  35.  * library, with the following changes:
  36.  * <ul>
  37.  *   <li>a {@link #getVT() getVt} method has been added,</li>
  38.  *   <li>two {@link #getRealEigenvalue(int) getRealEigenvalue} and
  39.  *       {@link #getImagEigenvalue(int) getImagEigenvalue} methods to pick up a
  40.  *       single eigenvalue have been added,</li>
  41.  *   <li>a {@link #getEigenvector(int) getEigenvector} method to pick up a
  42.  *       single eigenvector has been added,</li>
  43.  *   <li>a {@link #getDeterminant() getDeterminant} method has been added.</li>
  44.  *   <li>a {@link #getSolver() getSolver} method has been added.</li>
  45.  * </ul>
  46.  * <p>
  47.  * As of 3.1, this class supports general real matrices (both symmetric and non-symmetric):
  48.  * <p>
  49.  * If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal
  50.  * and the eigenvector matrix V is orthogonal, i.e.
  51.  * {@code A = V.multiply(D.multiply(V.transpose()))} and
  52.  * {@code V.multiply(V.transpose())} equals the identity matrix.
  53.  * </p>
  54.  * <p>
  55.  * If A is not symmetric, then the eigenvalue matrix D is block diagonal with the real
  56.  * eigenvalues in 1-by-1 blocks and any complex eigenvalues, lambda + i*mu, in 2-by-2
  57.  * blocks:
  58.  * <pre>
  59.  *    [lambda, mu    ]
  60.  *    [   -mu, lambda]
  61.  * </pre>
  62.  * The columns of V represent the eigenvectors in the sense that {@code A*V = V*D},
  63.  * i.e. A.multiply(V) equals V.multiply(D).
  64.  * The matrix V may be badly conditioned, or even singular, so the validity of the
  65.  * equation {@code A = V*D*inverse(V)} depends upon the condition of V.
  66.  * <p>
  67.  * This implementation is based on the paper by A. Drubrulle, R.S. Martin and
  68.  * J.H. Wilkinson "The Implicit QL Algorithm" in Wilksinson and Reinsch (1971)
  69.  * Handbook for automatic computation, vol. 2, Linear algebra, Springer-Verlag,
  70.  * New-York.
  71.  *
  72.  * @see <a href="http://mathworld.wolfram.com/EigenDecomposition.html">MathWorld</a>
  73.  * @see <a href="http://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix">Wikipedia</a>
  74.  * @since 2.0 (changed to concrete class in 3.0)
  75.  */
  76. public class EigenDecomposition {
  77.     /** Internally used epsilon criteria. */
  78.     private static final double EPSILON = 1e-12;
  79.     /** Maximum number of iterations accepted in the implicit QL transformation. */
  80.     private static final byte MAX_ITER = 30;
  81.     /** Main diagonal of the tridiagonal matrix. */
  82.     private double[] main;
  83.     /** Secondary diagonal of the tridiagonal matrix. */
  84.     private double[] secondary;
  85.     /**
  86.      * Transformer to tridiagonal (may be null if matrix is already
  87.      * tridiagonal).
  88.      */
  89.     private TriDiagonalTransformer transformer;
  90.     /** Real part of the realEigenvalues. */
  91.     private double[] realEigenvalues;
  92.     /** Imaginary part of the realEigenvalues. */
  93.     private double[] imagEigenvalues;
  94.     /** Eigenvectors. */
  95.     private ArrayRealVector[] eigenvectors;
  96.     /** Cached value of V. */
  97.     private RealMatrix cachedV;
  98.     /** Cached value of D. */
  99.     private RealMatrix cachedD;
  100.     /** Cached value of Vt. */
  101.     private RealMatrix cachedVt;
  102.     /** Whether the matrix is symmetric. */
  103.     private final boolean isSymmetric;

  104.     /**
  105.      * Calculates the eigen decomposition of the given real matrix.
  106.      * <p>
  107.      * Supports decomposition of a general matrix since 3.1.
  108.      *
  109.      * @param matrix Matrix to decompose.
  110.      * @throws MaxCountExceededException if the algorithm fails to converge.
  111.      * @throws MathArithmeticException if the decomposition of a general matrix
  112.      * results in a matrix with zero norm
  113.      * @since 3.1
  114.      */
  115.     public EigenDecomposition(final RealMatrix matrix)
  116.         throws MathArithmeticException {
  117.         final double symTol = 10 * matrix.getRowDimension() * matrix.getColumnDimension() * Precision.EPSILON;
  118.         isSymmetric = MatrixUtils.isSymmetric(matrix, symTol);
  119.         if (isSymmetric) {
  120.             transformToTridiagonal(matrix);
  121.             findEigenVectors(transformer.getQ().getData());
  122.         } else {
  123.             final SchurTransformer t = transformToSchur(matrix);
  124.             findEigenVectorsFromSchur(t);
  125.         }
  126.     }

  127.     /**
  128.      * Calculates the eigen decomposition of the symmetric tridiagonal
  129.      * matrix.  The Householder matrix is assumed to be the identity matrix.
  130.      *
  131.      * @param main Main diagonal of the symmetric tridiagonal form.
  132.      * @param secondary Secondary of the tridiagonal form.
  133.      * @throws MaxCountExceededException if the algorithm fails to converge.
  134.      * @since 3.1
  135.      */
  136.     public EigenDecomposition(final double[] main, final double[] secondary) {
  137.         isSymmetric = true;
  138.         this.main      = main.clone();
  139.         this.secondary = secondary.clone();
  140.         transformer    = null;
  141.         final int size = main.length;
  142.         final double[][] z = new double[size][size];
  143.         for (int i = 0; i < size; i++) {
  144.             z[i][i] = 1.0;
  145.         }
  146.         findEigenVectors(z);
  147.     }

  148.     /**
  149.      * Gets the matrix V of the decomposition.
  150.      * V is an orthogonal matrix, i.e. its transpose is also its inverse.
  151.      * The columns of V are the eigenvectors of the original matrix.
  152.      * No assumption is made about the orientation of the system axes formed
  153.      * by the columns of V (e.g. in a 3-dimension space, V can form a left-
  154.      * or right-handed system).
  155.      *
  156.      * @return the V matrix.
  157.      */
  158.     public RealMatrix getV() {

  159.         if (cachedV == null) {
  160.             final int m = eigenvectors.length;
  161.             cachedV = MatrixUtils.createRealMatrix(m, m);
  162.             for (int k = 0; k < m; ++k) {
  163.                 cachedV.setColumnVector(k, eigenvectors[k]);
  164.             }
  165.         }
  166.         // return the cached matrix
  167.         return cachedV;
  168.     }

  169.     /**
  170.      * Gets the block diagonal matrix D of the decomposition.
  171.      * D is a block diagonal matrix.
  172.      * Real eigenvalues are on the diagonal while complex values are on
  173.      * 2x2 blocks { {real +imaginary}, {-imaginary, real} }.
  174.      *
  175.      * @return the D matrix.
  176.      *
  177.      * @see #getRealEigenvalues()
  178.      * @see #getImagEigenvalues()
  179.      */
  180.     public RealMatrix getD() {

  181.         if (cachedD == null) {
  182.             // cache the matrix for subsequent calls
  183.             cachedD = MatrixUtils.createRealMatrixWithDiagonal(realEigenvalues);

  184.             for (int i = 0; i < imagEigenvalues.length; i++) {
  185.                 if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) > 0) {
  186.                     cachedD.setEntry(i, i+1, imagEigenvalues[i]);
  187.                 } else if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
  188.                     cachedD.setEntry(i, i-1, imagEigenvalues[i]);
  189.                 }
  190.             }
  191.         }
  192.         return cachedD;
  193.     }

  194.     /**
  195.      * Gets the transpose of the matrix V of the decomposition.
  196.      * V is an orthogonal matrix, i.e. its transpose is also its inverse.
  197.      * The columns of V are the eigenvectors of the original matrix.
  198.      * No assumption is made about the orientation of the system axes formed
  199.      * by the columns of V (e.g. in a 3-dimension space, V can form a left-
  200.      * or right-handed system).
  201.      *
  202.      * @return the transpose of the V matrix.
  203.      */
  204.     public RealMatrix getVT() {

  205.         if (cachedVt == null) {
  206.             final int m = eigenvectors.length;
  207.             cachedVt = MatrixUtils.createRealMatrix(m, m);
  208.             for (int k = 0; k < m; ++k) {
  209.                 cachedVt.setRowVector(k, eigenvectors[k]);
  210.             }
  211.         }

  212.         // return the cached matrix
  213.         return cachedVt;
  214.     }

  215.     /**
  216.      * Returns whether the calculated eigen values are complex or real.
  217.      * <p>The method performs a zero check for each element of the
  218.      * {@link #getImagEigenvalues()} array and returns {@code true} if any
  219.      * element is not equal to zero.
  220.      *
  221.      * @return {@code true} if the eigen values are complex, {@code false} otherwise
  222.      * @since 3.1
  223.      */
  224.     public boolean hasComplexEigenvalues() {
  225.         for (int i = 0; i < imagEigenvalues.length; i++) {
  226.             if (!Precision.equals(imagEigenvalues[i], 0.0, EPSILON)) {
  227.                 return true;
  228.             }
  229.         }
  230.         return false;
  231.     }

  232.     /**
  233.      * Gets a copy of the real parts of the eigenvalues of the original matrix.
  234.      *
  235.      * @return a copy of the real parts of the eigenvalues of the original matrix.
  236.      *
  237.      * @see #getD()
  238.      * @see #getRealEigenvalue(int)
  239.      * @see #getImagEigenvalues()
  240.      */
  241.     public double[] getRealEigenvalues() {
  242.         return realEigenvalues.clone();
  243.     }

  244.     /**
  245.      * Returns the real part of the i<sup>th</sup> eigenvalue of the original
  246.      * matrix.
  247.      *
  248.      * @param i index of the eigenvalue (counting from 0)
  249.      * @return real part of the i<sup>th</sup> eigenvalue of the original
  250.      * matrix.
  251.      *
  252.      * @see #getD()
  253.      * @see #getRealEigenvalues()
  254.      * @see #getImagEigenvalue(int)
  255.      */
  256.     public double getRealEigenvalue(final int i) {
  257.         return realEigenvalues[i];
  258.     }

  259.     /**
  260.      * Gets a copy of the imaginary parts of the eigenvalues of the original
  261.      * matrix.
  262.      *
  263.      * @return a copy of the imaginary parts of the eigenvalues of the original
  264.      * matrix.
  265.      *
  266.      * @see #getD()
  267.      * @see #getImagEigenvalue(int)
  268.      * @see #getRealEigenvalues()
  269.      */
  270.     public double[] getImagEigenvalues() {
  271.         return imagEigenvalues.clone();
  272.     }

  273.     /**
  274.      * Gets the imaginary part of the i<sup>th</sup> eigenvalue of the original
  275.      * matrix.
  276.      *
  277.      * @param i Index of the eigenvalue (counting from 0).
  278.      * @return the imaginary part of the i<sup>th</sup> eigenvalue of the original
  279.      * matrix.
  280.      *
  281.      * @see #getD()
  282.      * @see #getImagEigenvalues()
  283.      * @see #getRealEigenvalue(int)
  284.      */
  285.     public double getImagEigenvalue(final int i) {
  286.         return imagEigenvalues[i];
  287.     }

  288.     /**
  289.      * Gets a copy of the i<sup>th</sup> eigenvector of the original matrix.
  290.      *
  291.      * @param i Index of the eigenvector (counting from 0).
  292.      * @return a copy of the i<sup>th</sup> eigenvector of the original matrix.
  293.      * @see #getD()
  294.      */
  295.     public RealVector getEigenvector(final int i) {
  296.         return eigenvectors[i].copy();
  297.     }

  298.     /**
  299.      * Computes the determinant of the matrix.
  300.      *
  301.      * @return the determinant of the matrix.
  302.      */
  303.     public double getDeterminant() {
  304.         double determinant = 1;
  305.         for (double lambda : realEigenvalues) {
  306.             determinant *= lambda;
  307.         }
  308.         return determinant;
  309.     }

  310.     /**
  311.      * Computes the square-root of the matrix.
  312.      * This implementation assumes that the matrix is symmetric and positive
  313.      * definite.
  314.      *
  315.      * @return the square-root of the matrix.
  316.      * @throws MathUnsupportedOperationException if the matrix is not
  317.      * symmetric or not positive definite.
  318.      * @since 3.1
  319.      */
  320.     public RealMatrix getSquareRoot() {
  321.         if (!isSymmetric) {
  322.             throw new MathUnsupportedOperationException();
  323.         }

  324.         final double[] sqrtEigenValues = new double[realEigenvalues.length];
  325.         for (int i = 0; i < realEigenvalues.length; i++) {
  326.             final double eigen = realEigenvalues[i];
  327.             if (eigen <= 0) {
  328.                 throw new MathUnsupportedOperationException();
  329.             }
  330.             sqrtEigenValues[i] = JdkMath.sqrt(eigen);
  331.         }
  332.         final RealMatrix sqrtEigen = MatrixUtils.createRealDiagonalMatrix(sqrtEigenValues);
  333.         final RealMatrix v = getV();
  334.         final RealMatrix vT = getVT();

  335.         return v.multiply(sqrtEigen).multiply(vT);
  336.     }

  337.     /**
  338.      * Gets a solver for finding the A &times; X = B solution in exact
  339.      * linear sense.
  340.      * <p>
  341.      * Since 3.1, eigen decomposition of a general matrix is supported,
  342.      * but the {@link DecompositionSolver} only supports real eigenvalues.
  343.      *
  344.      * @return a solver
  345.      * @throws MathUnsupportedOperationException if the decomposition resulted in
  346.      * complex eigenvalues
  347.      */
  348.     public DecompositionSolver getSolver() {
  349.         if (hasComplexEigenvalues()) {
  350.             throw new MathUnsupportedOperationException();
  351.         }
  352.         return new Solver(realEigenvalues, imagEigenvalues, eigenvectors);
  353.     }

  354.     /** Specialized solver. */
  355.     private static final class Solver implements DecompositionSolver {
  356.         /** Real part of the realEigenvalues. */
  357.         private final double[] realEigenvalues;
  358.         /** Imaginary part of the realEigenvalues. */
  359.         private final double[] imagEigenvalues;
  360.         /** Eigenvectors. */
  361.         private final ArrayRealVector[] eigenvectors;

  362.         /**
  363.          * Builds a solver from decomposed matrix.
  364.          *
  365.          * @param realEigenvalues Real parts of the eigenvalues.
  366.          * @param imagEigenvalues Imaginary parts of the eigenvalues.
  367.          * @param eigenvectors Eigenvectors.
  368.          */
  369.         private Solver(final double[] realEigenvalues,
  370.                 final double[] imagEigenvalues,
  371.                 final ArrayRealVector[] eigenvectors) {
  372.             this.realEigenvalues = realEigenvalues;
  373.             this.imagEigenvalues = imagEigenvalues;
  374.             this.eigenvectors = eigenvectors;
  375.         }

  376.         /**
  377.          * Solves the linear equation A &times; X = B for symmetric matrices A.
  378.          * <p>
  379.          * This method only finds exact linear solutions, i.e. solutions for
  380.          * which ||A &times; X - B|| is exactly 0.
  381.          * </p>
  382.          *
  383.          * @param b Right-hand side of the equation A &times; X = B.
  384.          * @return a Vector X that minimizes the two norm of A &times; X - B.
  385.          *
  386.          * @throws DimensionMismatchException if the matrices dimensions do not match.
  387.          * @throws SingularMatrixException if the decomposed matrix is singular.
  388.          */
  389.         @Override
  390.         public RealVector solve(final RealVector b) {
  391.             if (!isNonSingular()) {
  392.                 throw new SingularMatrixException();
  393.             }

  394.             final int m = realEigenvalues.length;
  395.             if (b.getDimension() != m) {
  396.                 throw new DimensionMismatchException(b.getDimension(), m);
  397.             }

  398.             final double[] bp = new double[m];
  399.             for (int i = 0; i < m; ++i) {
  400.                 final ArrayRealVector v = eigenvectors[i];
  401.                 final double[] vData = v.getDataRef();
  402.                 final double s = v.dotProduct(b) / realEigenvalues[i];
  403.                 for (int j = 0; j < m; ++j) {
  404.                     bp[j] += s * vData[j];
  405.                 }
  406.             }

  407.             return new ArrayRealVector(bp, false);
  408.         }

  409.         /** {@inheritDoc} */
  410.         @Override
  411.         public RealMatrix solve(RealMatrix b) {

  412.             if (!isNonSingular()) {
  413.                 throw new SingularMatrixException();
  414.             }

  415.             final int m = realEigenvalues.length;
  416.             if (b.getRowDimension() != m) {
  417.                 throw new DimensionMismatchException(b.getRowDimension(), m);
  418.             }

  419.             final int nColB = b.getColumnDimension();
  420.             final double[][] bp = new double[m][nColB];
  421.             final double[] tmpCol = new double[m];
  422.             for (int k = 0; k < nColB; ++k) {
  423.                 for (int i = 0; i < m; ++i) {
  424.                     tmpCol[i] = b.getEntry(i, k);
  425.                     bp[i][k]  = 0;
  426.                 }
  427.                 for (int i = 0; i < m; ++i) {
  428.                     final ArrayRealVector v = eigenvectors[i];
  429.                     final double[] vData = v.getDataRef();
  430.                     double s = 0;
  431.                     for (int j = 0; j < m; ++j) {
  432.                         s += v.getEntry(j) * tmpCol[j];
  433.                     }
  434.                     s /= realEigenvalues[i];
  435.                     for (int j = 0; j < m; ++j) {
  436.                         bp[j][k] += s * vData[j];
  437.                     }
  438.                 }
  439.             }

  440.             return new Array2DRowRealMatrix(bp, false);
  441.         }

  442.         /**
  443.          * Checks whether the decomposed matrix is non-singular.
  444.          *
  445.          * @return true if the decomposed matrix is non-singular.
  446.          */
  447.         @Override
  448.         public boolean isNonSingular() {
  449.             double largestEigenvalueNorm = 0.0;
  450.             // Looping over all values (in case they are not sorted in decreasing
  451.             // order of their norm).
  452.             for (int i = 0; i < realEigenvalues.length; ++i) {
  453.                 largestEigenvalueNorm = JdkMath.max(largestEigenvalueNorm, eigenvalueNorm(i));
  454.             }
  455.             // Corner case: zero matrix, all exactly 0 eigenvalues
  456.             if (largestEigenvalueNorm == 0.0) {
  457.                 return false;
  458.             }
  459.             for (int i = 0; i < realEigenvalues.length; ++i) {
  460.                 // Looking for eigenvalues that are 0, where we consider anything much much smaller
  461.                 // than the largest eigenvalue to be effectively 0.
  462.                 if (Precision.equals(eigenvalueNorm(i) / largestEigenvalueNorm, 0, EPSILON)) {
  463.                     return false;
  464.                 }
  465.             }
  466.             return true;
  467.         }

  468.         /**
  469.          * @param i which eigenvalue to find the norm of
  470.          * @return the norm of ith (complex) eigenvalue.
  471.          */
  472.         private double eigenvalueNorm(int i) {
  473.             final double re = realEigenvalues[i];
  474.             final double im = imagEigenvalues[i];
  475.             return JdkMath.sqrt(re * re + im * im);
  476.         }

  477.         /**
  478.          * Get the inverse of the decomposed matrix.
  479.          *
  480.          * @return the inverse matrix.
  481.          * @throws SingularMatrixException if the decomposed matrix is singular.
  482.          */
  483.         @Override
  484.         public RealMatrix getInverse() {
  485.             if (!isNonSingular()) {
  486.                 throw new SingularMatrixException();
  487.             }

  488.             final int m = realEigenvalues.length;
  489.             final double[][] invData = new double[m][m];

  490.             for (int i = 0; i < m; ++i) {
  491.                 final double[] invI = invData[i];
  492.                 for (int j = 0; j < m; ++j) {
  493.                     double invIJ = 0;
  494.                     for (int k = 0; k < m; ++k) {
  495.                         final double[] vK = eigenvectors[k].getDataRef();
  496.                         invIJ += vK[i] * vK[j] / realEigenvalues[k];
  497.                     }
  498.                     invI[j] = invIJ;
  499.                 }
  500.             }
  501.             return MatrixUtils.createRealMatrix(invData);
  502.         }
  503.     }

  504.     /**
  505.      * Transforms the matrix to tridiagonal form.
  506.      *
  507.      * @param matrix Matrix to transform.
  508.      */
  509.     private void transformToTridiagonal(final RealMatrix matrix) {
  510.         // transform the matrix to tridiagonal
  511.         transformer = new TriDiagonalTransformer(matrix);
  512.         main = transformer.getMainDiagonalRef();
  513.         secondary = transformer.getSecondaryDiagonalRef();
  514.     }

  515.     /**
  516.      * Find eigenvalues and eigenvectors (Dubrulle et al., 1971).
  517.      *
  518.      * @param householderMatrix Householder matrix of the transformation
  519.      * to tridiagonal form.
  520.      */
  521.     private void findEigenVectors(final double[][] householderMatrix) {
  522.         final double[][]z = householderMatrix.clone();
  523.         final int n = main.length;
  524.         realEigenvalues = new double[n];
  525.         imagEigenvalues = new double[n];
  526.         final double[] e = new double[n];
  527.         for (int i = 0; i < n - 1; i++) {
  528.             realEigenvalues[i] = main[i];
  529.             e[i] = secondary[i];
  530.         }
  531.         realEigenvalues[n - 1] = main[n - 1];
  532.         e[n - 1] = 0;

  533.         // Determine the largest main and secondary value in absolute term.
  534.         double maxAbsoluteValue = 0;
  535.         for (int i = 0; i < n; i++) {
  536.             if (JdkMath.abs(realEigenvalues[i]) > maxAbsoluteValue) {
  537.                 maxAbsoluteValue = JdkMath.abs(realEigenvalues[i]);
  538.             }
  539.             if (JdkMath.abs(e[i]) > maxAbsoluteValue) {
  540.                 maxAbsoluteValue = JdkMath.abs(e[i]);
  541.             }
  542.         }
  543.         // Make null any main and secondary value too small to be significant
  544.         if (maxAbsoluteValue != 0) {
  545.             for (int i=0; i < n; i++) {
  546.                 if (JdkMath.abs(realEigenvalues[i]) <= Precision.EPSILON * maxAbsoluteValue) {
  547.                     realEigenvalues[i] = 0;
  548.                 }
  549.                 if (JdkMath.abs(e[i]) <= Precision.EPSILON * maxAbsoluteValue) {
  550.                     e[i]=0;
  551.                 }
  552.             }
  553.         }

  554.         for (int j = 0; j < n; j++) {
  555.             int its = 0;
  556.             int m;
  557.             do {
  558.                 for (m = j; m < n - 1; m++) {
  559.                     double delta = JdkMath.abs(realEigenvalues[m]) +
  560.                         JdkMath.abs(realEigenvalues[m + 1]);
  561.                     if (JdkMath.abs(e[m]) + delta == delta) {
  562.                         break;
  563.                     }
  564.                 }
  565.                 if (m != j) {
  566.                     if (its == MAX_ITER) {
  567.                         throw new MaxCountExceededException(LocalizedFormats.CONVERGENCE_FAILED,
  568.                                                             MAX_ITER);
  569.                     }
  570.                     its++;
  571.                     double q = (realEigenvalues[j + 1] - realEigenvalues[j]) / (2 * e[j]);
  572.                     double t = JdkMath.sqrt(1 + q * q);
  573.                     if (q < 0.0) {
  574.                         q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q - t);
  575.                     } else {
  576.                         q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q + t);
  577.                     }
  578.                     double u = 0.0;
  579.                     double s = 1.0;
  580.                     double c = 1.0;
  581.                     int i;
  582.                     for (i = m - 1; i >= j; i--) {
  583.                         double p = s * e[i];
  584.                         double h = c * e[i];
  585.                         if (JdkMath.abs(p) >= JdkMath.abs(q)) {
  586.                             c = q / p;
  587.                             t = JdkMath.sqrt(c * c + 1.0);
  588.                             e[i + 1] = p * t;
  589.                             s = 1.0 / t;
  590.                             c *= s;
  591.                         } else {
  592.                             s = p / q;
  593.                             t = JdkMath.sqrt(s * s + 1.0);
  594.                             e[i + 1] = q * t;
  595.                             c = 1.0 / t;
  596.                             s *= c;
  597.                         }
  598.                         if (e[i + 1] == 0.0) {
  599.                             realEigenvalues[i + 1] -= u;
  600.                             e[m] = 0.0;
  601.                             break;
  602.                         }
  603.                         q = realEigenvalues[i + 1] - u;
  604.                         t = (realEigenvalues[i] - q) * s + 2.0 * c * h;
  605.                         u = s * t;
  606.                         realEigenvalues[i + 1] = q + u;
  607.                         q = c * t - h;
  608.                         for (int ia = 0; ia < n; ia++) {
  609.                             p = z[ia][i + 1];
  610.                             z[ia][i + 1] = s * z[ia][i] + c * p;
  611.                             z[ia][i] = c * z[ia][i] - s * p;
  612.                         }
  613.                     }
  614.                     if (t == 0.0 && i >= j) {
  615.                         continue;
  616.                     }
  617.                     realEigenvalues[j] -= u;
  618.                     e[j] = q;
  619.                     e[m] = 0.0;
  620.                 }
  621.             } while (m != j);
  622.         }

  623.         //Sort the eigen values (and vectors) in increase order
  624.         for (int i = 0; i < n; i++) {
  625.             int k = i;
  626.             double p = realEigenvalues[i];
  627.             for (int j = i + 1; j < n; j++) {
  628.                 if (realEigenvalues[j] > p) {
  629.                     k = j;
  630.                     p = realEigenvalues[j];
  631.                 }
  632.             }
  633.             if (k != i) {
  634.                 realEigenvalues[k] = realEigenvalues[i];
  635.                 realEigenvalues[i] = p;
  636.                 for (int j = 0; j < n; j++) {
  637.                     p = z[j][i];
  638.                     z[j][i] = z[j][k];
  639.                     z[j][k] = p;
  640.                 }
  641.             }
  642.         }

  643.         // Determine the largest eigen value in absolute term.
  644.         maxAbsoluteValue = 0;
  645.         for (int i = 0; i < n; i++) {
  646.             if (JdkMath.abs(realEigenvalues[i]) > maxAbsoluteValue) {
  647.                 maxAbsoluteValue=JdkMath.abs(realEigenvalues[i]);
  648.             }
  649.         }
  650.         // Make null any eigen value too small to be significant
  651.         if (maxAbsoluteValue != 0.0) {
  652.             for (int i=0; i < n; i++) {
  653.                 if (JdkMath.abs(realEigenvalues[i]) < Precision.EPSILON * maxAbsoluteValue) {
  654.                     realEigenvalues[i] = 0;
  655.                 }
  656.             }
  657.         }
  658.         eigenvectors = new ArrayRealVector[n];
  659.         final double[] tmp = new double[n];
  660.         for (int i = 0; i < n; i++) {
  661.             for (int j = 0; j < n; j++) {
  662.                 tmp[j] = z[j][i];
  663.             }
  664.             eigenvectors[i] = new ArrayRealVector(tmp);
  665.         }
  666.     }

  667.     /**
  668.      * Transforms the matrix to Schur form and calculates the eigenvalues.
  669.      *
  670.      * @param matrix Matrix to transform.
  671.      * @return the {@link SchurTransformer Shur transform} for this matrix
  672.      */
  673.     private SchurTransformer transformToSchur(final RealMatrix matrix) {
  674.         final SchurTransformer schurTransform = new SchurTransformer(matrix);
  675.         final double[][] matT = schurTransform.getT().getData();

  676.         realEigenvalues = new double[matT.length];
  677.         imagEigenvalues = new double[matT.length];

  678.         for (int i = 0; i < realEigenvalues.length; i++) {
  679.             if (i == (realEigenvalues.length - 1) ||
  680.                 Precision.equals(matT[i + 1][i], 0.0, EPSILON)) {
  681.                 realEigenvalues[i] = matT[i][i];
  682.             } else {
  683.                 final double x = matT[i + 1][i + 1];
  684.                 final double p = 0.5 * (matT[i][i] - x);
  685.                 final double z = JdkMath.sqrt(JdkMath.abs(p * p + matT[i + 1][i] * matT[i][i + 1]));
  686.                 realEigenvalues[i] = x + p;
  687.                 imagEigenvalues[i] = z;
  688.                 realEigenvalues[i + 1] = x + p;
  689.                 imagEigenvalues[i + 1] = -z;
  690.                 i++;
  691.             }
  692.         }
  693.         return schurTransform;
  694.     }

  695.     /**
  696.      * Performs a division of two complex numbers.
  697.      *
  698.      * @param xr real part of the first number
  699.      * @param xi imaginary part of the first number
  700.      * @param yr real part of the second number
  701.      * @param yi imaginary part of the second number
  702.      * @return result of the complex division
  703.      */
  704.     private Complex cdiv(final double xr, final double xi,
  705.                          final double yr, final double yi) {
  706.         return Complex.ofCartesian(xr, xi).divide(Complex.ofCartesian(yr, yi));
  707.     }

  708.     /**
  709.      * Find eigenvectors from a matrix transformed to Schur form.
  710.      *
  711.      * @param schur the schur transformation of the matrix
  712.      * @throws MathArithmeticException if the Schur form has a norm of zero
  713.      */
  714.     private void findEigenVectorsFromSchur(final SchurTransformer schur)
  715.         throws MathArithmeticException {
  716.         final double[][] matrixT = schur.getT().getData();
  717.         final double[][] matrixP = schur.getP().getData();

  718.         final int n = matrixT.length;

  719.         // compute matrix norm
  720.         double norm = 0.0;
  721.         for (int i = 0; i < n; i++) {
  722.            for (int j = JdkMath.max(i - 1, 0); j < n; j++) {
  723.                norm += JdkMath.abs(matrixT[i][j]);
  724.            }
  725.         }

  726.         // we can not handle a matrix with zero norm
  727.         if (Precision.equals(norm, 0.0, EPSILON)) {
  728.            throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
  729.         }

  730.         // Backsubstitute to find vectors of upper triangular form

  731.         double r = 0.0;
  732.         double s = 0.0;
  733.         double z = 0.0;

  734.         for (int idx = n - 1; idx >= 0; idx--) {
  735.             double p = realEigenvalues[idx];
  736.             double q = imagEigenvalues[idx];

  737.             if (Precision.equals(q, 0.0)) {
  738.                 // Real vector
  739.                 int l = idx;
  740.                 matrixT[idx][idx] = 1.0;
  741.                 for (int i = idx - 1; i >= 0; i--) {
  742.                     double w = matrixT[i][i] - p;
  743.                     r = 0.0;
  744.                     for (int j = l; j <= idx; j++) {
  745.                         r += matrixT[i][j] * matrixT[j][idx];
  746.                     }
  747.                     if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
  748.                         z = w;
  749.                         s = r;
  750.                     } else {
  751.                         l = i;
  752.                         if (Precision.equals(imagEigenvalues[i], 0.0)) {
  753.                             if (w != 0.0) {
  754.                                 matrixT[i][idx] = -r / w;
  755.                             } else {
  756.                                 matrixT[i][idx] = -r / (Precision.EPSILON * norm);
  757.                             }
  758.                         } else {
  759.                             // Solve real equations
  760.                             double x = matrixT[i][i + 1];
  761.                             double y = matrixT[i + 1][i];
  762.                             q = (realEigenvalues[i] - p) * (realEigenvalues[i] - p) +
  763.                                 imagEigenvalues[i] * imagEigenvalues[i];
  764.                             double t = (x * s - z * r) / q;
  765.                             matrixT[i][idx] = t;
  766.                             if (JdkMath.abs(x) > JdkMath.abs(z)) {
  767.                                 matrixT[i + 1][idx] = (-r - w * t) / x;
  768.                             } else {
  769.                                 matrixT[i + 1][idx] = (-s - y * t) / z;
  770.                             }
  771.                         }

  772.                         // Overflow control
  773.                         double t = JdkMath.abs(matrixT[i][idx]);
  774.                         if ((Precision.EPSILON * t) * t > 1) {
  775.                             for (int j = i; j <= idx; j++) {
  776.                                 matrixT[j][idx] /= t;
  777.                             }
  778.                         }
  779.                     }
  780.                 }
  781.             } else if (q < 0.0) {
  782.                 // Complex vector
  783.                 int l = idx - 1;

  784.                 // Last vector component imaginary so matrix is triangular
  785.                 if (JdkMath.abs(matrixT[idx][idx - 1]) > JdkMath.abs(matrixT[idx - 1][idx])) {
  786.                     matrixT[idx - 1][idx - 1] = q / matrixT[idx][idx - 1];
  787.                     matrixT[idx - 1][idx]     = -(matrixT[idx][idx] - p) / matrixT[idx][idx - 1];
  788.                 } else {
  789.                     final Complex result = cdiv(0.0, -matrixT[idx - 1][idx],
  790.                                                 matrixT[idx - 1][idx - 1] - p, q);
  791.                     matrixT[idx - 1][idx - 1] = result.getReal();
  792.                     matrixT[idx - 1][idx]     = result.getImaginary();
  793.                 }

  794.                 matrixT[idx][idx - 1] = 0.0;
  795.                 matrixT[idx][idx]     = 1.0;

  796.                 for (int i = idx - 2; i >= 0; i--) {
  797.                     double ra = 0.0;
  798.                     double sa = 0.0;
  799.                     for (int j = l; j <= idx; j++) {
  800.                         ra += matrixT[i][j] * matrixT[j][idx - 1];
  801.                         sa += matrixT[i][j] * matrixT[j][idx];
  802.                     }
  803.                     double w = matrixT[i][i] - p;

  804.                     if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
  805.                         z = w;
  806.                         r = ra;
  807.                         s = sa;
  808.                     } else {
  809.                         l = i;
  810.                         if (Precision.equals(imagEigenvalues[i], 0.0)) {
  811.                             final Complex c = cdiv(-ra, -sa, w, q);
  812.                             matrixT[i][idx - 1] = c.getReal();
  813.                             matrixT[i][idx] = c.getImaginary();
  814.                         } else {
  815.                             // Solve complex equations
  816.                             double x = matrixT[i][i + 1];
  817.                             double y = matrixT[i + 1][i];
  818.                             double vr = (realEigenvalues[i] - p) * (realEigenvalues[i] - p) +
  819.                                         imagEigenvalues[i] * imagEigenvalues[i] - q * q;
  820.                             final double vi = (realEigenvalues[i] - p) * 2.0 * q;
  821.                             if (Precision.equals(vr, 0.0) && Precision.equals(vi, 0.0)) {
  822.                                 vr = Precision.EPSILON * norm *
  823.                                      (JdkMath.abs(w) + JdkMath.abs(q) + JdkMath.abs(x) +
  824.                                       JdkMath.abs(y) + JdkMath.abs(z));
  825.                             }
  826.                             final Complex c     = cdiv(x * r - z * ra + q * sa,
  827.                                                        x * s - z * sa - q * ra, vr, vi);
  828.                             matrixT[i][idx - 1] = c.getReal();
  829.                             matrixT[i][idx]     = c.getImaginary();

  830.                             if (JdkMath.abs(x) > (JdkMath.abs(z) + JdkMath.abs(q))) {
  831.                                 matrixT[i + 1][idx - 1] = (-ra - w * matrixT[i][idx - 1] +
  832.                                                            q * matrixT[i][idx]) / x;
  833.                                 matrixT[i + 1][idx]     = (-sa - w * matrixT[i][idx] -
  834.                                                            q * matrixT[i][idx - 1]) / x;
  835.                             } else {
  836.                                 final Complex c2        = cdiv(-r - y * matrixT[i][idx - 1],
  837.                                                                -s - y * matrixT[i][idx], z, q);
  838.                                 matrixT[i + 1][idx - 1] = c2.getReal();
  839.                                 matrixT[i + 1][idx]     = c2.getImaginary();
  840.                             }
  841.                         }

  842.                         // Overflow control
  843.                         double t = JdkMath.max(JdkMath.abs(matrixT[i][idx - 1]),
  844.                                                 JdkMath.abs(matrixT[i][idx]));
  845.                         if ((Precision.EPSILON * t) * t > 1) {
  846.                             for (int j = i; j <= idx; j++) {
  847.                                 matrixT[j][idx - 1] /= t;
  848.                                 matrixT[j][idx] /= t;
  849.                             }
  850.                         }
  851.                     }
  852.                 }
  853.             }
  854.         }

  855.         // Back transformation to get eigenvectors of original matrix
  856.         for (int j = n - 1; j >= 0; j--) {
  857.             for (int i = 0; i <= n - 1; i++) {
  858.                 z = 0.0;
  859.                 for (int k = 0; k <= JdkMath.min(j, n - 1); k++) {
  860.                     z += matrixP[i][k] * matrixT[k][j];
  861.                 }
  862.                 matrixP[i][j] = z;
  863.             }
  864.         }

  865.         eigenvectors = new ArrayRealVector[n];
  866.         final double[] tmp = new double[n];
  867.         for (int i = 0; i < n; i++) {
  868.             for (int j = 0; j < n; j++) {
  869.                 tmp[j] = matrixP[j][i];
  870.             }
  871.             eigenvectors[i] = new ArrayRealVector(tmp);
  872.         }
  873.     }
  874. }