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