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