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 "qr_decompose.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
Include dependency graph for qr_eigen_values.c:

Macros

#define LIMS   9
 
#define EPSILON   1e-10
 

Functions

void create_matrix (double **A, int N)
 
double ** mat_mul (double **A, double **B, double **OUT, int R1, int C1, int R2, int C2)
 
double eigen_values (double **A, double *eigen_vals, int mat_size, char debug_print)
 
void test1 ()
 
void test2 ()
 
int main (int argc, char **argv)
 

Detailed Description

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

Author
Krishna Vedala

Macro Definition Documentation

◆ EPSILON

#define EPSILON   1e-10

accuracy tolerance limit

◆ LIMS

#define LIMS   9

limit of range of matrix values

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
27 {
28  int i, j, tmp, lim2 = LIMS >> 1;
29 
30 #ifdef _OPENMP
31 #pragma omp for
32 #endif
33  for (i = 0; i < N; i++)
34  {
35  A[i][i] = (rand() % LIMS) - lim2;
36  for (j = i + 1; j < N; j++)
37  {
38  tmp = (rand() % LIMS) - lim2;
39  A[i][j] = tmp;
40  A[j][i] = tmp;
41  }
42  }
43 }

◆ 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]eig_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
106 {
107  double **R = (double **)malloc(sizeof(double *) * mat_size);
108  double **Q = (double **)malloc(sizeof(double *) * mat_size);
109 
110  if (!eigen_vals)
111  {
112  perror("Output eigen value vector cannot be NULL!");
113  return -1;
114  }
115  else if (!Q || !R)
116  {
117  perror("Unable to allocate memory for Q & R!");
118  return -1;
119  }
120 
121  /* allocate dynamic memory for matrices */
122  for (int i = 0; i < mat_size; i++)
123  {
124  R[i] = (double *)malloc(sizeof(double) * mat_size);
125  Q[i] = (double *)malloc(sizeof(double) * mat_size);
126  if (!Q[i] || !R[i])
127  {
128  perror("Unable to allocate memory for Q & R.");
129  return -1;
130  }
131  }
132 
133  if (debug_print)
134  print_matrix(A, mat_size, mat_size);
135 
136  int rows = mat_size, columns = mat_size;
137  int counter = 0, num_eigs = rows - 1;
138  double last_eig = 0;
139 
140  clock_t t1 = clock();
141  while (num_eigs > 0) /* continue till all eigen values are found */
142  {
143  /* iterate with QR decomposition */
144  while (fabs(A[num_eigs][num_eigs - 1]) > EPSILON)
145  {
146  last_eig = A[num_eigs][num_eigs];
147  for (int i = 0; i < rows; i++)
148  A[i][i] -= last_eig; /* A - cI */
149  qr_decompose(A, Q, R, rows, columns);
150 
151  if (debug_print)
152  {
153  print_matrix(A, rows, columns);
154  print_matrix(Q, rows, columns);
155  print_matrix(R, columns, columns);
156  printf("-------------------- %d ---------------------\n",
157  ++counter);
158  }
159 
160  mat_mul(R, Q, A, columns, columns, rows, columns);
161  for (int i = 0; i < rows; i++)
162  A[i][i] += last_eig; /* A + cI */
163  }
164 
165  /* store the converged eigen value */
166  eigen_vals[num_eigs] = last_eig;
167 
168  if (debug_print)
169  {
170  printf("========================\n");
171  printf("Eigen value: % g,\n", last_eig);
172  printf("========================\n");
173  }
174 
175  num_eigs--;
176  rows--;
177  columns--;
178  }
179  eigen_vals[0] = A[0][0];
180  double dtime = (double)(clock() - t1) / CLOCKS_PER_SEC;
181 
182  if (debug_print)
183  {
184  print_matrix(R, mat_size, mat_size);
185  print_matrix(Q, mat_size, mat_size);
186  }
187 
188  /* cleanup dynamic memory */
189  for (int i = 0; i < mat_size; i++)
190  {
191  free(R[i]);
192  free(Q[i]);
193  }
194  free(R);
195  free(Q);
196 
197  return dtime;
198 }
Here is the call graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

main function

