Algorithms_in_C 1.0.0
Set of algorithms implemented in C.
Loading...
Searching...
No Matches
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
 
double ** mat_mul (double **A, double **B, double **OUT, int R1, int C1, int R2, int C2)
 Perform multiplication of two matrices.
 
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:
 
void test1 ()
 test function to compute eigen values of a 2x2 matrix
 
void test2 ()
 test function to compute eigen values of a 2x2 matrix
 
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}
#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:

  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}
#define malloc(bytes)
This macro replace the standard malloc function with malloc_dbg.
Definition malloc_dbg.h:18
#define free(ptr)
This macro replace the standard free function with free_dbg.
Definition malloc_dbg.h:26
void qr_decompose(double **A, double **Q, double **R, int M, int N)
Decompose matrix using Gram-Schmidt process.
Definition qr_decompose.h:142
void print_matrix(double **A, int M, int N)
function to display matrix on stdout
Definition qr_decompose.h:22
#define EPSILON
accuracy tolerance limit
Definition qr_eigen_values.c:20
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
Here is the call graph for this function:

◆ main()

int main ( int  argc,
char **  argv 
)

main function

316{
317 srand(time(NULL));
318
319 int mat_size = 5;
320 if (argc == 2)
321 {
322 mat_size = atoi(argv[1]);
323 }
324 else
325 { // if invalid input argument is given run tests
326 test1();
327 test2();
328 printf("Usage: ./qr_eigen_values [mat_size]\n");
329 return 0;
330 }
331
332 if (mat_size < 2)
333 {
334 fprintf(stderr, "Matrix size should be > 2\n");
335 return -1;
336 }
337
338 int i;
339
340 double **A = (double **)malloc(sizeof(double *) * mat_size);
341 /* number of eigen values = matrix size */
342 double *eigen_vals = (double *)malloc(sizeof(double) * mat_size);
343 if (!eigen_vals)
344 {
345 perror("Unable to allocate memory for eigen values!");
346 free(A);
347 return -1;
348 }
349 for (i = 0; i < mat_size; i++)
350 {
351 A[i] = (double *)malloc(sizeof(double) * mat_size);
352 eigen_vals[i] = 0.f;
353 }
354
355 /* create a random matrix */
356 create_matrix(A, mat_size);
357
358 print_matrix(A, mat_size, mat_size);
359
360 double dtime = eigen_values(A, eigen_vals, mat_size, 0);
361 printf("Eigen vals: ");
362 for (i = 0; i < mat_size; i++) printf("% 9.4g\t", eigen_vals[i]);
363 printf("\nTime taken to compute: % .4g sec\n", dtime);
364
365 for (int i = 0; i < mat_size; i++) free(A[i]);
366 free(A);
367 free(eigen_vals);
368 return 0;
369}
void test2()
test function to compute eigen values of a 2x2 matrix
Definition qr_eigen_values.c:271
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
void test1()
test function to compute eigen values of a 2x2 matrix
Definition qr_eigen_values.c:224
void create_matrix(double **A, int N)
create a square matrix of given size with random elements
Definition qr_eigen_values.c:27
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}
static const uint8_t R2
Rotation constant 2 for mixing function G.
Definition hash_blake2b.c:84
static const uint8_t R1
Rotation constant 1 for mixing function G.
Definition hash_blake2b.c:83

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