EigenDecompositionSymmetric.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.linear;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.exception.MathIllegalStateException;
  25. import org.hipparchus.exception.MathRuntimeException;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.Precision;

  28. /**
  29.  * Calculates the eigen decomposition of a symmetric real matrix.
  30.  * <p>
  31.  * The eigen decomposition of matrix A is a set of two matrices:
  32.  * \(V\) and \(D\) such that \(A V = V D\) where $\(A\),
  33.  * \(V\) and \(D\) are all \(m \times m\) matrices.
  34.  * <p>
  35.  * This class is similar in spirit to the {@code EigenvalueDecomposition}
  36.  * class from the <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a>
  37.  * library, with the following changes:
  38.  * </p>
  39.  * <ul>
  40.  *   <li>a {@link #getVT() getVt} method has been added,</li>
  41.  *   <li>a {@link #getEigenvalue(int) getEigenvalue} method to pick up a
  42.  *       single eigenvalue has been added,</li>
  43.  *   <li>a {@link #getEigenvector(int) getEigenvector} method to pick up a
  44.  *       single eigenvector has been added,</li>
  45.  *   <li>a {@link #getDeterminant() getDeterminant} method has been added.</li>
  46.  *   <li>a {@link #getSolver() getSolver} method has been added.</li>
  47.  * </ul>
  48.  * <p>
  49.  * As \(A\) is symmetric, then \(A = V D V^T\) where the eigenvalue matrix \(D\)
  50.  * is diagonal 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.  * The columns of \(V\) represent the eigenvectors in the sense that \(A V = V D\),
  56.  * i.e. {@code A.multiply(V)} equals {@code V.multiply(D)}.
  57.  * The matrix \(V\) may be badly conditioned, or even singular, so the validity of the
  58.  * equation \(A = V D V^{-1}\) depends upon the condition of \(V\).
  59.  * </p>
  60.  * This implementation is based on the paper by A. Drubrulle, R.S. Martin and
  61.  * J.H. Wilkinson "The Implicit QL Algorithm" in Wilksinson and Reinsch (1971)
  62.  * Handbook for automatic computation, vol. 2, Linear algebra, Springer-Verlag,
  63.  * New-York.
  64.  *
  65.  * @see <a href="http://mathworld.wolfram.com/EigenDecomposition.html">MathWorld</a>
  66.  * @see <a href="http://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix">Wikipedia</a>
  67.  */
  68. public class EigenDecompositionSymmetric {

  69.     /** Default epsilon value to use for internal epsilon **/
  70.     public static final double DEFAULT_EPSILON = 1e-12;

  71.     /** Maximum number of iterations accepted in the implicit QL transformation */
  72.     private static final byte MAX_ITER = 30;

  73.     /** Internally used epsilon criteria. */
  74.     private final double epsilon;

  75.     /** Eigenvalues. */
  76.     private double[] eigenvalues;

  77.     /** Eigenvectors. */
  78.     private ArrayRealVector[] eigenvectors;

  79.     /** Cached value of V. */
  80.     private RealMatrix cachedV;

  81.     /** Cached value of D. */
  82.     private DiagonalMatrix cachedD;

  83.     /** Cached value of Vt. */
  84.     private RealMatrix cachedVt;

  85.     /**
  86.      * Calculates the eigen decomposition of the given symmetric real matrix.
  87.      * <p>
  88.      * This constructor uses the {@link #DEFAULT_EPSILON default epsilon} and
  89.      * decreasing order for eigenvalues.
  90.      * </p>
  91.      * @param matrix Matrix to decompose.
  92.      * @throws MathIllegalStateException if the algorithm fails to converge.
  93.      * @throws MathRuntimeException if the decomposition of a general matrix
  94.      * results in a matrix with zero norm
  95.      */
  96.     public EigenDecompositionSymmetric(final RealMatrix matrix) {
  97.         this(matrix, DEFAULT_EPSILON, true);
  98.     }

  99.     /**
  100.      * Calculates the eigen decomposition of the given real matrix.
  101.      * <p>
  102.      * Supports decomposition of a general matrix since 3.1.
  103.      *
  104.      * @param matrix Matrix to decompose.
  105.      * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
  106.      * @param decreasing if true, eigenvalues will be sorted in decreasing order
  107.      * @throws MathIllegalStateException if the algorithm fails to converge.
  108.      * @throws MathRuntimeException if the decomposition of a general matrix
  109.      * results in a matrix with zero norm
  110.      * @since 3.0
  111.      */
  112.     public EigenDecompositionSymmetric(final RealMatrix matrix,
  113.                                        final double epsilon, final boolean decreasing)
  114.         throws MathRuntimeException {

  115.         this.epsilon = epsilon;
  116.         MatrixUtils.checkSymmetric(matrix, epsilon);

  117.         // transform the matrix to tridiagonal
  118.         final TriDiagonalTransformer transformer = new TriDiagonalTransformer(matrix);

  119.         findEigenVectors(transformer.getMainDiagonalRef(),
  120.                          transformer.getSecondaryDiagonalRef(),
  121.                          transformer.getQ().getData(),
  122.                          decreasing);

  123.     }

  124.     /**
  125.      * Calculates the eigen decomposition of the symmetric tridiagonal matrix.
  126.      * <p>
  127.      * The Householder matrix is assumed to be the identity matrix.
  128.      * </p>
  129.      * <p>
  130.      * This constructor uses the {@link #DEFAULT_EPSILON default epsilon} and
  131.      * decreasing order for eigenvalues.
  132.      * </p>
  133.      * @param main Main diagonal of the symmetric tridiagonal form.
  134.      * @param secondary Secondary of the tridiagonal form.
  135.      * @throws MathIllegalStateException if the algorithm fails to converge.
  136.      */
  137.     public EigenDecompositionSymmetric(final double[] main, final double[] secondary) {
  138.         this(main, secondary, DEFAULT_EPSILON, true);
  139.     }

  140.     /**
  141.      * Calculates the eigen decomposition of the symmetric tridiagonal
  142.      * matrix.  The Householder matrix is assumed to be the identity matrix.
  143.      *
  144.      * @param main Main diagonal of the symmetric tridiagonal form.
  145.      * @param secondary Secondary of the tridiagonal form.
  146.      * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
  147.      * @param decreasing if true, eigenvalues will be sorted in decreasing order
  148.      * @throws MathIllegalStateException if the algorithm fails to converge.
  149.      * @since 3.0
  150.      */
  151.     public EigenDecompositionSymmetric(final double[] main, final double[] secondary,
  152.                                        final double epsilon, final boolean decreasing) {
  153.         this.epsilon = epsilon;
  154.         final int size = main.length;
  155.         final double[][] z = new double[size][size];
  156.         for (int i = 0; i < size; i++) {
  157.             z[i][i] = 1.0;
  158.         }
  159.         findEigenVectors(main.clone(), secondary.clone(), z, decreasing);
  160.     }

  161.     /**
  162.      * Gets the matrix V of the decomposition.
  163.      * V is an orthogonal matrix, i.e. its transpose is also its inverse.
  164.      * The columns of V are the eigenvectors of the original matrix.
  165.      * No assumption is made about the orientation of the system axes formed
  166.      * by the columns of V (e.g. in a 3-dimension space, V can form a left-
  167.      * or right-handed system).
  168.      *
  169.      * @return the V matrix.
  170.      */
  171.     public RealMatrix getV() {

  172.         if (cachedV == null) {
  173.             final int m = eigenvectors.length;
  174.             cachedV = MatrixUtils.createRealMatrix(m, m);
  175.             for (int k = 0; k < m; ++k) {
  176.                 cachedV.setColumnVector(k, eigenvectors[k]);
  177.             }
  178.         }
  179.         // return the cached matrix
  180.         return cachedV;
  181.     }

  182.     /**
  183.      * Gets the diagonal matrix D of the decomposition.
  184.      * D is a diagonal matrix.
  185.      * @return the D matrix.
  186.      *
  187.      * @see #getEigenvalues()
  188.       */
  189.     public DiagonalMatrix getD() {

  190.         if (cachedD == null) {
  191.             // cache the matrix for subsequent calls
  192.             cachedD = new DiagonalMatrix(eigenvalues);
  193.         }

  194.         return cachedD;

  195.     }

  196.     /**
  197.      * Get's the value for epsilon which is used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
  198.      *
  199.      * @return the epsilon value.
  200.      */
  201.     public double getEpsilon() { return epsilon; }

  202.     /**
  203.      * Gets the transpose of the matrix V of the decomposition.
  204.      * V is an orthogonal matrix, i.e. its transpose is also its inverse.
  205.      * The columns of V are the eigenvectors of the original matrix.
  206.      * No assumption is made about the orientation of the system axes formed
  207.      * by the columns of V (e.g. in a 3-dimension space, V can form a left-
  208.      * or right-handed system).
  209.      *
  210.      * @return the transpose of the V matrix.
  211.      */
  212.     public RealMatrix getVT() {

  213.         if (cachedVt == null) {
  214.             final int m = eigenvectors.length;
  215.             cachedVt = MatrixUtils.createRealMatrix(m, m);
  216.             for (int k = 0; k < m; ++k) {
  217.                 cachedVt.setRowVector(k, eigenvectors[k]);
  218.             }
  219.         }

  220.         // return the cached matrix
  221.         return cachedVt;
  222.     }

  223.     /**
  224.      * Gets a copy of the eigenvalues of the original matrix.
  225.      *
  226.      * @return a copy of the eigenvalues of the original matrix.
  227.      *
  228.      * @see #getD()
  229.      * @see #getEigenvalue(int)
  230.      */
  231.     public double[] getEigenvalues() {
  232.         return eigenvalues.clone();
  233.     }

  234.     /**
  235.      * Returns the i<sup>th</sup> eigenvalue of the original matrix.
  236.      *
  237.      * @param i index of the eigenvalue (counting from 0)
  238.      * @return real part of the i<sup>th</sup> eigenvalue of the original
  239.      * matrix.
  240.      *
  241.      * @see #getD()
  242.      * @see #getEigenvalues()
  243.      */
  244.     public double getEigenvalue(final int i) {
  245.         return eigenvalues[i];
  246.     }

  247.     /**
  248.      * Gets a copy of the i<sup>th</sup> eigenvector of the original matrix.
  249.      * <p>
  250.      * Note that if the the i<sup>th</sup> is complex this method will throw
  251.      * an exception.
  252.      * </p>
  253.      * @param i Index of the eigenvector (counting from 0).
  254.      * @return a copy of the i<sup>th</sup> eigenvector of the original matrix.
  255.      * @see #getD()
  256.      */
  257.     public RealVector getEigenvector(final int i) {
  258.         return eigenvectors[i].copy();
  259.     }

  260.     /**
  261.      * Computes the determinant of the matrix.
  262.      *
  263.      * @return the determinant of the matrix.
  264.      */
  265.     public double getDeterminant() {
  266.         double determinant = 1;
  267.         for (double eigenvalue : eigenvalues) {
  268.             determinant *= eigenvalue;
  269.         }
  270.         return determinant;
  271.     }

  272.     /**
  273.      * Computes the square-root of the matrix.
  274.      * This implementation assumes that the matrix is positive definite.
  275.      *
  276.      * @return the square-root of the matrix.
  277.      * @throws MathRuntimeException if the matrix is not
  278.      * symmetric or not positive definite.
  279.      */
  280.     public RealMatrix getSquareRoot() {

  281.         final double[] sqrtEigenValues = new double[eigenvalues.length];
  282.         for (int i = 0; i < eigenvalues.length; i++) {
  283.             final double eigen = eigenvalues[i];
  284.             if (eigen <= 0) {
  285.                 throw new MathRuntimeException(LocalizedCoreFormats.UNSUPPORTED_OPERATION);
  286.             }
  287.             sqrtEigenValues[i] = FastMath.sqrt(eigen);
  288.         }
  289.         final RealMatrix sqrtEigen = MatrixUtils.createRealDiagonalMatrix(sqrtEigenValues);
  290.         final RealMatrix v = getV();
  291.         final RealMatrix vT = getVT();

  292.         return v.multiply(sqrtEigen).multiply(vT);

  293.     }

  294.     /** Gets a solver for finding the \(A \times X = B\) solution in exact linear sense.
  295.      * @return a solver
  296.      */
  297.     public DecompositionSolver getSolver() {
  298.         return new Solver();
  299.     }

  300.     /** Specialized solver. */
  301.     private class Solver implements DecompositionSolver {

  302.         /**
  303.          * Solves the linear equation \(A \times X = B\)for symmetric matrices A.
  304.          * <p>
  305.          * This method only finds exact linear solutions, i.e. solutions for
  306.          * which ||A &times; X - B|| is exactly 0.
  307.          * </p>
  308.          *
  309.          * @param b Right-hand side of the equation \(A \times X = B\).
  310.          * @return a Vector X that minimizes the 2-norm of \(A \times X - B\).
  311.          *
  312.          * @throws MathIllegalArgumentException if the matrices dimensions do not match.
  313.          * @throws MathIllegalArgumentException if the decomposed matrix is singular.
  314.          */
  315.         @Override
  316.         public RealVector solve(final RealVector b) {
  317.             if (!isNonSingular()) {
  318.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
  319.             }

  320.             final int m = eigenvalues.length;
  321.             if (b.getDimension() != m) {
  322.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  323.                                                        b.getDimension(), m);
  324.             }

  325.             final double[] bp = new double[m];
  326.             for (int i = 0; i < m; ++i) {
  327.                 final ArrayRealVector v = eigenvectors[i];
  328.                 final double[] vData = v.getDataRef();
  329.                 final double s = v.dotProduct(b) / eigenvalues[i];
  330.                 for (int j = 0; j < m; ++j) {
  331.                     bp[j] += s * vData[j];
  332.                 }
  333.             }

  334.             return new ArrayRealVector(bp, false);
  335.         }

  336.         /** {@inheritDoc} */
  337.         @Override
  338.         public RealMatrix solve(RealMatrix b) {

  339.             if (!isNonSingular()) {
  340.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
  341.             }

  342.             final int m = eigenvalues.length;
  343.             if (b.getRowDimension() != m) {
  344.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  345.                                                        b.getRowDimension(), m);
  346.             }

  347.             final int nColB = b.getColumnDimension();
  348.             final double[][] bp = new double[m][nColB];
  349.             final double[] tmpCol = new double[m];
  350.             for (int k = 0; k < nColB; ++k) {
  351.                 for (int i = 0; i < m; ++i) {
  352.                     tmpCol[i] = b.getEntry(i, k);
  353.                     bp[i][k]  = 0;
  354.                 }
  355.                 for (int i = 0; i < m; ++i) {
  356.                     final ArrayRealVector v = eigenvectors[i];
  357.                     final double[] vData = v.getDataRef();
  358.                     double s = 0;
  359.                     for (int j = 0; j < m; ++j) {
  360.                         s += v.getEntry(j) * tmpCol[j];
  361.                     }
  362.                     s /= eigenvalues[i];
  363.                     for (int j = 0; j < m; ++j) {
  364.                         bp[j][k] += s * vData[j];
  365.                     }
  366.                 }
  367.             }

  368.             return new Array2DRowRealMatrix(bp, false);

  369.         }

  370.         /**
  371.          * Checks whether the decomposed matrix is non-singular.
  372.          *
  373.          * @return true if the decomposed matrix is non-singular.
  374.          */
  375.         @Override
  376.         public boolean isNonSingular() {
  377.             double largestEigenvalueNorm = 0.0;
  378.             // Looping over all values (in case they are not sorted in decreasing
  379.             // order of their norm).
  380.             for (double v : eigenvalues) {
  381.                 largestEigenvalueNorm = FastMath.max(largestEigenvalueNorm, FastMath.abs(v));
  382.             }
  383.             // Corner case: zero matrix, all exactly 0 eigenvalues
  384.             if (largestEigenvalueNorm == 0.0) {
  385.                 return false;
  386.             }
  387.             for (double eigenvalue : eigenvalues) {
  388.                 // Looking for eigenvalues that are 0, where we consider anything much much smaller
  389.                 // than the largest eigenvalue to be effectively 0.
  390.                 if (Precision.equals(FastMath.abs(eigenvalue) / largestEigenvalueNorm, 0, epsilon)) {
  391.                     return false;
  392.                 }
  393.             }
  394.             return true;
  395.         }

  396.         /**
  397.          * Get the inverse of the decomposed matrix.
  398.          *
  399.          * @return the inverse matrix.
  400.          * @throws MathIllegalArgumentException if the decomposed matrix is singular.
  401.          */
  402.         @Override
  403.         public RealMatrix getInverse() {
  404.             if (!isNonSingular()) {
  405.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
  406.             }

  407.             final int m = eigenvalues.length;
  408.             final double[][] invData = new double[m][m];

  409.             for (int i = 0; i < m; ++i) {
  410.                 final double[] invI = invData[i];
  411.                 for (int j = 0; j < m; ++j) {
  412.                     double invIJ = 0;
  413.                     for (int k = 0; k < m; ++k) {
  414.                         final double[] vK = eigenvectors[k].getDataRef();
  415.                         invIJ += vK[i] * vK[j] / eigenvalues[k];
  416.                     }
  417.                     invI[j] = invIJ;
  418.                 }
  419.             }
  420.             return MatrixUtils.createRealMatrix(invData);
  421.         }

  422.         /** {@inheritDoc} */
  423.         @Override
  424.         public int getRowDimension() {
  425.             return eigenvalues.length;
  426.         }

  427.         /** {@inheritDoc} */
  428.         @Override
  429.         public int getColumnDimension() {
  430.             return eigenvalues.length;
  431.         }

  432.     }

  433.     /**
  434.      * Find eigenvalues and eigenvectors (Dubrulle et al., 1971)
  435.      * @param main main diagonal of the tridiagonal matrix
  436.      * @param secondary secondary diagonal of the tridiagonal matrix
  437.      * @param householderMatrix Householder matrix of the transformation
  438.      * @param decreasing if true, eigenvalues will be sorted in decreasing order
  439.      * to tridiagonal form.
  440.      */
  441.     private void findEigenVectors(final double[] main, final double[] secondary,
  442.                                   final double[][] householderMatrix, final boolean decreasing) {
  443.         final double[][]z = householderMatrix.clone();
  444.         final int n = main.length;
  445.         eigenvalues = new double[n];
  446.         final double[] e = new double[n];
  447.         for (int i = 0; i < n - 1; i++) {
  448.             eigenvalues[i] = main[i];
  449.             e[i] = secondary[i];
  450.         }
  451.         eigenvalues[n - 1] = main[n - 1];
  452.         e[n - 1] = 0;

  453.         // Determine the largest main and secondary value in absolute term.
  454.         double maxAbsoluteValue = 0;
  455.         for (int i = 0; i < n; i++) {
  456.             if (FastMath.abs(eigenvalues[i]) > maxAbsoluteValue) {
  457.                 maxAbsoluteValue = FastMath.abs(eigenvalues[i]);
  458.             }
  459.             if (FastMath.abs(e[i]) > maxAbsoluteValue) {
  460.                 maxAbsoluteValue = FastMath.abs(e[i]);
  461.             }
  462.         }
  463.         // Make null any main and secondary value too small to be significant
  464.         if (maxAbsoluteValue != 0) {
  465.             for (int i=0; i < n; i++) {
  466.                 if (FastMath.abs(eigenvalues[i]) <= Precision.EPSILON * maxAbsoluteValue) {
  467.                     eigenvalues[i] = 0;
  468.                 }
  469.                 if (FastMath.abs(e[i]) <= Precision.EPSILON * maxAbsoluteValue) {
  470.                     e[i]=0;
  471.                 }
  472.             }
  473.         }

  474.         for (int j = 0; j < n; j++) {
  475.             int its = 0;
  476.             int m;
  477.             do {
  478.                 for (m = j; m < n - 1; m++) {
  479.                     double delta = FastMath.abs(eigenvalues[m]) +
  480.                         FastMath.abs(eigenvalues[m + 1]);
  481.                     if (FastMath.abs(e[m]) + delta == delta) {
  482.                         break;
  483.                     }
  484.                 }
  485.                 if (m != j) {
  486.                     if (its == MAX_ITER) {
  487.                         throw new MathIllegalStateException(LocalizedCoreFormats.CONVERGENCE_FAILED,
  488.                                                             MAX_ITER);
  489.                     }
  490.                     its++;
  491.                     double q = (eigenvalues[j + 1] - eigenvalues[j]) / (2 * e[j]);
  492.                     double t = FastMath.sqrt(1 + q * q);
  493.                     if (q < 0.0) {
  494.                         q = eigenvalues[m] - eigenvalues[j] + e[j] / (q - t);
  495.                     } else {
  496.                         q = eigenvalues[m] - eigenvalues[j] + e[j] / (q + t);
  497.                     }
  498.                     double u = 0.0;
  499.                     double s = 1.0;
  500.                     double c = 1.0;
  501.                     int i;
  502.                     for (i = m - 1; i >= j; i--) {
  503.                         double p = s * e[i];
  504.                         double h = c * e[i];
  505.                         if (FastMath.abs(p) >= FastMath.abs(q)) {
  506.                             c = q / p;
  507.                             t = FastMath.sqrt(c * c + 1.0);
  508.                             e[i + 1] = p * t;
  509.                             s = 1.0 / t;
  510.                             c *= s;
  511.                         } else {
  512.                             s = p / q;
  513.                             t = FastMath.sqrt(s * s + 1.0);
  514.                             e[i + 1] = q * t;
  515.                             c = 1.0 / t;
  516.                             s *= c;
  517.                         }
  518.                         if (e[i + 1] == 0.0) {
  519.                             eigenvalues[i + 1] -= u;
  520.                             e[m] = 0.0;
  521.                             break;
  522.                         }
  523.                         q = eigenvalues[i + 1] - u;
  524.                         t = (eigenvalues[i] - q) * s + 2.0 * c * h;
  525.                         u = s * t;
  526.                         eigenvalues[i + 1] = q + u;
  527.                         q = c * t - h;
  528.                         for (int ia = 0; ia < n; ia++) {
  529.                             p = z[ia][i + 1];
  530.                             z[ia][i + 1] = s * z[ia][i] + c * p;
  531.                             z[ia][i] = c * z[ia][i] - s * p;
  532.                         }
  533.                     }
  534.                     if (t == 0.0 && i >= j) {
  535.                         continue;
  536.                     }
  537.                     eigenvalues[j] -= u;
  538.                     e[j] = q;
  539.                     e[m] = 0.0;
  540.                 }
  541.             } while (m != j);
  542.         }

  543.         // Sort the eigen values (and vectors) in desired order
  544.         for (int i = 0; i < n; i++) {
  545.             int k = i;
  546.             double p = eigenvalues[i];
  547.             for (int j = i + 1; j < n; j++) {
  548.                 if (eigenvalues[j] > p == decreasing) {
  549.                     k = j;
  550.                     p = eigenvalues[j];
  551.                 }
  552.             }
  553.             if (k != i) {
  554.                 eigenvalues[k] = eigenvalues[i];
  555.                 eigenvalues[i] = p;
  556.                 for (int j = 0; j < n; j++) {
  557.                     p = z[j][i];
  558.                     z[j][i] = z[j][k];
  559.                     z[j][k] = p;
  560.                 }
  561.             }
  562.         }

  563.         // Determine the largest eigen value in absolute term.
  564.         maxAbsoluteValue = 0;
  565.         for (int i = 0; i < n; i++) {
  566.             if (FastMath.abs(eigenvalues[i]) > maxAbsoluteValue) {
  567.                 maxAbsoluteValue = FastMath.abs(eigenvalues[i]);
  568.             }
  569.         }
  570.         // Make null any eigen value too small to be significant
  571.         if (maxAbsoluteValue != 0.0) {
  572.             for (int i=0; i < n; i++) {
  573.                 if (FastMath.abs(eigenvalues[i]) < Precision.EPSILON * maxAbsoluteValue) {
  574.                     eigenvalues[i] = 0;
  575.                 }
  576.             }
  577.         }
  578.         eigenvectors = new ArrayRealVector[n];
  579.         for (int i = 0; i < n; i++) {
  580.             eigenvectors[i] = new ArrayRealVector(n);
  581.             for (int j = 0; j < n; j++) {
  582.                 eigenvectors[i].setEntry(j, z[j][i]);
  583.             }
  584.         }
  585.     }

  586. }