1*dc9b5c4aSJames Wright // Copyright (c) 2017-2023, Lawrence Livermore National Security, LLC and other CEED contributors. 2*dc9b5c4aSJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3*dc9b5c4aSJames Wright // 4*dc9b5c4aSJames Wright // SPDX-License-Identifier: BSD-2-Clause 5*dc9b5c4aSJames Wright // 6*dc9b5c4aSJames Wright // This file is part of CEED: http://github.com/ceed 7*dc9b5c4aSJames Wright 8*dc9b5c4aSJames Wright /// @file 9*dc9b5c4aSJames Wright /// Eigen system solver for symmetric NxN matrices. Modified from the CC0 code provided at https://github.com/jewettaij/jacobi_pd 10*dc9b5c4aSJames Wright 11*dc9b5c4aSJames Wright #ifndef utils_eigensolver_jacobi_h 12*dc9b5c4aSJames Wright #define utils_eigensolver_jacobi_h 13*dc9b5c4aSJames Wright 14*dc9b5c4aSJames Wright #include <ceed.h> 15*dc9b5c4aSJames Wright #include <math.h> 16*dc9b5c4aSJames Wright 17*dc9b5c4aSJames Wright #include "utils.h" 18*dc9b5c4aSJames Wright 19*dc9b5c4aSJames Wright // @typedef choose the criteria for sorting eigenvalues and eigenvectors 20*dc9b5c4aSJames Wright typedef enum eSortCriteria { 21*dc9b5c4aSJames Wright SORT_NONE, 22*dc9b5c4aSJames Wright SORT_DECREASING_EVALS, 23*dc9b5c4aSJames Wright SORT_INCREASING_EVALS, 24*dc9b5c4aSJames Wright SORT_DECREASING_ABS_EVALS, 25*dc9b5c4aSJames Wright SORT_INCREASING_ABS_EVALS 26*dc9b5c4aSJames Wright } SortCriteria; 27*dc9b5c4aSJames Wright 28*dc9b5c4aSJames Wright ///@brief Find the off-diagonal index in row i whose absolute value is largest 29*dc9b5c4aSJames Wright /// 30*dc9b5c4aSJames Wright /// @param[in] *A matrix 31*dc9b5c4aSJames Wright /// @param[in] i row index 32*dc9b5c4aSJames Wright /// @returns Index of absolute largest off-diagonal element in row i 33*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER CeedInt MaxEntryRow(const CeedScalar *A, CeedInt N, CeedInt i) { 34*dc9b5c4aSJames Wright CeedInt j_max = i + 1; 35*dc9b5c4aSJames Wright for (CeedInt j = i + 2; j < N; j++) 36*dc9b5c4aSJames Wright if (fabs(A[i * N + j]) > fabs(A[i * N + j_max])) j_max = j; 37*dc9b5c4aSJames Wright return j_max; 38*dc9b5c4aSJames Wright } 39*dc9b5c4aSJames Wright 40*dc9b5c4aSJames Wright /// @brief Find the indices (i_max, j_max) marking the location of the 41*dc9b5c4aSJames Wright /// entry in the matrix with the largest absolute value. This 42*dc9b5c4aSJames Wright /// uses the max_idx_row[] array to find the answer in O(n) time. 43*dc9b5c4aSJames Wright /// 44*dc9b5c4aSJames Wright /// @param[in] *A matrix 45*dc9b5c4aSJames Wright /// @param[inout] i_max row index 46*dc9b5c4aSJames Wright /// @param[inout] j_max column index 47*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER void MaxEntry(const CeedScalar *A, CeedInt N, CeedInt *max_idx_row, CeedInt *i_max, CeedInt *j_max) { 48*dc9b5c4aSJames Wright *i_max = 0; 49*dc9b5c4aSJames Wright *j_max = max_idx_row[*i_max]; 50*dc9b5c4aSJames Wright CeedScalar max_entry = fabs(A[*i_max * N + *j_max]); 51*dc9b5c4aSJames Wright for (CeedInt i = 1; i < N - 1; i++) { 52*dc9b5c4aSJames Wright CeedInt j = max_idx_row[i]; 53*dc9b5c4aSJames Wright if (fabs(A[i * N + j]) > max_entry) { 54*dc9b5c4aSJames Wright max_entry = fabs(A[i * N + j]); 55*dc9b5c4aSJames Wright *i_max = i; 56*dc9b5c4aSJames Wright *j_max = j; 57*dc9b5c4aSJames Wright } 58*dc9b5c4aSJames Wright } 59*dc9b5c4aSJames Wright } 60*dc9b5c4aSJames Wright 61*dc9b5c4aSJames Wright /// @brief Calculate the components of a rotation matrix which performs a 62*dc9b5c4aSJames Wright /// rotation in the i,j plane by an angle (θ) that (when multiplied on 63*dc9b5c4aSJames Wright /// both sides) will zero the ij'th element of A, so that afterwards 64*dc9b5c4aSJames Wright /// A[i][j] = 0. The results will be stored in c, s, and t 65*dc9b5c4aSJames Wright /// (which store cos(θ), sin(θ), and tan(θ), respectively). 66*dc9b5c4aSJames Wright /// 67*dc9b5c4aSJames Wright /// @param[in] *A matrix 68*dc9b5c4aSJames Wright /// @param[in] i row index 69*dc9b5c4aSJames Wright /// @param[in] j column index 70*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER void CalcRot(const CeedScalar *A, CeedInt N, CeedInt i, CeedInt j, CeedScalar *rotmat_cst) { 71*dc9b5c4aSJames Wright rotmat_cst[2] = 1.0; // = tan(θ) 72*dc9b5c4aSJames Wright CeedScalar A_jj_ii = (A[j * N + j] - A[i * N + i]); 73*dc9b5c4aSJames Wright if (A_jj_ii != 0.0) { 74*dc9b5c4aSJames Wright // kappa = (A[j][j] - A[i][i]) / (2*A[i][j]) 75*dc9b5c4aSJames Wright CeedScalar kappa = A_jj_ii; 76*dc9b5c4aSJames Wright rotmat_cst[2] = 0.0; 77*dc9b5c4aSJames Wright CeedScalar A_ij = A[i * N + j]; 78*dc9b5c4aSJames Wright if (A_ij != 0.0) { 79*dc9b5c4aSJames Wright kappa /= (2.0 * A_ij); 80*dc9b5c4aSJames Wright // t satisfies: t^2 + 2*t*kappa - 1 = 0 81*dc9b5c4aSJames Wright // (choose the root which has the smaller absolute value) 82*dc9b5c4aSJames Wright rotmat_cst[2] = 1.0 / (sqrt(1 + kappa * kappa) + fabs(kappa)); 83*dc9b5c4aSJames Wright if (kappa < 0.0) rotmat_cst[2] = -rotmat_cst[2]; 84*dc9b5c4aSJames Wright } 85*dc9b5c4aSJames Wright } 86*dc9b5c4aSJames Wright rotmat_cst[0] = 1.0 / sqrt(1 + rotmat_cst[2] * rotmat_cst[2]); 87*dc9b5c4aSJames Wright rotmat_cst[1] = rotmat_cst[0] * rotmat_cst[2]; 88*dc9b5c4aSJames Wright } 89*dc9b5c4aSJames Wright 90*dc9b5c4aSJames Wright /// @brief Perform a similarity transformation by multiplying matrix A on both 91*dc9b5c4aSJames Wright /// sides by a rotation matrix (and its transpose) to eliminate A[i][j]. 92*dc9b5c4aSJames Wright /// @details This rotation matrix performs a rotation in the i,j plane by 93*dc9b5c4aSJames Wright /// angle θ. This function assumes that c=cos(θ). s=sin(θ), t=tan(θ) 94*dc9b5c4aSJames Wright /// have been calculated in advance (using the CalcRot() function). 95*dc9b5c4aSJames Wright /// It also assumes that i<j. The max_idx_row[] array is also updated. 96*dc9b5c4aSJames Wright /// To save time, since the matrix is symmetric, the elements 97*dc9b5c4aSJames Wright /// below the diagonal (ie. A[u][v] where u>v) are not computed. 98*dc9b5c4aSJames Wright /// @verbatim 99*dc9b5c4aSJames Wright /// A' = R^T * A * R 100*dc9b5c4aSJames Wright /// where R the rotation in the i,j plane and ^T denotes the transpose. 101*dc9b5c4aSJames Wright /// i j 102*dc9b5c4aSJames Wright /// _ _ 103*dc9b5c4aSJames Wright /// | 1 | 104*dc9b5c4aSJames Wright /// | . | 105*dc9b5c4aSJames Wright /// | . | 106*dc9b5c4aSJames Wright /// | 1 | 107*dc9b5c4aSJames Wright /// | c ... s | 108*dc9b5c4aSJames Wright /// | . . . | 109*dc9b5c4aSJames Wright /// R = | . 1 . | 110*dc9b5c4aSJames Wright /// | . . . | 111*dc9b5c4aSJames Wright /// | -s ... c | 112*dc9b5c4aSJames Wright /// | 1 | 113*dc9b5c4aSJames Wright /// | . | 114*dc9b5c4aSJames Wright /// | . | 115*dc9b5c4aSJames Wright /// |_ 1 _| 116*dc9b5c4aSJames Wright /// @endverbatim 117*dc9b5c4aSJames Wright /// 118*dc9b5c4aSJames Wright /// Let A' denote the matrix A after multiplication by R^T and R. 119*dc9b5c4aSJames Wright /// The components of A' are: 120*dc9b5c4aSJames Wright /// 121*dc9b5c4aSJames Wright /// @verbatim 122*dc9b5c4aSJames Wright /// A'_uv = Σ_w Σ_z R_wu * A_wz * R_zv 123*dc9b5c4aSJames Wright /// @endverbatim 124*dc9b5c4aSJames Wright /// 125*dc9b5c4aSJames Wright /// Note that a the rotation at location i,j will modify all of the matrix 126*dc9b5c4aSJames Wright /// elements containing at least one index which is either i or j 127*dc9b5c4aSJames Wright /// such as: A[w][i], A[i][w], A[w][j], A[j][w]. 128*dc9b5c4aSJames Wright /// Check and see whether these modified matrix elements exceed the 129*dc9b5c4aSJames Wright /// corresponding values in max_idx_row[] array for that row. 130*dc9b5c4aSJames Wright /// If so, then update max_idx_row for that row. 131*dc9b5c4aSJames Wright /// This is somewhat complicated by the fact that we must only consider 132*dc9b5c4aSJames Wright /// matrix elements in the upper-right triangle strictly above the diagonal. 133*dc9b5c4aSJames Wright /// (ie. matrix elements whose second index is > the first index). 134*dc9b5c4aSJames Wright /// The modified elements we must consider are marked with an "X" below: 135*dc9b5c4aSJames Wright /// 136*dc9b5c4aSJames Wright /// @verbatim 137*dc9b5c4aSJames Wright /// i j 138*dc9b5c4aSJames Wright /// _ _ 139*dc9b5c4aSJames Wright /// | . X X | 140*dc9b5c4aSJames Wright /// | . X X | 141*dc9b5c4aSJames Wright /// | . X X | 142*dc9b5c4aSJames Wright /// | . X X | 143*dc9b5c4aSJames Wright /// | X X X X X 0 X X X X | i 144*dc9b5c4aSJames Wright /// | . X | 145*dc9b5c4aSJames Wright /// | . X | 146*dc9b5c4aSJames Wright /// A = | . X | 147*dc9b5c4aSJames Wright /// | . X | 148*dc9b5c4aSJames Wright /// | X X X X X | j 149*dc9b5c4aSJames Wright /// | . | 150*dc9b5c4aSJames Wright /// | . | 151*dc9b5c4aSJames Wright /// | . | 152*dc9b5c4aSJames Wright /// |_ . _| 153*dc9b5c4aSJames Wright /// @endverbatim 154*dc9b5c4aSJames Wright /// 155*dc9b5c4aSJames Wright /// @param[in] *A matrix 156*dc9b5c4aSJames Wright /// @param[in] i row index 157*dc9b5c4aSJames Wright /// @param[in] j column index 158*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER void ApplyRot(CeedScalar *A, CeedInt N, CeedInt i, CeedInt j, CeedInt *max_idx_row, CeedScalar *rotmat_cst) { 159*dc9b5c4aSJames Wright // Compute the diagonal elements of A which have changed: 160*dc9b5c4aSJames Wright A[i * N + i] -= rotmat_cst[2] * A[i * N + j]; 161*dc9b5c4aSJames Wright A[j * N + j] += rotmat_cst[2] * A[i * N + j]; 162*dc9b5c4aSJames Wright // Note: This is algebraically equivalent to: 163*dc9b5c4aSJames Wright // A[i][i] = c*c*A[i][i] + s*s*A[j][j] - 2*s*c*A[i][j] 164*dc9b5c4aSJames Wright // A[j][j] = s*s*A[i][i] + c*c*A[j][j] + 2*s*c*A[i][j] 165*dc9b5c4aSJames Wright 166*dc9b5c4aSJames Wright // Update the off-diagonal elements of A which will change (above the diagonal) 167*dc9b5c4aSJames Wright 168*dc9b5c4aSJames Wright A[i * N + j] = 0.0; 169*dc9b5c4aSJames Wright 170*dc9b5c4aSJames Wright // compute A[w][i] and A[i][w] for all w!=i,considering above-diagonal elements 171*dc9b5c4aSJames Wright for (CeedInt w = 0; w < i; w++) { // 0 <= w < i < j < N 172*dc9b5c4aSJames Wright A[i * N + w] = A[w * N + i]; // backup the previous value. store below diagonal (i>w) 173*dc9b5c4aSJames Wright A[w * N + i] = rotmat_cst[0] * A[w * N + i] - rotmat_cst[1] * A[w * N + j]; // A[w][i], A[w][j] from previous iteration 174*dc9b5c4aSJames Wright if (i == max_idx_row[w]) max_idx_row[w] = MaxEntryRow(A, N, w); 175*dc9b5c4aSJames Wright else if (fabs(A[w * N + i]) > fabs(A[w * N + max_idx_row[w]])) max_idx_row[w] = i; 176*dc9b5c4aSJames Wright } 177*dc9b5c4aSJames Wright for (CeedInt w = i + 1; w < j; w++) { // 0 <= i < w < j < N 178*dc9b5c4aSJames Wright A[w * N + i] = A[i * N + w]; // backup the previous value. store below diagonal (w>i) 179*dc9b5c4aSJames Wright A[i * N + w] = rotmat_cst[0] * A[i * N + w] - rotmat_cst[1] * A[w * N + j]; // A[i][w], A[w][j] from previous iteration 180*dc9b5c4aSJames Wright } 181*dc9b5c4aSJames Wright for (CeedInt w = j + 1; w < N; w++) { // 0 <= i < j+1 <= w < N 182*dc9b5c4aSJames Wright A[w * N + i] = A[i * N + w]; // backup the previous value. store below diagonal (w>i) 183*dc9b5c4aSJames Wright A[i * N + w] = rotmat_cst[0] * A[i * N + w] - rotmat_cst[1] * A[j * N + w]; // A[i][w], A[j][w] from previous iteration 184*dc9b5c4aSJames Wright } 185*dc9b5c4aSJames Wright 186*dc9b5c4aSJames Wright // now that we're done modifying row i, we can update max_idx_row[i] 187*dc9b5c4aSJames Wright max_idx_row[i] = MaxEntryRow(A, N, i); 188*dc9b5c4aSJames Wright 189*dc9b5c4aSJames Wright // compute A[w][j] and A[j][w] for all w!=j,considering above-diagonal elements 190*dc9b5c4aSJames Wright for (CeedInt w = 0; w < i; w++) { // 0 <= w < i < j < N 191*dc9b5c4aSJames Wright A[w * N + j] = rotmat_cst[1] * A[i * N + w] + rotmat_cst[0] * A[w * N + j]; // A[i][w], A[w][j] from previous iteration 192*dc9b5c4aSJames Wright if (j == max_idx_row[w]) max_idx_row[w] = MaxEntryRow(A, N, w); 193*dc9b5c4aSJames Wright else if (fabs(A[w * N + j]) > fabs(A[w * N + max_idx_row[w]])) max_idx_row[w] = j; 194*dc9b5c4aSJames Wright } 195*dc9b5c4aSJames Wright for (CeedInt w = i + 1; w < j; w++) { // 0 <= i+1 <= w < j < N 196*dc9b5c4aSJames Wright A[w * N + j] = rotmat_cst[1] * A[w * N + i] + rotmat_cst[0] * A[w * N + j]; // A[w][i], A[w][j] from previous iteration 197*dc9b5c4aSJames Wright if (j == max_idx_row[w]) max_idx_row[w] = MaxEntryRow(A, N, w); 198*dc9b5c4aSJames Wright else if (fabs(A[w * N + j]) > fabs(A[w * N + max_idx_row[w]])) max_idx_row[w] = j; 199*dc9b5c4aSJames Wright } 200*dc9b5c4aSJames Wright for (CeedInt w = j + 1; w < N; w++) { // 0 <= i < j < w < N 201*dc9b5c4aSJames Wright A[j * N + w] = rotmat_cst[1] * A[w * N + i] + rotmat_cst[0] * A[j * N + w]; // A[w][i], A[j][w] from previous iteration 202*dc9b5c4aSJames Wright } 203*dc9b5c4aSJames Wright // now that we're done modifying row j, we can update max_idx_row[j] 204*dc9b5c4aSJames Wright max_idx_row[j] = MaxEntryRow(A, N, j); 205*dc9b5c4aSJames Wright } 206*dc9b5c4aSJames Wright 207*dc9b5c4aSJames Wright ///@brief Multiply matrix A on the LEFT side by a transposed rotation matrix R^T 208*dc9b5c4aSJames Wright /// This matrix performs a rotation in the i,j plane by angle θ (where 209*dc9b5c4aSJames Wright /// the arguments "s" and "c" refer to cos(θ) and sin(θ), respectively). 210*dc9b5c4aSJames Wright /// @verbatim 211*dc9b5c4aSJames Wright /// A'_uv = Σ_w R_wu * A_wv 212*dc9b5c4aSJames Wright /// @endverbatim 213*dc9b5c4aSJames Wright /// 214*dc9b5c4aSJames Wright /// @param[in] *A matrix 215*dc9b5c4aSJames Wright /// @param[in] i row index 216*dc9b5c4aSJames Wright /// @param[in] j column index 217*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER void ApplyRotLeft(CeedScalar *A, CeedInt N, CeedInt i, CeedInt j, CeedScalar *rotmat_cst) { 218*dc9b5c4aSJames Wright // Recall that c = cos(θ) and s = sin(θ) 219*dc9b5c4aSJames Wright for (CeedInt v = 0; v < N; v++) { 220*dc9b5c4aSJames Wright CeedScalar Aiv = A[i * N + v]; 221*dc9b5c4aSJames Wright A[i * N + v] = rotmat_cst[0] * A[i * N + v] - rotmat_cst[1] * A[j * N + v]; 222*dc9b5c4aSJames Wright A[j * N + v] = rotmat_cst[1] * Aiv + rotmat_cst[0] * A[j * N + v]; 223*dc9b5c4aSJames Wright } 224*dc9b5c4aSJames Wright } 225*dc9b5c4aSJames Wright 226*dc9b5c4aSJames Wright /// @brief Sort the rows in evec according to the numbers in v (also sorted) 227*dc9b5c4aSJames Wright /// 228*dc9b5c4aSJames Wright /// @param[inout] *eval vector containing the keys used for sorting 229*dc9b5c4aSJames Wright /// @param[inout] *evec matrix whose rows will be sorted according to v 230*dc9b5c4aSJames Wright /// @param[in] n size of the vector and matrix 231*dc9b5c4aSJames Wright /// @param[in] s sort decreasing order? 232*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER void SortRows(CeedScalar *eval, CeedScalar *evec, CeedInt N, SortCriteria sort_criteria) { 233*dc9b5c4aSJames Wright if (sort_criteria == SORT_NONE) return; 234*dc9b5c4aSJames Wright 235*dc9b5c4aSJames Wright for (CeedInt i = 0; i < N - 1; i++) { 236*dc9b5c4aSJames Wright CeedInt i_max = i; 237*dc9b5c4aSJames Wright for (CeedInt j = i + 1; j < N; j++) { 238*dc9b5c4aSJames Wright // find the "maximum" element in the array starting at position i+1 239*dc9b5c4aSJames Wright switch (sort_criteria) { 240*dc9b5c4aSJames Wright case SORT_DECREASING_EVALS: 241*dc9b5c4aSJames Wright if (eval[j] > eval[i_max]) i_max = j; 242*dc9b5c4aSJames Wright break; 243*dc9b5c4aSJames Wright case SORT_INCREASING_EVALS: 244*dc9b5c4aSJames Wright if (eval[j] < eval[i_max]) i_max = j; 245*dc9b5c4aSJames Wright break; 246*dc9b5c4aSJames Wright case SORT_DECREASING_ABS_EVALS: 247*dc9b5c4aSJames Wright if (fabs(eval[j]) > fabs(eval[i_max])) i_max = j; 248*dc9b5c4aSJames Wright break; 249*dc9b5c4aSJames Wright case SORT_INCREASING_ABS_EVALS: 250*dc9b5c4aSJames Wright if (fabs(eval[j]) < fabs(eval[i_max])) i_max = j; 251*dc9b5c4aSJames Wright break; 252*dc9b5c4aSJames Wright default: 253*dc9b5c4aSJames Wright break; 254*dc9b5c4aSJames Wright } 255*dc9b5c4aSJames Wright } 256*dc9b5c4aSJames Wright SwapScalar(&eval[i], &eval[i_max]); 257*dc9b5c4aSJames Wright for (CeedInt k = 0; k < N; k++) SwapScalar(&evec[i * N + k], &evec[i_max * N + k]); 258*dc9b5c4aSJames Wright } 259*dc9b5c4aSJames Wright } 260*dc9b5c4aSJames Wright 261*dc9b5c4aSJames Wright /// @brief Calculate all the eigenvalues and eigevectors of a symmetric matrix 262*dc9b5c4aSJames Wright /// using the Jacobi eigenvalue algorithm: 263*dc9b5c4aSJames Wright /// https://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm 264*dc9b5c4aSJames Wright /// @returns The number of Jacobi iterations attempted, which should be > 0. 265*dc9b5c4aSJames Wright /// If the return value is not strictly > 0 then convergence failed. 266*dc9b5c4aSJames Wright /// @note To reduce the computation time further, set calc_evecs=false. 267*dc9b5c4aSJames Wright /// Additionally, note that the output evecs should be normalized. It 268*dc9b5c4aSJames Wright /// simply takes the Identity matrix and performs (isometric) rotations 269*dc9b5c4aSJames Wright /// on it, so divergence from normalized is due to finite-precision 270*dc9b5c4aSJames Wright /// arithmetic of the rotations. 271*dc9b5c4aSJames Wright // 272*dc9b5c4aSJames Wright // @param[in] A the matrix you wish to diagonalize (size NxN) 273*dc9b5c4aSJames Wright // @param[in] N size of the matrix 274*dc9b5c4aSJames Wright // @param[out] eval store the eigenvalues here (size N) 275*dc9b5c4aSJames Wright // @param[out] evec store the eigenvectors here (in rows, size NxN) 276*dc9b5c4aSJames Wright // @param[out] max_idx_row work vector of size N-1 277*dc9b5c4aSJames Wright // @param[in] sort_criteria sort results? 278*dc9b5c4aSJames Wright // @param[in] calc_evecs calculate the eigenvectors? 279*dc9b5c4aSJames Wright // @param[in] max_num_sweeps maximum number of iterations = max_num_sweeps * number of off-diagonals (N*(N-1)/2) 280*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER CeedInt Diagonalize(CeedScalar *A, CeedInt N, CeedScalar *eval, CeedScalar *evec, CeedInt *max_idx_row, 281*dc9b5c4aSJames Wright SortCriteria sort_criteria, bool calc_evec, const CeedInt max_num_sweeps) { 282*dc9b5c4aSJames Wright CeedScalar rotmat_cst[3] = {0.}; // cos(θ), sin(θ), and tan(θ), 283*dc9b5c4aSJames Wright 284*dc9b5c4aSJames Wright if (calc_evec) 285*dc9b5c4aSJames Wright for (CeedInt i = 0; i < N; i++) 286*dc9b5c4aSJames Wright for (CeedInt j = 0; j < N; j++) evec[i * N + j] = (i == j) ? 1.0 : 0.0; // Set evec equal to the identity matrix 287*dc9b5c4aSJames Wright 288*dc9b5c4aSJames Wright for (CeedInt i = 0; i < N - 1; i++) max_idx_row[i] = MaxEntryRow(A, N, i); 289*dc9b5c4aSJames Wright 290*dc9b5c4aSJames Wright // -- Iteration -- 291*dc9b5c4aSJames Wright CeedInt n_iters; 292*dc9b5c4aSJames Wright CeedInt max_num_iters = max_num_sweeps * N * (N - 1) / 2; 293*dc9b5c4aSJames Wright for (n_iters = 1; n_iters <= max_num_iters; n_iters++) { 294*dc9b5c4aSJames Wright CeedInt i, j; 295*dc9b5c4aSJames Wright MaxEntry(A, N, max_idx_row, &i, &j); 296*dc9b5c4aSJames Wright 297*dc9b5c4aSJames Wright // If A[i][j] is small compared to A[i][i] and A[j][j], set it to 0. 298*dc9b5c4aSJames Wright if ((A[i * N + i] + A[i * N + j] == A[i * N + i]) && (A[j * N + j] + A[i * N + j] == A[j * N + j])) { 299*dc9b5c4aSJames Wright A[i * N + j] = 0.0; 300*dc9b5c4aSJames Wright max_idx_row[i] = MaxEntryRow(A, N, i); 301*dc9b5c4aSJames Wright } 302*dc9b5c4aSJames Wright 303*dc9b5c4aSJames Wright if (A[i * N + j] == 0.0) break; 304*dc9b5c4aSJames Wright 305*dc9b5c4aSJames Wright CalcRot(A, N, i, j, rotmat_cst); // Calculate the parameters of the rotation matrix. 306*dc9b5c4aSJames Wright ApplyRot(A, N, i, j, max_idx_row, rotmat_cst); // Apply this rotation to the A matrix. 307*dc9b5c4aSJames Wright if (calc_evec) ApplyRotLeft(evec, N, i, j, rotmat_cst); 308*dc9b5c4aSJames Wright } 309*dc9b5c4aSJames Wright 310*dc9b5c4aSJames Wright for (CeedInt i = 0; i < N; i++) eval[i] = A[i * N + i]; 311*dc9b5c4aSJames Wright 312*dc9b5c4aSJames Wright // Optional: Sort results by eigenvalue. 313*dc9b5c4aSJames Wright SortRows(eval, evec, N, sort_criteria); 314*dc9b5c4aSJames Wright 315*dc9b5c4aSJames Wright if ((n_iters > max_num_iters) && (N > 1)) // If we exceeded max_num_iters, 316*dc9b5c4aSJames Wright return 0; // indicate an error occured. 317*dc9b5c4aSJames Wright 318*dc9b5c4aSJames Wright return n_iters; 319*dc9b5c4aSJames Wright } 320*dc9b5c4aSJames Wright 321*dc9b5c4aSJames Wright // @brief Interface to Diagonalize for 3x3 systems 322*dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER CeedInt Diagonalize3(CeedScalar A[3][3], CeedScalar eval[3], CeedScalar evec[3][3], CeedInt max_idx_row[3], 323*dc9b5c4aSJames Wright SortCriteria sort_criteria, bool calc_evec, const CeedInt max_num_sweeps) { 324*dc9b5c4aSJames Wright return Diagonalize((CeedScalar *)A, 3, (CeedScalar *)eval, (CeedScalar *)evec, (CeedInt *)max_idx_row, sort_criteria, calc_evec, max_num_sweeps); 325*dc9b5c4aSJames Wright } 326*dc9b5c4aSJames Wright 327*dc9b5c4aSJames Wright #endif // utils_eigensolver_jacobi_h 328