Algorithms_in_C  1.0.0
Set of algorithms implemented in C.
qr_eigen_values.c File Reference

Compute real eigen values and eigen vectors of a symmetric matrix using QR decomposition method. More...

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "qr_decompose.h"
Include dependency graph for qr_eigen_values.c:

Macros

#define LIMS   9
 limit of range of matrix values
 
#define EPSILON   1e-10
 accuracy tolerance limit
 

Functions

void create_matrix (double **A, int N)
 create a square matrix of given size with random elements More...
 
double ** mat_mul (double **A, double **B, double **OUT, int R1, int C1, int R2, int C2)
 Perform multiplication of two matrices. More...
 
double eigen_values (double **A, double *eigen_vals, int mat_size, char debug_print)
 Compute eigen values using iterative shifted QR decomposition algorithm as follows: More...
 
void test1 ()
 test function to compute eigen values of a 2x2 matrix More...
 
void test2 ()
 test function to compute eigen values of a 2x2 matrix More...
 
int main (int argc, char **argv)
 main function
 

Detailed Description

Compute real eigen values and eigen vectors of a symmetric matrix using QR decomposition method.

Author
Krishna Vedala

Function Documentation

◆ create_matrix()

void create_matrix ( double **  A,
int  N 
)

create a square matrix of given size with random elements

Parameters
[out]Amatrix to create (must be pre-allocated in memory)
[in]Nmatrix size
28 {
29  int i, j, tmp, lim2 = LIMS >> 1;
30 
31 #ifdef _OPENMP
32 #pragma omp for
33 #endif
34  for (i = 0; i < N; i++)
35  {
36  A[i][i] = (rand() % LIMS) - lim2;
37  for (j = i + 1; j < N; j++)
38  {
39  tmp = (rand() % LIMS) - lim2;
40  A[i][j] = tmp;
41  A[j][i] = tmp;
42  }
43  }
44 }

◆ eigen_values()

double eigen_values ( double **  A,
double *  eigen_vals,
int  mat_size,
char  debug_print 
)

