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 19 import java.util.Arrays; 20 import java.util.Comparator; 21 22 import org.hipparchus.complex.Complex; 23 24 /** 25 * Given a matrix A, it computes a complex eigen decomposition A = VDV^{T}. 26 * 27 * It ensures that eigen values in the diagonal of D are in ascending order. 28 * 29 */ 30 public class OrderedComplexEigenDecomposition extends ComplexEigenDecomposition { 31 32 /** 33 * Constructor for the decomposition. 34 * 35 * @param matrix real matrix. 36 */ 37 public OrderedComplexEigenDecomposition(final RealMatrix matrix) { 38 this(matrix, 39 ComplexEigenDecomposition.DEFAULT_EIGENVECTORS_EQUALITY, 40 ComplexEigenDecomposition.DEFAULT_EPSILON, 41 ComplexEigenDecomposition.DEFAULT_EPSILON_AV_VD_CHECK); 42 } 43 44 /** 45 * Constructor for decomposition. 46 * <p> 47 * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized 48 * eigenvectors found using inverse iteration are different from each other. 49 * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm 50 * considers it has found again an already known vector, so it drops it and attempts 51 * a new inverse iteration with a different start vector. This value should be 52 * much larger than {@code epsilon} which is used for convergence 53 * </p> 54 * <p> 55 * This constructor calls {@link #OrderedComplexEigenDecomposition(RealMatrix, double, 56 * double, double, Comparator)} with a comparator using real ordering as the primary 57 * sort order and imaginary ordering as the secondary sort order.. 58 * </p> 59 * @param matrix real matrix. 60 * @param eigenVectorsEquality threshold below which eigenvectors are considered equal 61 * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.) 62 * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check 63 * @since 1.9 64 */ 65 public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality, 66 final double epsilon, final double epsilonAVVDCheck) { 67 this(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck, 68 (c1, c2) -> { 69 final int cR = Double.compare(c1.getReal(), c2.getReal()); 70 if (cR == 0) { 71 return Double.compare(c1.getImaginary(), c2.getImaginary()); 72 } else { 73 return cR; 74 } 75 }); 76 } 77 78 /** 79 * Constructor for decomposition. 80 * <p> 81 * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized 82 * eigenvectors found using inverse iteration are different from each other. 83 * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm 84 * considers it has found again an already known vector, so it drops it and attempts 85 * a new inverse iteration with a different start vector. This value should be 86 * much larger than {@code epsilon} which is used for convergence 87 * </p> 88 * @param matrix real matrix. 89 * @param eigenVectorsEquality threshold below which eigenvectors are considered equal 90 * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.) 91 * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check 92 * @param eigenValuesComparator comparator for sorting eigen values 93 * @since 3.0 94 */ 95 public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality, 96 final double epsilon, final double epsilonAVVDCheck, 97 final Comparator<Complex> eigenValuesComparator) { 98 super(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck); 99 final FieldMatrix<Complex> D = this.getD(); 100 final FieldMatrix<Complex> V = this.getV(); 101 102 // getting eigen values 103 IndexedEigenvalue[] eigenValues = new IndexedEigenvalue[D.getRowDimension()]; 104 for (int ij = 0; ij < matrix.getRowDimension(); ij++) { 105 eigenValues[ij] = new IndexedEigenvalue(ij, D.getEntry(ij, ij)); 106 } 107 108 // ordering 109 Arrays.sort(eigenValues, (v1, v2) -> eigenValuesComparator.compare(v1.eigenValue, v2.eigenValue)); 110 for (int ij = 0; ij < matrix.getRowDimension() - 1; ij++) { 111 final IndexedEigenvalue eij = eigenValues[ij]; 112 113 if (ij == eij.index) { 114 continue; 115 } 116 117 // exchanging D 118 final Complex previousValue = D.getEntry(ij, ij); 119 D.setEntry(ij, ij, eij.eigenValue); 120 D.setEntry(eij.index, eij.index, previousValue); 121 122 // exchanging V 123 for (int k = 0; k < matrix.getRowDimension(); ++k) { 124 final Complex previous = V.getEntry(k, ij); 125 V.setEntry(k, ij, V.getEntry(k, eij.index)); 126 V.setEntry(k, eij.index, previous); 127 } 128 129 // exchanging eigenvalue 130 for (int k = ij + 1; k < matrix.getRowDimension(); ++k) { 131 if (eigenValues[k].index == ij) { 132 eigenValues[k].index = eij.index; 133 break; 134 } 135 } 136 } 137 138 // reorder the eigenvalues and eigenvector s array in base class 139 matricesToEigenArrays(); 140 141 checkDefinition(matrix); 142 143 } 144 145 /** {@inheritDoc} */ 146 @Override 147 public FieldMatrix<Complex> getVT() { 148 return getV().transpose(); 149 } 150 151 /** Container for index and eigenvalue pair. */ 152 private static class IndexedEigenvalue { 153 154 /** Index in the diagonal matrix. */ 155 private int index; 156 157 /** Eigenvalue. */ 158 private final Complex eigenValue; 159 160 /** Build the container from its fields. 161 * @param index index in the diagonal matrix 162 * @param eigenvalue eigenvalue 163 */ 164 IndexedEigenvalue(final int index, final Complex eigenvalue) { 165 this.index = index; 166 this.eigenValue = eigenvalue; 167 } 168 169 /** {@inheritDoc} */ 170 @Override 171 public boolean equals(final Object other) { 172 173 if (this == other) { 174 return true; 175 } 176 177 if (other instanceof IndexedEigenvalue) { 178 final IndexedEigenvalue rhs = (IndexedEigenvalue) other; 179 return eigenValue.equals(rhs.eigenValue); 180 } 181 182 return false; 183 184 } 185 186 /** 187 * Get a hashCode for the pair. 188 * @return a hash code value for this object 189 */ 190 @Override 191 public int hashCode() { 192 return 4563 + index + eigenValue.hashCode(); 193 } 194 195 } 196 197 }