/src/openbabel/src/math/matrix3x3.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | matrix3x3.cpp - Handle 3D rotation matrix. |
3 | | |
4 | | Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc. |
5 | | Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison |
6 | | Some portions Copyright (C) 2006 by Benoit Jacob |
7 | | |
8 | | This file is part of the Open Babel project. |
9 | | For more information, see <http://openbabel.org/> |
10 | | |
11 | | This program is free software; you can redistribute it and/or modify |
12 | | it under the terms of the GNU General Public License as published by |
13 | | the Free Software Foundation version 2 of the License. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License for more details. |
19 | | ***********************************************************************/ |
20 | | #include <openbabel/babelconfig.h> |
21 | | |
22 | | #include <openbabel/math/matrix3x3.h> |
23 | | #include <openbabel/obutil.h> |
24 | | |
25 | | using namespace std; |
26 | | |
27 | | namespace OpenBabel |
28 | | { |
29 | | |
30 | | /** \class matrix3x3 matrix3x3.h <openbabel/math/matrix3x3.h> |
31 | | \brief Represents a real 3x3 matrix. |
32 | | |
33 | | Rotating points in space can be performed by a vector-matrix |
34 | | multiplication. The matrix3x3 class is designed as a helper to the |
35 | | vector3 class for rotating points in space. The rotation matrix may be |
36 | | initialised by passing in the array of floating point values, by |
37 | | passing euler angles, or a rotation vector and angle of rotation about |
38 | | that vector. Once set, the matrix3x3 class can be used to rotate |
39 | | vectors by the overloaded multiplication operator. The following |
40 | | demonstrates the usage of the matrix3x3 class: |
41 | | |
42 | | \code |
43 | | matrix3x3 mat; |
44 | | mat.SetupRotMat(0.0,180.0,0.0); //rotate theta by 180 degrees |
45 | | vector3 v = VX; |
46 | | v *= mat; //apply the rotation |
47 | | \endcode |
48 | | |
49 | | */ |
50 | | |
51 | | void matrix3x3::SetupRotMat(double phi,double theta,double psi) |
52 | 0 | { |
53 | 0 | double p = phi * DEG_TO_RAD; |
54 | 0 | double h = theta * DEG_TO_RAD; |
55 | 0 | double b = psi * DEG_TO_RAD; |
56 | |
|
57 | 0 | double cx = cos(p); |
58 | 0 | double sx = sin(p); |
59 | 0 | double cy = cos(h); |
60 | 0 | double sy = sin(h); |
61 | 0 | double cz = cos(b); |
62 | 0 | double sz = sin(b); |
63 | |
|
64 | 0 | ele[0][0] = cy*cz; |
65 | 0 | ele[0][1] = cy*sz; |
66 | 0 | ele[0][2] = -sy; |
67 | |
|
68 | 0 | ele[1][0] = sx*sy*cz-cx*sz; |
69 | 0 | ele[1][1] = sx*sy*sz+cx*cz; |
70 | 0 | ele[1][2] = sx*cy; |
71 | |
|
72 | 0 | ele[2][0] = cx*sy*cz+sx*sz; |
73 | 0 | ele[2][1] = cx*sy*sz-sx*cz; |
74 | 0 | ele[2][2] = cx*cy; |
75 | 0 | } |
76 | | |
77 | | /*! Replaces *this with a matrix that represents reflection on |
78 | | the plane through 0 which is given by the normal vector norm. |
79 | | |
80 | | \warning If the vector norm has length zero, this method will |
81 | | generate the 0-matrix. If the length of the axis is close to |
82 | | zero, but not == 0.0, this method may behave in unexpected |
83 | | ways and return almost random results; details may depend on |
84 | | your particular floating point implementation. The use of this |
85 | | method is therefore highly discouraged, unless you are certain |
86 | | that the length is in a reasonable range, away from 0.0 |
87 | | (Stefan Kebekus) |
88 | | |
89 | | \deprecated This method will probably replaced by a safer |
90 | | algorithm in the future. |
91 | | |
92 | | \todo Replace this method with a more fool-proof version. |
93 | | |
94 | | @param norm specifies the normal to the plane |
95 | | */ |
96 | | OB_DEPRECATED |
97 | | void matrix3x3::PlaneReflection(const vector3 &norm) |
98 | 0 | { |
99 | | //@@@ add a safety net |
100 | |
|
101 | 0 | vector3 normtmp = norm; |
102 | 0 | normtmp.normalize(); |
103 | |
|
104 | 0 | SetColumn(0, vector3(1,0,0) - 2*normtmp.x()*normtmp); |
105 | 0 | SetColumn(1, vector3(0,1,0) - 2*normtmp.y()*normtmp); |
106 | 0 | SetColumn(2, vector3(0,0,1) - 2*normtmp.z()*normtmp); |
107 | 0 | } |
108 | | |
109 | | /*! Replaces *this with a matrix that represents rotation about the |
110 | | axis by a an angle. |
111 | | |
112 | | \warning If the vector axis has length zero, this method will |
113 | | generate the 0-matrix. If the length of the axis is close to |
114 | | zero, but not == 0.0, this method may behave in unexpected ways |
115 | | and return almost random results; details may depend on your |
116 | | particular floating point implementation. The use of this method |
117 | | is therefore highly discouraged, unless you are certain that the |
118 | | length is in a reasonable range, away from 0.0 (Stefan |
119 | | Kebekus) |
120 | | |
121 | | \deprecated This method will probably replaced by a safer |
122 | | algorithm in the future. |
123 | | |
124 | | \todo Replace this method with a more fool-proof version. |
125 | | |
126 | | @param v specifies the axis of the rotation |
127 | | @param angle angle in degrees (0..360) |
128 | | */ |
129 | | OB_DEPRECATED |
130 | | void matrix3x3::RotAboutAxisByAngle(const vector3 &v,const double angle) |
131 | 0 | { |
132 | 0 | double theta = angle*DEG_TO_RAD; |
133 | 0 | double s = sin(theta); |
134 | 0 | double c = cos(theta); |
135 | 0 | double t = 1 - c; |
136 | |
|
137 | 0 | vector3 vtmp = v; |
138 | 0 | vtmp.normalize(); |
139 | |
|
140 | 0 | double x = vtmp.x(), |
141 | 0 | y = vtmp.y(), |
142 | 0 | z = vtmp.z(); |
143 | |
|
144 | 0 | ele[0][0] = t*x*x + c; |
145 | 0 | ele[0][1] = t*x*y + s*z; |
146 | 0 | ele[0][2] = t*x*z - s*y; |
147 | |
|
148 | 0 | ele[1][0] = t*x*y - s*z; |
149 | 0 | ele[1][1] = t*y*y + c; |
150 | 0 | ele[1][2] = t*y*z + s*x; |
151 | |
|
152 | 0 | ele[2][0] = t*x*z + s*y; |
153 | 0 | ele[2][1] = t*y*z - s*x; |
154 | 0 | ele[2][2] = t*z*z + c; |
155 | 0 | } |
156 | | |
157 | | void matrix3x3::SetColumn(int col, const vector3 &v) |
158 | | #ifdef OB_OLD_MATH_CHECKS |
159 | | throw(OBError) |
160 | | #endif |
161 | 0 | { |
162 | | #ifdef OB_OLD_MATH_CHECKS |
163 | | if (col > 2) |
164 | | { |
165 | | OBError er("matrix3x3::SetColumn(int col, const vector3 &v)", |
166 | | "The method was called with col > 2.", |
167 | | "This is a programming error in your application."); |
168 | | throw er; |
169 | | } |
170 | | #endif |
171 | |
|
172 | 0 | ele[0][col] = v.x(); |
173 | 0 | ele[1][col] = v.y(); |
174 | 0 | ele[2][col] = v.z(); |
175 | 0 | } |
176 | | |
177 | | void matrix3x3::SetRow(int row, const vector3 &v) |
178 | | #ifdef OB_OLD_MATH_CHECKS |
179 | | throw(OBError) |
180 | | #endif |
181 | 0 | { |
182 | | #ifdef OB_OLD_MATH_CHECKS |
183 | | if (row > 2) |
184 | | { |
185 | | OBError er("matrix3x3::SetRow(int row, const vector3 &v)", |
186 | | "The method was called with row > 2.", |
187 | | "This is a programming error in your application."); |
188 | | throw er; |
189 | | } |
190 | | #endif |
191 | |
|
192 | 0 | ele[row][0] = v.x(); |
193 | 0 | ele[row][1] = v.y(); |
194 | 0 | ele[row][2] = v.z(); |
195 | 0 | } |
196 | | |
197 | | vector3 matrix3x3::GetColumn(unsigned int col) const |
198 | | #ifdef OB_OLD_MATH_CHECKS |
199 | | throw(OBError) |
200 | | #endif |
201 | 0 | { |
202 | | #ifdef OB_OLD_MATH_CHECKS |
203 | | if (col > 2) |
204 | | { |
205 | | OBError er("matrix3x3::GetColumn(unsigned int col) const", |
206 | | "The method was called with col > 2.", |
207 | | "This is a programming error in your application."); |
208 | | throw er; |
209 | | } |
210 | | #endif |
211 | |
|
212 | 0 | return vector3(ele[0][col], ele[1][col], ele[2][col]); |
213 | 0 | } |
214 | | |
215 | | vector3 matrix3x3::GetRow(unsigned int row) const |
216 | | #ifdef OB_OLD_MATH_CHECKS |
217 | | throw(OBError) |
218 | | #endif |
219 | 0 | { |
220 | | #ifdef OB_OLD_MATH_CHECKS |
221 | | if (row > 2) |
222 | | { |
223 | | OBError er("matrix3x3::GetRow(unsigned int row) const", |
224 | | "The method was called with row > 2.", |
225 | | "This is a programming error in your application."); |
226 | | throw er; |
227 | | } |
228 | | #endif |
229 | |
|
230 | 0 | return vector3(ele[row][0], ele[row][1], ele[row][2]); |
231 | 0 | } |
232 | | |
233 | | /*! Calculates the product m*v of the matrix m and the column |
234 | | vector represented by v |
235 | | */ |
236 | | vector3 operator *(const matrix3x3 &m,const vector3 &v) |
237 | 0 | { |
238 | 0 | vector3 vv; |
239 | |
|
240 | 0 | vv.x() = v.x()*m.ele[0][0] + v.y()*m.ele[0][1] + v.z()*m.ele[0][2]; |
241 | 0 | vv.y() = v.x()*m.ele[1][0] + v.y()*m.ele[1][1] + v.z()*m.ele[1][2]; |
242 | 0 | vv.z() = v.x()*m.ele[2][0] + v.y()*m.ele[2][1] + v.z()*m.ele[2][2]; |
243 | |
|
244 | 0 | return(vv); |
245 | 0 | } |
246 | | |
247 | | matrix3x3 operator *(const matrix3x3 &A,const matrix3x3 &B) |
248 | 0 | { |
249 | 0 | matrix3x3 result; |
250 | |
|
251 | 0 | result.ele[0][0] = A.ele[0][0]*B.ele[0][0] + A.ele[0][1]*B.ele[1][0] + A.ele[0][2]*B.ele[2][0]; |
252 | 0 | result.ele[0][1] = A.ele[0][0]*B.ele[0][1] + A.ele[0][1]*B.ele[1][1] + A.ele[0][2]*B.ele[2][1]; |
253 | 0 | result.ele[0][2] = A.ele[0][0]*B.ele[0][2] + A.ele[0][1]*B.ele[1][2] + A.ele[0][2]*B.ele[2][2]; |
254 | |
|
255 | 0 | result.ele[1][0] = A.ele[1][0]*B.ele[0][0] + A.ele[1][1]*B.ele[1][0] + A.ele[1][2]*B.ele[2][0]; |
256 | 0 | result.ele[1][1] = A.ele[1][0]*B.ele[0][1] + A.ele[1][1]*B.ele[1][1] + A.ele[1][2]*B.ele[2][1]; |
257 | 0 | result.ele[1][2] = A.ele[1][0]*B.ele[0][2] + A.ele[1][1]*B.ele[1][2] + A.ele[1][2]*B.ele[2][2]; |
258 | |
|
259 | 0 | result.ele[2][0] = A.ele[2][0]*B.ele[0][0] + A.ele[2][1]*B.ele[1][0] + A.ele[2][2]*B.ele[2][0]; |
260 | 0 | result.ele[2][1] = A.ele[2][0]*B.ele[0][1] + A.ele[2][1]*B.ele[1][1] + A.ele[2][2]*B.ele[2][1]; |
261 | 0 | result.ele[2][2] = A.ele[2][0]*B.ele[0][2] + A.ele[2][1]*B.ele[1][2] + A.ele[2][2]*B.ele[2][2]; |
262 | |
|
263 | 0 | return(result); |
264 | 0 | } |
265 | | |
266 | | /*! calculates the product m*(*this) of the matrix m and the |
267 | | column vector represented by *this |
268 | | */ |
269 | | vector3 &vector3::operator *= (const matrix3x3 &m) |
270 | 0 | { |
271 | 0 | vector3 vv; |
272 | |
|
273 | 0 | vv.SetX(x()*m.Get(0,0) + y()*m.Get(0,1) + z()*m.Get(0,2)); |
274 | 0 | vv.SetY(x()*m.Get(1,0) + y()*m.Get(1,1) + z()*m.Get(1,2)); |
275 | 0 | vv.SetZ(x()*m.Get(2,0) + y()*m.Get(2,1) + z()*m.Get(2,2)); |
276 | 0 | x() = vv.x(); |
277 | 0 | y() = vv.y(); |
278 | 0 | z() = vv.z(); |
279 | |
|
280 | 0 | return(*this); |
281 | 0 | } |
282 | | |
283 | | /*! This method checks if the absolute value of the determinant is smaller than 1e-6. If |
284 | | so, nothing is done and an exception is thrown. Otherwise, the |
285 | | inverse matrix is calculated and returned. *this is not changed. |
286 | | |
287 | | \warning If the determinant is close to zero, but not == 0.0, |
288 | | this method may behave in unexpected ways and return almost |
289 | | random results; details may depend on your particular floating |
290 | | point implementation. The use of this method is therefore highly |
291 | | discouraged, unless you are certain that the determinant is in a |
292 | | reasonable range, away from 0.0 (Stefan Kebekus) |
293 | | */ |
294 | | matrix3x3 matrix3x3::inverse(void) const |
295 | | #ifdef OB_OLD_MATH_CHECKS |
296 | | throw(OBError) |
297 | | #endif |
298 | 0 | { |
299 | 0 | double det = determinant(); |
300 | |
|
301 | | #ifdef OB_OLD_MATH_CHECKS |
302 | | if (fabs(det) <= 1e-6) |
303 | | { |
304 | | OBError er("matrix3x3::invert(void)", |
305 | | "The method was called on a matrix with |determinant| <= 1e-6.", |
306 | | "This is a runtime or a programming error in your application."); |
307 | | throw er; |
308 | | } |
309 | | #endif |
310 | |
|
311 | 0 | matrix3x3 returnValue; |
312 | 0 | returnValue.ele[0][0] = ele[1][1]*ele[2][2] - ele[1][2]*ele[2][1]; |
313 | 0 | returnValue.ele[1][0] = ele[1][2]*ele[2][0] - ele[1][0]*ele[2][2]; |
314 | 0 | returnValue.ele[2][0] = ele[1][0]*ele[2][1] - ele[1][1]*ele[2][0]; |
315 | 0 | returnValue.ele[0][1] = ele[2][1]*ele[0][2] - ele[2][2]*ele[0][1]; |
316 | 0 | returnValue.ele[1][1] = ele[2][2]*ele[0][0] - ele[2][0]*ele[0][2]; |
317 | 0 | returnValue.ele[2][1] = ele[2][0]*ele[0][1] - ele[2][1]*ele[0][0]; |
318 | 0 | returnValue.ele[0][2] = ele[0][1]*ele[1][2] - ele[0][2]*ele[1][1]; |
319 | 0 | returnValue.ele[1][2] = ele[0][2]*ele[1][0] - ele[0][0]*ele[1][2]; |
320 | 0 | returnValue.ele[2][2] = ele[0][0]*ele[1][1] - ele[0][1]*ele[1][0]; |
321 | |
|
322 | 0 | returnValue /= det; |
323 | |
|
324 | 0 | return(returnValue); |
325 | 0 | } |
326 | | |
327 | | /* This method returns the transpose of a matrix. The original |
328 | | matrix remains unchanged. */ |
329 | | matrix3x3 matrix3x3::transpose(void) const |
330 | 0 | { |
331 | 0 | matrix3x3 returnValue; |
332 | |
|
333 | 0 | for(unsigned int i=0; i<3; i++) |
334 | 0 | for(unsigned int j=0; j<3; j++) |
335 | 0 | returnValue.ele[i][j] = ele[j][i]; |
336 | |
|
337 | 0 | return(returnValue); |
338 | 0 | } |
339 | | |
340 | | double matrix3x3::determinant(void) const |
341 | 0 | { |
342 | 0 | return( ele[0][0] * (ele[1][1] * ele[2][2] - ele[1][2] * ele[2][1]) |
343 | 0 | + ele[0][1] * (ele[1][2] * ele[2][0] - ele[1][0] * ele[2][2]) |
344 | 0 | + ele[0][2] * (ele[1][0] * ele[2][1] - ele[1][1] * ele[2][0]) ); |
345 | 0 | } |
346 | | |
347 | | /*! \return False if there are indices i,j such that |
348 | | fabs(*this[i][j]-*this[j][i]) > 1e-6. Otherwise, it returns |
349 | | true. */ |
350 | | bool matrix3x3::isSymmetric(void) const |
351 | 0 | { |
352 | 0 | return( IsApprox( ele[0][1], ele[1][0], 1e-6 ) |
353 | 0 | && IsApprox( ele[0][2], ele[2][0], 1e-6 ) |
354 | 0 | && IsApprox( ele[1][2], ele[2][1], 1e-6 ) ); |
355 | 0 | } |
356 | | |
357 | | /*! This method returns true if and only if the matrix is |
358 | | * (approximately) a diagonal matrix. The precision used |
359 | | * by this function is 1e-6. */ |
360 | | bool matrix3x3::isDiagonal(void) const |
361 | 0 | { |
362 | 0 | return( IsNegligible( ele[1][0], ele[0][0], 1e-6 ) |
363 | 0 | && IsNegligible( ele[2][0], ele[0][0], 1e-6 ) |
364 | 0 | && IsNegligible( ele[0][1], ele[1][1], 1e-6 ) |
365 | 0 | && IsNegligible( ele[2][1], ele[1][1], 1e-6 ) |
366 | 0 | && IsNegligible( ele[0][2], ele[2][2], 1e-6 ) |
367 | 0 | && IsNegligible( ele[1][2], ele[2][2], 1e-6 ) ); |
368 | 0 | } |
369 | | |
370 | | /*! This method returns true if and only if the matrix is |
371 | | * (approximately) equal to the identity matrix. The precision used |
372 | | * by this function is 1e-6. */ |
373 | | bool matrix3x3::isUnitMatrix(void) const |
374 | 0 | { |
375 | 0 | return ( isDiagonal() |
376 | 0 | && IsApprox( ele[0][0], 1.0, 1e-6 ) |
377 | 0 | && IsApprox( ele[1][1], 1.0, 1e-6 ) |
378 | 0 | && IsApprox( ele[2][2], 1.0, 1e-6 ) ); |
379 | 0 | } |
380 | | |
381 | | /*! This method employs the static method matrix3x3::jacobi(...) |
382 | | to find the eigenvalues and eigenvectors of a symmetric |
383 | | matrix. On entry it is checked if the matrix really is |
384 | | symmetric: if isSymmetric() returns 'false', an OBError is |
385 | | thrown. |
386 | | |
387 | | \note The jacobi algorithm is should work great for all |
388 | | symmetric 3x3 matrices. If you need to find the eigenvectors |
389 | | of a non-symmetric matrix, you might want to resort to the |
390 | | sophisticated routines of LAPACK. |
391 | | |
392 | | @param eigenvals a reference to a vector3 where the |
393 | | eigenvalues will be stored. The eigenvalues are ordered so |
394 | | that eigenvals[0] <= eigenvals[1] <= eigenvals[2]. |
395 | | |
396 | | @return an orthogonal matrix whose ith column is an |
397 | | eigenvector for the eigenvalue eigenvals[i]. Here 'orthogonal' |
398 | | means that all eigenvectors have length one and are mutually |
399 | | orthogonal. The ith eigenvector can thus be conveniently |
400 | | accessed by the GetColumn() method, as in the following |
401 | | example. |
402 | | \code |
403 | | // Calculate eigenvectors and -values |
404 | | vector3 eigenvals; |
405 | | matrix3x3 eigenmatrix = somematrix.findEigenvectorsIfSymmetric(eigenvals); |
406 | | |
407 | | // Print the 2nd eigenvector |
408 | | cout << eigenmatrix.GetColumn(1) << endl; |
409 | | \endcode |
410 | | With these conventions, a matrix is diagonalized in the following way: |
411 | | \code |
412 | | // Diagonalize the matrix |
413 | | matrix3x3 diagonalMatrix = eigenmatrix.inverse() * somematrix * eigenmatrix; |
414 | | \endcode |
415 | | |
416 | | */ |
417 | | matrix3x3 matrix3x3::findEigenvectorsIfSymmetric(vector3 &eigenvals) const |
418 | | #ifdef OB_OLD_MATH_CHECKS |
419 | | throw(OBError) |
420 | | #endif |
421 | 0 | { |
422 | 0 | matrix3x3 result; |
423 | |
|
424 | | #ifdef OB_OLD_MATH_CHECKS |
425 | | if (!isSymmetric()) |
426 | | { |
427 | | OBError er("matrix3x3::findEigenvectorsIfSymmetric(vector3 &eigenvals) const throw(OBError)", |
428 | | "The method was called on a matrix that was not symmetric, i.e. where isSymetric() == false.", |
429 | | "This is a runtime or a programming error in your application."); |
430 | | throw er; |
431 | | } |
432 | | #endif |
433 | |
|
434 | 0 | double d[3]; |
435 | 0 | matrix3x3 copyOfThis = *this; |
436 | |
|
437 | 0 | jacobi(3, copyOfThis.ele[0], d, result.ele[0]); |
438 | 0 | eigenvals.Set(d); |
439 | |
|
440 | 0 | return result; |
441 | 0 | } |
442 | | |
443 | 0 | static inline double SQUARE( double x ) { return x*x; } |
444 | | |
445 | | void matrix3x3::FillOrth(double Alpha,double Beta, double Gamma, |
446 | | double A, double B, double C) |
447 | 0 | { |
448 | 0 | double V; |
449 | |
|
450 | 0 | Alpha *= DEG_TO_RAD; |
451 | 0 | Beta *= DEG_TO_RAD; |
452 | 0 | Gamma *= DEG_TO_RAD; |
453 | | |
454 | | // from the PDB specification: |
455 | | // http://www.rcsb.org/pdb/docs/format/pdbguide2.2/part_75.html |
456 | | |
457 | | |
458 | | // since we'll ultimately divide by (a * b), we've factored those out here |
459 | 0 | V = C * sqrt(1 - SQUARE(cos(Alpha)) - SQUARE(cos(Beta)) - SQUARE(cos(Gamma)) |
460 | 0 | + 2 * cos(Alpha) * cos(Beta) * cos(Gamma)); |
461 | |
|
462 | 0 | ele[0][0] = A; |
463 | 0 | ele[0][1] = B*cos(Gamma); |
464 | 0 | ele[0][2] = C*cos(Beta); |
465 | |
|
466 | 0 | ele[1][0] = 0.0; |
467 | 0 | ele[1][1] = B*sin(Gamma); |
468 | 0 | ele[1][2] = C*(cos(Alpha)-cos(Beta)*cos(Gamma))/sin(Gamma); |
469 | |
|
470 | 0 | ele[2][0] = 0.0; |
471 | 0 | ele[2][1] = 0.0; |
472 | 0 | ele[2][2] = V / (sin(Gamma)); // again, we factored out A * B when defining V |
473 | 0 | } |
474 | | |
475 | | /** Print a text representation of the matrix in the standardized form: |
476 | | [ a, b, c ] <br> |
477 | | [ d, e, f ] <br> |
478 | | [ g, h, i ] <br> |
479 | | where the letters represent the appropriate entries in the matrix. |
480 | | Uses the standard output format for the individual entries, separated |
481 | | by ", " for each column, and [ ] indicating each row. |
482 | | **/ |
483 | | ostream& operator<< ( ostream& co, const matrix3x3& m ) |
484 | | |
485 | 0 | { |
486 | 0 | co << "[ " |
487 | 0 | << m.ele[0][0] |
488 | 0 | << ", " |
489 | 0 | << m.ele[0][1] |
490 | 0 | << ", " |
491 | 0 | << m.ele[0][2] |
492 | 0 | << " ]" << endl; |
493 | |
|
494 | 0 | co << "[ " |
495 | 0 | << m.ele[1][0] |
496 | 0 | << ", " |
497 | 0 | << m.ele[1][1] |
498 | 0 | << ", " |
499 | 0 | << m.ele[1][2] |
500 | 0 | << " ]" << endl; |
501 | |
|
502 | 0 | co << "[ " |
503 | 0 | << m.ele[2][0] |
504 | 0 | << ", " |
505 | 0 | << m.ele[2][1] |
506 | 0 | << ", " |
507 | 0 | << m.ele[2][2] |
508 | 0 | << " ]" << endl; |
509 | |
|
510 | 0 | return co ; |
511 | 0 | } |
512 | | |
513 | | /*! This static function computes the eigenvalues and |
514 | | eigenvectors of a SYMMETRIC nxn matrix. This method is used |
515 | | internally by OpenBabel, but may be useful as a general |
516 | | eigenvalue finder. |
517 | | |
518 | | The algorithm uses Jacobi transformations. It is described |
519 | | e.g. in Wilkinson, Reinsch "Handbook for automatic computation, |
520 | | Volume II: Linear Algebra", part II, contribution II/1. The |
521 | | implementation is also similar to the implementation in this |
522 | | book. This method is adequate to solve the eigenproblem for |
523 | | small matrices, of size perhaps up to 10x10. For bigger |
524 | | problems, you might want to resort to the sophisticated routines |
525 | | of LAPACK. |
526 | | |
527 | | \note If you plan to find the eigenvalues of a symmetric 3x3 |
528 | | matrix, you will probably prefer to use the more convenient |
529 | | method findEigenvectorsIfSymmetric() |
530 | | |
531 | | @param n the size of the matrix that should be diagonalized |
532 | | |
533 | | @param a array of size n^2 which holds the symmetric matrix |
534 | | whose eigenvectors are to be computed. The convention is that |
535 | | the entry in row r and column c is addressed as a[n*r+c] where, |
536 | | of course, 0 <= r < n and 0 <= c < n. There is no check that the |
537 | | matrix is actually symmetric. If it is not, the behaviour of |
538 | | this function is undefined. On return, the matrix is |
539 | | overwritten with junk. |
540 | | |
541 | | @param d pointer to a field of at least n doubles which will be |
542 | | overwritten. On return of this function, the entries d[0]..d[n-1] |
543 | | will contain the eigenvalues of the matrix. |
544 | | |
545 | | @param v an array of size n^2 where the eigenvectors will be |
546 | | stored. On return, the columns of this matrix will contain the |
547 | | eigenvectors. The eigenvectors are normalized and mutually |
548 | | orthogonal. |
549 | | */ |
550 | | void matrix3x3::jacobi(unsigned int n, double *a, double *d, double *v) |
551 | 0 | { |
552 | 0 | double onorm, dnorm; |
553 | 0 | double b, dma, q, t, c, s; |
554 | 0 | double atemp, vtemp, dtemp; |
555 | 0 | int i, j, k, l; |
556 | 0 | int nrot; |
557 | |
|
558 | 0 | int MAX_SWEEPS=50; |
559 | | |
560 | | // Set v to the identity matrix, set the vector d to contain the |
561 | | // diagonal elements of the matrix a |
562 | 0 | for (j = 0; j < static_cast<int>(n); j++) |
563 | 0 | { |
564 | 0 | for (i = 0; i < static_cast<int>(n); i++) |
565 | 0 | v[n*i+j] = 0.0; |
566 | 0 | v[n*j+j] = 1.0; |
567 | 0 | d[j] = a[n*j+j]; |
568 | 0 | } |
569 | |
|
570 | 0 | nrot = MAX_SWEEPS; |
571 | 0 | for (l = 1; l <= nrot; l++) |
572 | 0 | { |
573 | | // Set dnorm to be the maximum norm of the diagonal elements, set |
574 | | // onorm to the maximum norm of the off-diagonal elements |
575 | 0 | dnorm = 0.0; |
576 | 0 | onorm = 0.0; |
577 | 0 | for (j = 0; j < static_cast<int>(n); j++) |
578 | 0 | { |
579 | 0 | dnorm += (double)fabs(d[j]); |
580 | 0 | for (i = 0; i < j; i++) |
581 | 0 | onorm += (double)fabs(a[n*i+j]); |
582 | 0 | } |
583 | | // Normal end point of this algorithm. |
584 | 0 | if((onorm/dnorm) <= 1.0e-12) |
585 | 0 | goto Exit_now; |
586 | | |
587 | 0 | for (j = 1; j < static_cast<int>(n); j++) |
588 | 0 | { |
589 | 0 | for (i = 0; i <= j - 1; i++) |
590 | 0 | { |
591 | |
|
592 | 0 | b = a[n*i+j]; |
593 | 0 | if(fabs(b) > 0.0) |
594 | 0 | { |
595 | 0 | dma = d[j] - d[i]; |
596 | 0 | if((fabs(dma) + fabs(b)) <= fabs(dma)) |
597 | 0 | t = b / dma; |
598 | 0 | else |
599 | 0 | { |
600 | 0 | q = 0.5 * dma / b; |
601 | 0 | t = 1.0/((double)fabs(q) + (double)sqrt(1.0+q*q)); |
602 | 0 | if (q < 0.0) |
603 | 0 | t = -t; |
604 | 0 | } |
605 | |
|
606 | 0 | c = 1.0/(double)sqrt(t*t + 1.0); |
607 | 0 | s = t * c; |
608 | 0 | a[n*i+j] = 0.0; |
609 | |
|
610 | 0 | for (k = 0; k <= i-1; k++) |
611 | 0 | { |
612 | 0 | atemp = c * a[n*k+i] - s * a[n*k+j]; |
613 | 0 | a[n*k+j] = s * a[n*k+i] + c * a[n*k+j]; |
614 | 0 | a[n*k+i] = atemp; |
615 | 0 | } |
616 | |
|
617 | 0 | for (k = i+1; k <= j-1; k++) |
618 | 0 | { |
619 | 0 | atemp = c * a[n*i+k] - s * a[n*k+j]; |
620 | 0 | a[n*k+j] = s * a[n*i+k] + c * a[n*k+j]; |
621 | 0 | a[n*i+k] = atemp; |
622 | 0 | } |
623 | |
|
624 | 0 | for (k = j+1; k < static_cast<int>(n); k++) |
625 | 0 | { |
626 | 0 | atemp = c * a[n*i+k] - s * a[n*j+k]; |
627 | 0 | a[n*j+k] = s * a[n*i+k] + c * a[n*j+k]; |
628 | 0 | a[n*i+k] = atemp; |
629 | 0 | } |
630 | |
|
631 | 0 | for (k = 0; k < static_cast<int>(n); k++) |
632 | 0 | { |
633 | 0 | vtemp = c * v[n*k+i] - s * v[n*k+j]; |
634 | 0 | v[n*k+j] = s * v[n*k+i] + c * v[n*k+j]; |
635 | 0 | v[n*k+i] = vtemp; |
636 | 0 | } |
637 | |
|
638 | 0 | dtemp = c*c*d[i] + s*s*d[j] - 2.0*c*s*b; |
639 | 0 | d[j] = s*s*d[i] + c*c*d[j] + 2.0*c*s*b; |
640 | 0 | d[i] = dtemp; |
641 | 0 | } /* end if */ |
642 | 0 | } /* end for i */ |
643 | 0 | } /* end for j */ |
644 | 0 | } /* end for l */ |
645 | | |
646 | 0 | Exit_now: |
647 | | |
648 | | // Now sort the eigenvalues (and the eigenvectors) so that the |
649 | | // smallest eigenvalues come first. |
650 | 0 | nrot = l; |
651 | |
|
652 | 0 | for (j = 0; j < static_cast<int>(n)-1; j++) |
653 | 0 | { |
654 | 0 | k = j; |
655 | 0 | dtemp = d[k]; |
656 | 0 | for (i = j+1; i < static_cast<int>(n); i++) |
657 | 0 | if(d[i] < dtemp) |
658 | 0 | { |
659 | 0 | k = i; |
660 | 0 | dtemp = d[k]; |
661 | 0 | } |
662 | |
|
663 | 0 | if(k > j) |
664 | 0 | { |
665 | 0 | d[k] = d[j]; |
666 | 0 | d[j] = dtemp; |
667 | 0 | for (i = 0; i < static_cast<int>(n); i++) |
668 | 0 | { |
669 | 0 | dtemp = v[n*i+k]; |
670 | 0 | v[n*i+k] = v[n*i+j]; |
671 | 0 | v[n*i+j] = dtemp; |
672 | 0 | } |
673 | 0 | } |
674 | 0 | } |
675 | 0 | } |
676 | | |
677 | | } // end namespace OpenBabel |
678 | | |
679 | | //! \file matrix3x3.cpp |
680 | | //! \brief Handle 3D rotation matrix. |
681 | | |