Compute eigen values using iterative shifted QR decomposition algorithm as follows:

  1. Use last diagonal element of A as eigen value approximation \(c\)
  2. Shift diagonals of matrix \(A' = A - cI\)
  3. Decompose matrix \(A'=QR\)
  4. Compute next approximation \(A'_1 = RQ \)
  5. Shift diagonals back \(A_1 = A'_1 + cI\)
  6. Termination condition check: last element below diagonal is almost 0
    1. If not 0, go back to step 1 with the new approximation \(A_1\)
    2. If 0, continue to step 7
  7. Save last known \(c\) as the eigen value.
  8. Are all eigen values found?
    1. If not, remove last row and column of \(A_1\) and go back to step 1.
    2. If yes, stop.
Note
The matrix \(A\) gets modified
Parameters
[in,out]Amatrix to compute eigen values for
[out]eigen_valsresultant vector containing computed eigen values
[in]mat_sizematrix size
[in]debug_print1 to print intermediate Q & R matrices, 0 for not to
Returns
time for computation in seconds
108 {
109  if (!eigen_vals)
110  {
111  perror("Output eigen value vector cannot be NULL!");
112  return -1;
113  }
114  double **R = (double **)malloc(sizeof(double *) * mat_size);
115  double **Q = (double **)malloc(sizeof(double *) * mat_size);
116  if (!Q || !R)
117  {
118  perror("Unable to allocate memory for Q & R!");
119  if (Q)
120  {
121  free(Q);
122  }
123  if (R)
124  {
125  free(R);
126  }
127  return -1;
128  }
129 
130  /* allocate dynamic memory for matrices */
131  for (int i = 0; i < mat_size; i++)
132  {
133  R[i] = (double *)malloc(sizeof(double) * mat_size);
134  Q[i] = (double *)malloc(sizeof(double) * mat_size);
135  if (!Q[i] || !R[i])
136  {
137  perror("Unable to allocate memory for Q & R.");
138  for (; i >= 0; i--)
139  {
140  free(R[i]);
141  free(Q[i]);
142  }
143  free(Q);
144  free(R);
145  return -1;
146  }
147  }
148 
149  if (debug_print)
150  {
151  print_matrix(A, mat_size, mat_size);
152  }
153 
154  int rows = mat_size, columns = mat_size;
155  int counter = 0, num_eigs = rows - 1;
156  double last_eig = 0;
157 
158  clock_t t1 = clock();
159  while (num_eigs > 0) /* continue till all eigen values are found */
160  {
161  /* iterate with QR decomposition */
162  while (fabs(A[num_eigs][num_eigs - 1]) > EPSILON)
163  {
164  last_eig = A[num_eigs][num_eigs];
165  for (int i = 0; i < rows; i++) A[i][i] -= last_eig; /* A - cI */
166  qr_decompose(A, Q, R, rows, columns);
167 
168  if (debug_print)
169  {
170  print_matrix(A, rows, columns);
171  print_matrix(Q, rows, columns);
172  print_matrix(R, columns, columns);
173  printf("-------------------- %d ---------------------\n",
174  ++counter);
175  }
176 
177  mat_mul(R, Q, A, columns, columns, rows, columns);
178  for (int i = 0; i < rows; i++) A[i][i] += last_eig; /* A + cI */
179  }
180 
181  /* store the converged eigen value */
182  eigen_vals[num_eigs] = last_eig;
183 
184  if (debug_print)
185  {
186  printf("========================\n");
187  printf("Eigen value: % g,\n", last_eig);
188  printf("========================\n");
189  }
190 
191  num_eigs--;
192  rows--;
193  columns--;
194  }
195  eigen_vals[0] = A[0][0];
196  double dtime = (double)(clock() - t1) / CLOCKS_PER_SEC;
197 
198  if (debug_print)
199  {
200  print_matrix(R, mat_size, mat_size);
201  print_matrix(Q, mat_size, mat_size);
202  }
203 
204  /* cleanup dynamic memory */
205  for (int i = 0; i < mat_size; i++)
206  {
207  free(R[i]);
208  free(Q[i]);
209  }
210  free(R);
211  free(Q);
212 
213  return dtime;
214 }
Here is the call graph for this function:

◆ mat_mul()

double** mat_mul ( double **  A,
double **  B,
double **  OUT,
int  R1,
int  C1,
int  R2,
int  C2 
)

Perform multiplication of two matrices.

  • R2 must be equal to C1
  • Resultant matrix size should be R1xC2
    Parameters
    [in]Afirst matrix to multiply
    [in]Bsecond matrix to multiply
    [out]OUToutput matrix (must be pre-allocated)
    [in]R1number of rows of first matrix
    [in]C1number of columns of first matrix
    [in]R2number of rows of second matrix
    [in]C2number of columns of second matrix
    Returns
    pointer to resultant matrix
61 {
62  if (C1 != R2)
63  {
64  perror("Matrix dimensions mismatch!");
65  return OUT;
66  }
67 
68  int i;
69 #ifdef _OPENMP
70 #pragma omp for
71 #endif
72  for (i = 0; i < R1; i++)
73  {
74  for (int j = 0; j < C2; j++)
75  {
76  OUT[i][j] = 0.f;
77  for (int k = 0; k < C1; k++) OUT[i][j] += A[i][k] * B[k][j];
78  }
79  }
80  return OUT;
81 }

◆ test1()

void test1 ( )

test function to compute eigen values of a 2x2 matrix

\[\begin{bmatrix} 5 & 7\\ 7 & 11 \end{bmatrix}\]

which are approximately, {15.56158, 0.384227}

225 {
226  int mat_size = 2;
227  double X[][2] = {{5, 7}, {7, 11}};
228  double y[] = {15.56158, 0.384227}; // corresponding y-values
229  double eig_vals[2] = {0, 0};
230 
231  // The following steps are to convert a "double[][]" to "double **"
232  double **A = (double **)malloc(mat_size * sizeof(double *));
233  for (int i = 0; i < mat_size; i++) A[i] = X[i];
234 
235  printf("------- Test 1 -------\n");
236 
237  double dtime = eigen_values(A, eig_vals, mat_size, 0);
238 
239  for (int i = 0; i < mat_size; i++)
240  {
241  printf("%d/5 Checking for %.3g --> ", i + 1, y[i]);
242  char result = 0;
243  for (int j = 0; j < mat_size && !result; j++)
244  {
245  if (fabs(y[i] - eig_vals[j]) < 0.1)
246  {
247  result = 1;
248  printf("(%.3g) ", eig_vals[j]);
249  }
250  }
251 
252  // ensure that i^th expected eigen value was computed
253  assert(result != 0);
254  printf("found\n");
255  }
256  printf("Test 1 Passed in %.3g sec\n\n", dtime);
257  free(A);
258 }
Here is the call graph for this function:

◆ test2()

void test2 ( )

test function to compute eigen values of a 2x2 matrix

\[\begin{bmatrix} -4& 4& 2& 0& -3\\ 4& -4& 4& -3& -1\\ 2& 4& 4& 3& -3\\ 0& -3& 3& -1&-1\\ -3& -1& -3& -3& 0 \end{bmatrix}\]

which are approximately, {9.27648, -9.26948, 2.0181, -1.03516, -5.98994}

272 {
273  int mat_size = 5;
274  double X[][5] = {{-4, 4, 2, 0, -3},
275  {4, -4, 4, -3, -1},
276  {2, 4, 4, 3, -3},
277  {0, -3, 3, -1, -3},
278  {-3, -1, -3, -3, 0}};
279  double y[] = {9.27648, -9.26948, 2.0181, -1.03516,
280  -5.98994}; // corresponding y-values
281  double eig_vals[5];
282 
283  // The following steps are to convert a "double[][]" to "double **"
284  double **A = (double **)malloc(mat_size * sizeof(double *));
285  for (int i = 0; i < mat_size; i++) A[i] = X[i];
286 
287  printf("------- Test 2 -------\n");
288 
289  double dtime = eigen_values(A, eig_vals, mat_size, 0);
290 
291  for (int i = 0; i < mat_size; i++)
292  {
293  printf("%d/5 Checking for %.3g --> ", i + 1, y[i]);
294  char result = 0;
295  for (int j = 0; j < mat_size && !result; j++)
296  {
297  if (fabs(y[i] - eig_vals[j]) < 0.1)
298  {
299  result = 1;
300  printf("(%.3g) ", eig_vals[j]);
301  }
302  }
303 
304  // ensure that i^th expected eigen value was computed
305  assert(result != 0);
306  printf("found\n");
307  }
308  printf("Test 2 Passed in %.3g sec\n\n", dtime);
309  free(A);
310 }
Here is the call graph for this function:
EPSILON
#define EPSILON
accuracy tolerance limit
Definition: qr_eigen_values.c:20
qr_decompose
void qr_decompose(double **A, double **Q, double **R, int M, int N)
Decompose matrix using Gram-Schmidt process.
Definition: qr_decompose.h:142
print_matrix
void print_matrix(double **A, int M, int N)
function to display matrix on stdout
Definition: qr_decompose.h:22
mat_mul
double ** mat_mul(double **A, double **B, double **OUT, int R1, int C1, int R2, int C2)
Perform multiplication of two matrices.
Definition: qr_eigen_values.c:59
LIMS
#define LIMS
limit of range of matrix values
Definition: qr_eigen_values.c:19
eigen_values
double eigen_values(double **A, double *eigen_vals, int mat_size, char debug_print)
Compute eigen values using iterative shifted QR decomposition algorithm as follows:
Definition: qr_eigen_values.c:106