Bioplib
Protein Structure C Library
 All Data Structures Files Functions Variables Typedefs Macros Pages
eigen.c
Go to the documentation of this file.
1 /************************************************************************/
2 /**
3 
4  \file eigen.c
5 
6  \version V1.0
7  \date 03.10.14
8  \brief Calculates Eigen values and Eigen vectors for a
9  symmetric matrix
10 
11  \copyright (c) Dr. Andrew C. R. Martin, UCL, 2014
12  \author Dr. Andrew C. R. Martin
13  \par
14  Institute of Structural & Molecular Biology,
15  University College London,
16  Gower Street,
17  London.
18  WC1E 6BT.
19  \par
20  andrew@bioinf.org.uk
21  andrew.martin@ucl.ac.uk
22 
23 **************************************************************************
24 
25  This code is NOT IN THE PUBLIC DOMAIN, but it may be copied
26  according to the conditions laid out in the accompanying file
27  COPYING.DOC.
28 
29  The code may be modified as required, but any modifications must be
30  documented so that the person responsible can be identified.
31 
32  The code may not be sold commercially or included as part of a
33  commercial product except as described in the file COPYING.DOC.
34 
35 **************************************************************************
36 
37  Description:
38  ============
39 
40 **************************************************************************
41 
42  Usage:
43  ======
44 
45 **************************************************************************
46 
47  Revision History:
48  =================
49 - V1.0 03.10.14 Original
50 
51 *************************************************************************/
52 /* Doxygen
53  -------
54  #GROUP Maths
55  #SUBGROUP Matrices
56  #FUNCTION blEigen()
57  Calculates the eigenvalues and eigenvectors of a REAL symmetric matrix
58  Note that this routine destroys the values above the diagonal of the
59  matrix.
60 */
61 
62 /************************************************************************/
63 /* Includes
64 */
65 #include <stdlib.h>
66 #include <math.h>
67 #include "MathType.h"
68 #include "array.h"
69 #include "eigen.h"
70 
71 /************************************************************************/
72 /* Defines and macros
73 */
74 #define MAXITERATION 50 /* Maximum number of iterations */
75 
76 /* Test whether a number x is smaller than double precision can cope with
77  when added to a number y
78 */
79 #define TESTSMALL(x, y) ((fabs(y)+(x)) == fabs(y))
80 
81 /************************************************************************/
82 /* Globals
83 */
84 
85 /************************************************************************/
86 /* Prototypes
87 */
88 static void PerformJacobiRotation(int ip, int iq, REAL g, int n,
89  REAL **matrix, REAL **eigenVectors,
90  REAL *eigenValues, REAL *ta_pq);
91 
92 
93 /************************************************************************/
94 /*>int blEigen(REAL **matrix, REAL **eigenVectors, REAL *eigenValues,
95  int matrixSize)
96  ------------------------------------------------------------------
97 *//**
98  \param[in] **matrix Symmetric matrix
99  \param[in] matrixSize Dimension of the matrix
100  \param[out] **eigenVectors The eigen vectors
101  \param[out] *eigenValues The eigen values
102  \return If >0, the number of Jacobi rotations
103  performed
104  If <0, error:
105  EIGEN_NOMEMORY - memory allocation
106  EIGEN_NOCONVERGE - didn't converge
107  in 50 iterations
108 
109  Calculates the eigenvalues and eigenvectors of a REAL symmetric matrix
110  Note that this routine destroys the values above the diagonal of the
111  matrix.
112 
113 - 02.10.14 Original By: ACRM
114 */
115 int blEigen(REAL **matrix, REAL **eigenVectors, REAL *eigenValues,
116  int matrixSize)
117 
118 {
119  int column = 0,
120  row = 0,
121  iteration = 0,
122  nRotations = 0;
123  REAL threshold = 0.0,
124  offDiagonalSum = 0.0,
125  *diagonal = NULL,
126  *ta_pq = NULL;
127 
128  diagonal = (REAL *)malloc(matrixSize*sizeof(REAL));
129  ta_pq = (REAL *)malloc(matrixSize*sizeof(REAL));
130 
131  if((diagonal == NULL) || (ta_pq == NULL))
132  {
133  if(diagonal != NULL) free(diagonal);
134  if(ta_pq != NULL) free(ta_pq);
135  return(EIGEN_NOMEMORY);
136  }
137 
138  /* Initialize the eigenVectors matrix to the identity matrix. */
139  for(row=0; row<matrixSize; row++)
140  {
141  for(column=0; column<matrixSize; column++)
142  eigenVectors[row][column]=0.0;
143  eigenVectors[row][row]=1.0;
144  }
145 
146  /* Initialize diagonal and eigenValues to the diagonal of matrix and
147  initialize ta_{pq} to accumulate terms of the form in NumRec
148  Equation 11.1.14
149  */
150  for(row=0; row<matrixSize; row++)
151  {
152  diagonal[row] = matrix[row][row];
153  eigenValues[row] = matrix[row][row];
154  ta_pq[row] = 0.0;
155  }
156 
157  /* Iterate until the off-diagonal sum is reduced to zero */
158  for(iteration=0; iteration<MAXITERATION; iteration++)
159  {
160  /* Sum the off-diagonal elements as in NumRec Equation 11.1.19 */
161  offDiagonalSum = 0.0;
162  for(row=0; row<matrixSize-1; row++)
163  {
164  for(column=row+1; column<matrixSize; column++)
165  {
166  offDiagonalSum += fabs(matrix[row][column]);
167  }
168  }
169 
170  /* If we have converged such that the off-diagonal sum is zero, then
171  we have our solution, so we return
172  */
173  if(offDiagonalSum == 0.0)
174  {
175  free(ta_pq);
176  free(diagonal);
177  return(nRotations);
178  }
179 
180  /* Use a larger threshold for the first 4 iterations. This is
181  NumRec Equation 11.1.25
182  */
183  if(iteration < 4)
184  {
185  threshold = 0.2 * offDiagonalSum / (matrixSize*matrixSize);
186  }
187  else
188  {
189  threshold = 0.0;
190  }
191 
192  for(row=0; row<matrixSize-1; row++)
193  {
194  for(column=row+1; column<matrixSize; column++)
195  {
196  REAL offDiagonal = 100.0 * fabs(matrix[row][column]);
197 
198  /* After the first four iterations, we skip the rotation if
199  the off-diagonal element is small
200  */
201  if(iteration > 4 &&
202  TESTSMALL(offDiagonal, eigenValues[row]) &&
203  TESTSMALL(offDiagonal, eigenValues[column]))
204  {
205  matrix[row][column]=0.0;
206  }
207  else if(fabs(matrix[row][column]) > threshold)
208  {
209  PerformJacobiRotation(row, column, offDiagonal, matrixSize,
210  matrix, eigenVectors, eigenValues,
211  ta_pq);
212 
213  /* Increment the number of rotations */
214  nRotations++;
215  }
216  }
217  }
218 
219  /* Update eigenValues with the value of ta_{pq}, and
220  reinitialize ta_{pq}
221  */
222  for(row=0; row<matrixSize; row++)
223  {
224  diagonal[row] += ta_pq[row];
225  eigenValues[row] = diagonal[row];
226  ta_pq[row] = 0.0;
227  }
228  }
229 
230  free(ta_pq);
231  free(diagonal);
232  return(EIGEN_NOCONVERGE);
233 }
234 
235 
236 /************************************************************************/
237 /*>static void PerformJacobiRotation(int row, int column, REAL g,
238  int matrixSize,
239  REAL **matrix, REAL **eigenVectors,
240  REAL *eigenValues, REAL *ta_pq)
241  ---------------------------------------------------------------------
242 *//**
243  \param[in] row Index of matrix row
244  \param[in] column Index of matrix column
245  \param[in] offDiagonal Off-diagonal value
246  \param[in] matrixSize Size of the matrix
247  \param[in,out] matrix The REAL matrix
248  \param[in,out] eigenVectors The eigen vectors
249  \param[in,out] eigenValues The eigen values
250  \param[in,out] ta_pq ta_{pq}
251 
252  Does the actual work of performing a Jacobi rotation
253 
254 - 03.10.14 Original By: ACRM
255 */
256 static void PerformJacobiRotation(int row, int column, REAL offDiagonal,
257  int matrixSize,
258  REAL **matrix, REAL **eigenVectors,
259  REAL *eigenValues, REAL *ta_pq)
260 {
261  REAL deltaEigenValue = eigenValues[column] - eigenValues[row];
262  REAL tTimesElement,
263  t,
264  theta,
265  tau,
266  tOverSqrtTSq,
267  oneOverSqrtTSq;
268  int j;
269 
270  if(TESTSMALL(offDiagonal, deltaEigenValue))
271  {
272  t = (matrix[row][column]) / deltaEigenValue;
273  }
274  else
275  {
276  /* Calculate theta as defined in NumRec Equation 11.1.8 */
277  theta = 0.5 * deltaEigenValue / matrix[row][column];
278  /* Calculate t as defined in NumRec Equation 11.1.10 */
279  t = 1.0 / (fabs(theta) + sqrt(1.0+theta*theta));
280  if(theta < 0.0) t = -t;
281  }
282 
283  tTimesElement = t * matrix[row][column];
284 
285  ta_pq[row] -= tTimesElement;
286  ta_pq[column] += tTimesElement;
287  eigenValues[row] -= tTimesElement;
288  eigenValues[column] += tTimesElement;
289  matrix[row][column] = 0.0;
290 
291  oneOverSqrtTSq = 1.0 / sqrt(1 + (t*t));
292  tOverSqrtTSq = t * oneOverSqrtTSq;
293  tau = tOverSqrtTSq / (1.0 + oneOverSqrtTSq);
294 
295  /* Perform rotations for 0<=j<row */
296  for(j=0; j<row; j++)
297  {
298  REAL temp1 = matrix[j][row];
299  REAL temp2 = matrix[j][column];
300  matrix[j][row] = temp1 - tOverSqrtTSq*(temp2 + temp1*tau);
301  matrix[j][column] = temp2 + tOverSqrtTSq*(temp1 - temp2*tau);
302  }
303 
304  /* Perform rotations for row<j<column */
305  for(j=row+1; j<column; j++)
306  {
307  REAL temp1 = matrix[row][j];
308  REAL temp2 = matrix[j][column];
309  matrix[row][j] = temp1 - tOverSqrtTSq*(temp2 + temp1*tau);
310  matrix[j][column] = temp2 + tOverSqrtTSq*(temp1 - temp2*tau);
311  }
312 
313  /* Perform rotations for column<j<matrixSize */
314  for(j=column+1; j<matrixSize; j++)
315  {
316  REAL temp1 = matrix[row][j];
317  REAL temp2 = matrix[column][j];
318  matrix[row][j] = temp1 - tOverSqrtTSq*(temp2 + temp1*tau);
319  matrix[column][j] = temp2 + tOverSqrtTSq*(temp1 - temp2*tau);
320  }
321 
322  /* And rotate the eigen vectors */
323  for(j=0; j<matrixSize; j++)
324  {
325  REAL temp1 = eigenVectors[j][row];
326  REAL temp2 = eigenVectors[j][column];
327  eigenVectors[j][row] = temp1 - tOverSqrtTSq*(temp2 + temp1*tau);
328  eigenVectors[j][column] = temp2 + tOverSqrtTSq*(temp1 - temp2*tau);
329  }
330 }
#define EIGEN_NOCONVERGE
Definition: eigen.h:5
#define NULL
Definition: array2.c:99
Include file for 2D/3D array functions.
int blEigen(REAL **matrix, REAL **eigenVectors, REAL *eigenValues, int matrixSize)
Definition: eigen.c:115
double REAL
Definition: MathType.h:67
#define MAXITERATION
Definition: eigen.c:74
#define TESTSMALL(x, y)
Definition: eigen.c:79
Type definitions for maths.
#define EIGEN_NOMEMORY
Definition: eigen.h:4