302 {
303  srand(time(NULL));
304 
305  int mat_size = 5;
306  if (argc == 2)
307  mat_size = atoi(argv[1]);
308  else
309  { // if invalid input argument is given run tests
310  test1();
311  test2();
312  printf("Usage: ./qr_eigen_values [mat_size]\n");
313  return 0;
314  }
315 
316  if (mat_size < 2)
317  {
318  fprintf(stderr, "Matrix size should be > 2\n");
319  return -1;
320  }
321 
322  int i, rows = mat_size, columns = mat_size;
323 
324  double **A = (double **)malloc(sizeof(double *) * mat_size);
325  /* number of eigen values = matrix size */
326  double *eigen_vals = (double *)malloc(sizeof(double) * mat_size);
327  if (!eigen_vals)
328  {
329  perror("Unable to allocate memory for eigen values!");
330  return -1;
331  }
332  for (i = 0; i < mat_size; i++)
333  {
334  A[i] = (double *)malloc(sizeof(double) * mat_size);
335  }
336 
337  /* create a random matrix */
338  create_matrix(A, mat_size);
339 
340  print_matrix(A, mat_size, mat_size);
341 
342  double dtime = eigen_values(A, eigen_vals, mat_size, 0);
343  printf("Eigen vals: ");
344  for (i = 0; i < mat_size; i++)
345  printf("% 9.4g\t", eigen_vals[i]);
346  printf("\nTime taken to compute: % .4g sec\n", dtime);
347 
348  for (int i = 0; i < mat_size; i++)
349  free(A[i]);
350  free(A);
351  free(eigen_vals);
352  return 0;
353 }
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
60 {
61  if (C1 != R2)
62  {
63  perror("Matrix dimensions mismatch!");
64  return OUT;
65  }
66 
67  int i;
68 #ifdef _OPENMP
69 #pragma omp for
70 #endif
71  for (i = 0; i < R1; i++)
72  for (int j = 0; j < C2; j++)
73  {
74  OUT[i][j] = 0.f;
75  for (int k = 0; k < C1; k++)
76  OUT[i][j] += A[i][k] * B[k][j];
77  }
78  return OUT;
79 }

◆ 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}

209 {
210  int mat_size = 2;
211  double X[][2] = {{5, 7}, {7, 11}};
212  double y[] = {15.56158, 0.384227}; // corresponding y-values
213  double eig_vals[2];
214 
215  // The following steps are to convert a "double[][]" to "double **"
216  double **A = (double **)malloc(mat_size * sizeof(double *));
217  for (int i = 0; i < mat_size; i++)
218  A[i] = X[i];
219 
220  printf("------- Test 1 -------\n");
221 
222  double dtime = eigen_values(A, eig_vals, mat_size, 0);
223 
224  for (int i = 0; i < mat_size; i++)
225  {
226  printf("%d/5 Checking for %.3g --> ", i + 1, y[i]);
227  char result = 0;
228  for (int j = 0; j < mat_size && !result; j++)
229  {
230  if (fabs(y[i] - eig_vals[j]) < 0.1)
231  {
232  result = 1;
233  printf("(%.3g) ", eig_vals[j]);
234  }
235  }
236 
237  // ensure that i^th expected eigen value was computed
238  assert(result != 0);
239  printf("found\n");
240  }
241  printf("Test 1 Passed in %.3g sec\n\n", dtime);
242  free(A);
243 }
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}

257 {
258  int mat_size = 5;
259  double X[][5] = {{-4, 4, 2, 0, -3},
260  {4, -4, 4, -3, -1},
261  {2, 4, 4, 3, -3},
262  {0, -3, 3, -1, -3},
263  {-3, -1, -3, -3, 0}};
264  double y[] = {9.27648, -9.26948, 2.0181, -1.03516,
265  -5.98994}; // corresponding y-values
266  double eig_vals[5];
267 
268  // The following steps are to convert a "double[][]" to "double **"
269  double **A = (double **)malloc(mat_size * sizeof(double *));
270  for (int i = 0; i < mat_size; i++)
271  A[i] = X[i];
272 
273  printf("------- Test 2 -------\n");
274 
275  double dtime = eigen_values(A, eig_vals, mat_size, 0);
276 
277  for (int i = 0; i < mat_size; i++)
278  {
279  printf("%d/5 Checking for %.3g --> ", i + 1, y[i]);
280  char result = 0;
281  for (int j = 0; j < mat_size && !result; j++)
282  {
283  if (fabs(y[i] - eig_vals[j]) < 0.1)
284  {
285  result = 1;
286  printf("(%.3g) ", eig_vals[j]);
287  }
288  }
289 
290  // ensure that i^th expected eigen value was computed
291  assert(result != 0);
292  printf("found\n");
293  }
294  printf("Test 2 Passed in %.3g sec\n\n", dtime);
295  free(A);
296 }
Here is the call graph for this function:
EPSILON
#define EPSILON
Definition: qr_eigen_values.c:19
test2
void test2()
Definition: qr_eigen_values.c:256
N
#define N
Definition: sol1.c:111
qr_decompose
void qr_decompose(double **A, double **Q, double **R, int M, int N)
Definition: qr_decompose.h:146
test1
void test1()
Definition: qr_eigen_values.c:208
print_matrix
void print_matrix(double **A, int M, int N)
Definition: qr_decompose.h:22
mat_mul
double ** mat_mul(double **A, double **B, double **OUT, int R1, int C1, int R2, int C2)
Definition: qr_eigen_values.c:58
LIMS
#define LIMS
Definition: qr_eigen_values.c:18
eigen_values
double eigen_values(double **A, double *eigen_vals, int mat_size, char debug_print)
Definition: qr_eigen_values.c:104
create_matrix
void create_matrix(double **A, int N)
Definition: qr_eigen_values.c:26