OrderedComplexEigenDecomposition.java

  1. /*
  2.  * Licensed to the Hipparchus project 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 Hipparchus project 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. package org.hipparchus.linear;

  18. import java.util.Arrays;
  19. import java.util.Comparator;

  20. import org.hipparchus.complex.Complex;

  21. /**
  22.  * Given a matrix A, it computes a complex eigen decomposition A = VDV^{T}.
  23.  *
  24.  * It ensures that eigen values in the diagonal of D are in ascending order.
  25.  *
  26.  */
  27. public class OrderedComplexEigenDecomposition extends ComplexEigenDecomposition {

  28.     /**
  29.      * Constructor for the decomposition.
  30.      *
  31.      * @param matrix real matrix.
  32.      */
  33.     public OrderedComplexEigenDecomposition(final RealMatrix matrix) {
  34.         this(matrix, DEFAULT_EIGENVECTORS_EQUALITY, DEFAULT_EPSILON, DEFAULT_EPSILON_AV_VD_CHECK);
  35.     }

  36.     /**
  37.      * Constructor for decomposition.
  38.      * <p>
  39.      * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized
  40.      * eigenvectors found using inverse iteration are different from each other.
  41.      * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm
  42.      * considers it has found again an already known vector, so it drops it and attempts
  43.      * a new inverse iteration with a different start vector. This value should be
  44.      * much larger than {@code epsilon} which is used for convergence
  45.      * </p>
  46.      * <p>
  47.      * This constructor calls {@link #OrderedComplexEigenDecomposition(RealMatrix, double,
  48.      * double, double, Comparator)} with a comparator using real ordering as the primary
  49.      * sort order and imaginary ordering as the secondary sort order..
  50.      * </p>
  51.      * @param matrix real matrix.
  52.      * @param eigenVectorsEquality threshold below which eigenvectors are considered equal
  53.      * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
  54.      * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check
  55.      * @since 1.9
  56.      */
  57.     public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality,
  58.                                             final double epsilon, final double epsilonAVVDCheck) {
  59.         this(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck,
  60.              (c1, c2) -> {
  61.                  final int cR = Double.compare(c1.getReal(), c2.getReal());
  62.                  if (cR == 0) {
  63.                      return Double.compare(c1.getImaginary(), c2.getImaginary());
  64.                  } else {
  65.                      return cR;
  66.                  }
  67.              });
  68.     }

  69.     /**
  70.      * Constructor for decomposition.
  71.      * <p>
  72.      * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized
  73.      * eigenvectors found using inverse iteration are different from each other.
  74.      * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm
  75.      * considers it has found again an already known vector, so it drops it and attempts
  76.      * a new inverse iteration with a different start vector. This value should be
  77.      * much larger than {@code epsilon} which is used for convergence
  78.      * </p>
  79.      * @param matrix real matrix.
  80.      * @param eigenVectorsEquality threshold below which eigenvectors are considered equal
  81.      * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
  82.      * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check
  83.      * @param eigenValuesComparator comparator for sorting eigen values
  84.      * @since 3.0
  85.      */
  86.     public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality,
  87.                                             final double epsilon, final double epsilonAVVDCheck,
  88.                                             final Comparator<Complex> eigenValuesComparator) {
  89.         super(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck);
  90.         final FieldMatrix<Complex> D = this.getD();
  91.         final FieldMatrix<Complex> V = this.getV();

  92.         // getting eigen values
  93.         IndexedEigenvalue[] eigenValues = new IndexedEigenvalue[D.getRowDimension()];
  94.         for (int ij = 0; ij < matrix.getRowDimension(); ij++) {
  95.             eigenValues[ij] = new IndexedEigenvalue(ij, D.getEntry(ij, ij));
  96.         }

  97.         // ordering
  98.         Arrays.sort(eigenValues, (v1, v2) -> eigenValuesComparator.compare(v1.eigenValue, v2.eigenValue));
  99.         for (int ij = 0; ij < matrix.getRowDimension() - 1; ij++) {
  100.             final IndexedEigenvalue eij = eigenValues[ij];

  101.             if (ij == eij.index) {
  102.                 continue;
  103.             }

  104.             // exchanging D
  105.             final Complex previousValue = D.getEntry(ij, ij);
  106.             D.setEntry(ij, ij, eij.eigenValue);
  107.             D.setEntry(eij.index, eij.index, previousValue);

  108.             // exchanging V
  109.             for (int k = 0; k  < matrix.getRowDimension(); ++k) {
  110.                 final Complex previous = V.getEntry(k, ij);
  111.                 V.setEntry(k, ij, V.getEntry(k, eij.index));
  112.                 V.setEntry(k, eij.index, previous);
  113.             }

  114.             // exchanging eigenvalue
  115.             for (int k = ij + 1; k < matrix.getRowDimension(); ++k) {
  116.                 if (eigenValues[k].index == ij) {
  117.                     eigenValues[k].index = eij.index;
  118.                     break;
  119.                 }
  120.             }
  121.         }

  122.         // reorder the eigenvalues and eigenvector s array in base class
  123.         matricesToEigenArrays();

  124.         checkDefinition(matrix);

  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public FieldMatrix<Complex> getVT() {
  129.         return getV().transpose();
  130.     }

  131.     /** Container for index and eigenvalue pair. */
  132.     private static class IndexedEigenvalue {

  133.         /** Index in the diagonal matrix. */
  134.         private int index;

  135.         /** Eigenvalue. */
  136.         private final Complex eigenValue;

  137.         /** Build the container from its fields.
  138.          * @param index index in the diagonal matrix
  139.          * @param eigenvalue eigenvalue
  140.          */
  141.         IndexedEigenvalue(final int index, final Complex eigenvalue) {
  142.             this.index      = index;
  143.             this.eigenValue = eigenvalue;
  144.         }

  145.         /** {@inheritDoc} */
  146.         @Override
  147.         public boolean equals(final Object other) {

  148.             if (this == other) {
  149.                 return true;
  150.             }

  151.             if (other instanceof IndexedEigenvalue) {
  152.                 final IndexedEigenvalue rhs = (IndexedEigenvalue) other;
  153.                 return eigenValue.equals(rhs.eigenValue);
  154.             }

  155.             return false;

  156.         }

  157.         /**
  158.          * Get a hashCode for the pair.
  159.          * @return a hash code value for this object
  160.          */
  161.         @Override
  162.         public int hashCode() {
  163.             return 4563 + index + eigenValue.hashCode();
  164.         }

  165.     }

  166. }