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