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
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++) A[i][i] -= last_eig; /* A - cI */
148  qr_decompose(A, Q, R, rows, columns);
149 
150  if (debug_print)
151  {
152  print_matrix(A, rows, columns);
153  print_matrix(Q, rows, columns);
154  print_matrix(R, columns, columns);
155  printf("-------------------- %d ---------------------\n",
156  ++counter);
157  }
158 
159  mat_mul(R, Q, A, columns, columns, rows, columns);
160  for (int i = 0; i < rows; i++) A[i][i] += last_eig; /* A + cI */
161  }
162 
163  /* store the converged eigen value */
164  eigen_vals[num_eigs] = last_eig;
165 
166  if (debug_print)
167  {
168  printf("========================\n");
169  printf("Eigen value: % g,\n", last_eig);
170  printf("========================\n");
171  }
172 
173  num_eigs--;
174  rows--;
175  columns--;
176  }
177  eigen_vals[0] = A[0][0];
178  double dtime = (double)(clock() - t1) / CLOCKS_PER_SEC;
179 
180  if (debug_print)
181  {
182  print_matrix(R, mat_size, mat_size);
183  print_matrix(Q, mat_size, mat_size);
184  }
185 
186  /* cleanup dynamic memory */
187  for (int i = 0; i < mat_size; i++)
188  {
189  free(R[i]);
190  free(Q[i]);
191  }
192  free(R);
193  free(Q);
194 
195  return dtime;
196 }
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  for (int j = 0; j < C2; j++)
74  {
75  OUT[i][j] = 0.f;
76  for (int k = 0; k < C1; k++) 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}

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

254 {
255  int mat_size = 5;
256  double X[][5] = {{-4, 4, 2, 0, -3},
257  {4, -4, 4, -3, -1},
258  {2, 4, 4, 3, -3},
259  {0, -3, 3, -1, -3},
260  {-3, -1, -3, -3, 0}};
261  double y[] = {9.27648, -9.26948, 2.0181, -1.03516,
262  -5.98994}; // corresponding y-values
263  double eig_vals[5];
264 
265  // The following steps are to convert a "double[][]" to "double **"
266  double **A = (double **)malloc(mat_size * sizeof(double *));
267  for (int i = 0; i < mat_size; i++) A[i] = X[i];
268 
269  printf("------- Test 2 -------\n");
270 
271  double dtime = eigen_values(A, eig_vals, mat_size, 0);
272 
273  for (int i = 0; i < mat_size; i++)
274  {
275  printf("%d/5 Checking for %.3g --> ", i + 1, y[i]);
276  char result = 0;
277  for (int j = 0; j < mat_size && !result; j++)
278  {
279  if (fabs(y[i] - eig_vals[j]) < 0.1)
280  {
281  result = 1;
282  printf("(%.3g) ", eig_vals[j]);
283  }
284  }
285 
286  // ensure that i^th expected eigen value was computed
287  assert(result != 0);
288  printf("found\n");
289  }
290  printf("Test 2 Passed in %.3g sec\n\n", dtime);
291  free(A);
292 }
Here is the call graph for this function:
EPSILON
#define EPSILON
accuracy tolerance limit
Definition: qr_eigen_values.c:20
N
#define N
number of digits of the large number
Definition: sol1.c:109
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:104