16
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
18
GNU General Public License for more details.
18
19
***********************************************************************/
23
#include "math/matrix3x3.h"
20
#include <openbabel/babelconfig.h>
22
#include <openbabel/math/matrix3x3.h>
23
#include <openbabel/obutil.h>
33
25
using namespace std;
35
27
namespace OpenBabel
39
\brief Represents a real 3x3 matrix.
41
Rotating points in space can be performed by a vector-matrix
42
multiplication. The matrix3x3 class is designed as a helper to the
43
vector3 class for rotating points in space. The rotation matrix may be
44
initialised by passing in the array of floating point values, by
45
passing euler angles, or a rotation vector and angle of rotation about
46
that vector. Once set, the matrix3x3 class can be used to rotate
47
vectors by the overloaded multiplication operator. The following
48
demonstrates the usage of the matrix3x3 class:
52
mat.SetupRotMat(0.0,180.0,0.0); //rotate theta by 180 degrees
54
v *= mat; //apply the rotation
30
/** \class matrix3x3 matrix3x3.h <openbabel/math/matrix3x3.h>
31
\brief Represents a real 3x3 matrix.
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:
44
mat.SetupRotMat(0.0,180.0,0.0); //rotate theta by 180 degrees
46
v *= mat; //apply the rotation
59
/*! the axis of the rotation will be uniformly distributed on
60
the unit sphere, the angle will be uniformly distributed in
61
the interval 0..360 degrees. */
62
void matrix3x3::randomRotation(OBRandom &rnd)
51
/*! The axis of the rotation will be uniformly distributed on
52
the unit sphere and the angle will be uniformly distributed in
53
the interval 0..360 degrees. */
54
void matrix3x3::randomRotation(OBRandom &rnd)
67
59
v1.randomUnitVector(&rnd);
68
60
rotAngle = 360.0 * rnd.NextFloat();
69
61
this->RotAboutAxisByAngle(v1,rotAngle);
72
void matrix3x3::SetupRotMat(double phi,double theta,double psi)
64
void matrix3x3::SetupRotMat(double phi,double theta,double psi)
74
66
double p = phi * DEG_TO_RAD;
75
67
double h = theta * DEG_TO_RAD;
76
68
double b = psi * DEG_TO_RAD;
124
116
SetColumn(0, vector3(1,0,0) - 2*normtmp.x()*normtmp);
125
117
SetColumn(1, vector3(0,1,0) - 2*normtmp.y()*normtmp);
126
118
SetColumn(2, vector3(0,0,1) - 2*normtmp.z()*normtmp);
133
/*! Replaces *this with a matrix that represents rotation about the
136
\warning If the vector axis has length zero, this method will
137
generate the 0-matrix. If the length of the axis is close to
138
zero, but not == 0.0, this method may behave in unexpected ways
139
and return almost random results; details may depend on your
140
particular floating point implementation. The use of this method
141
is therefore highly discouraged, unless you are certain that the
142
length is in a reasonable range, away from 0.0 (Stefan
145
\deprecated This method will probably replaced by a safer
146
algorithm in the future.
148
\todo Replace this method with a more fool-proof version.
150
@param v specifies the axis of the rotation
151
@param angle angle in degrees (0..360)
153
void matrix3x3::RotAboutAxisByAngle(const vector3 &v,const double angle)
121
/*! Replaces *this with a matrix that represents rotation about the
124
\warning If the vector axis has length zero, this method will
125
generate the 0-matrix. If the length of the axis is close to
126
zero, but not == 0.0, this method may behave in unexpected ways
127
and return almost random results; details may depend on your
128
particular floating point implementation. The use of this method
129
is therefore highly discouraged, unless you are certain that the
130
length is in a reasonable range, away from 0.0 (Stefan
133
\deprecated This method will probably replaced by a safer
134
algorithm in the future.
136
\todo Replace this method with a more fool-proof version.
138
@param v specifies the axis of the rotation
139
@param angle angle in degrees (0..360)
141
void matrix3x3::RotAboutAxisByAngle(const vector3 &v,const double angle)
155
143
double theta = angle*DEG_TO_RAD;
156
144
double s = sin(theta);
157
145
double c = cos(theta);
171
163
ele[2][0] = t*x*z + s*y;
172
164
ele[2][1] = t*y*z - s*x;
173
165
ele[2][2] = t*z*z + c;
180
void matrix3x3::SetColumn(int col, const vector3 &v) throw(OBError)
168
void matrix3x3::SetColumn(int col, const vector3 &v)
169
#ifdef OB_OLD_MATH_CHECKS
173
#ifdef OB_OLD_MATH_CHECKS
184
176
OBError er("matrix3x3::SetColumn(int col, const vector3 &v)",
185
177
"The method was called with col > 2.",
186
178
"This is a programming error in your application.");
190
183
ele[0][col] = v.x();
191
184
ele[1][col] = v.y();
192
185
ele[2][col] = v.z();
195
void matrix3x3::SetRow(int row, const vector3 &v) throw(OBError)
188
void matrix3x3::SetRow(int row, const vector3 &v)
189
#ifdef OB_OLD_MATH_CHECKS
193
#ifdef OB_OLD_MATH_CHECKS
199
196
OBError er("matrix3x3::SetRow(int row, const vector3 &v)",
200
197
"The method was called with row > 2.",
201
198
"This is a programming error in your application.");
205
203
ele[row][0] = v.x();
206
204
ele[row][1] = v.y();
207
205
ele[row][2] = v.z();
210
vector3 matrix3x3::GetColumn(unsigned int col) const throw(OBError)
208
vector3 matrix3x3::GetColumn(unsigned int col) const
209
#ifdef OB_OLD_MATH_CHECKS
213
#ifdef OB_OLD_MATH_CHECKS
214
216
OBError er("matrix3x3::GetColumn(unsigned int col) const",
215
217
"The method was called with col > 2.",
216
218
"This is a programming error in your application.");
220
223
return vector3(ele[0][col], ele[1][col], ele[2][col]);
223
vector3 matrix3x3::GetRow(unsigned int row) const throw(OBError)
226
vector3 matrix3x3::GetRow(unsigned int row) const
227
#ifdef OB_OLD_MATH_CHECKS
231
#ifdef OB_OLD_MATH_CHECKS
227
234
OBError er("matrix3x3::GetRow(unsigned int row) const",
228
235
"The method was called with row > 2.",
229
236
"This is a programming error in your application.");
233
241
return vector3(ele[row][0], ele[row][1], ele[row][2]);
236
/*! calculates the product m*v of the matrix m and the column
237
vector represented by v
239
vector3 operator *(const matrix3x3 &m,const vector3 &v)
244
/*! Calculates the product m*v of the matrix m and the column
245
vector represented by v
247
vector3 operator *(const matrix3x3 &m,const vector3 &v)
243
vv._vx = v._vx*m.ele[0][0] + v._vy*m.ele[0][1] + v._vz*m.ele[0][2];
244
vv._vy = v._vx*m.ele[1][0] + v._vy*m.ele[1][1] + v._vz*m.ele[1][2];
245
vv._vz = v._vx*m.ele[2][0] + v._vy*m.ele[2][1] + v._vz*m.ele[2][2];
251
vv.x() = v.x()*m.ele[0][0] + v.y()*m.ele[0][1] + v.z()*m.ele[0][2];
252
vv.y() = v.x()*m.ele[1][0] + v.y()*m.ele[1][1] + v.z()*m.ele[1][2];
253
vv.z() = v.x()*m.ele[2][0] + v.y()*m.ele[2][1] + v.z()*m.ele[2][2];
250
matrix3x3 operator *(const matrix3x3 &A,const matrix3x3 &B)
258
matrix3x3 operator *(const matrix3x3 &A,const matrix3x3 &B)
252
260
matrix3x3 result;
254
262
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];
264
272
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];
269
/*! calculates the product m*(*this) of the matrix m and the
270
column vector represented by *this
272
vector3 &vector3::operator *= (const matrix3x3 &m)
277
/*! calculates the product m*(*this) of the matrix m and the
278
column vector represented by *this
280
vector3 &vector3::operator *= (const matrix3x3 &m)
276
vv.SetX(_vx*m.Get(0,0) + _vy*m.Get(0,1) + _vz*m.Get(0,2));
277
vv.SetY(_vx*m.Get(1,0) + _vy*m.Get(1,1) + _vz*m.Get(1,2));
278
vv.SetZ(_vx*m.Get(2,0) + _vy*m.Get(2,1) + _vz*m.Get(2,2));
284
vv.SetX(x()*m.Get(0,0) + y()*m.Get(0,1) + z()*m.Get(0,2));
285
vv.SetY(x()*m.Get(1,0) + y()*m.Get(1,1) + z()*m.Get(1,2));
286
vv.SetZ(x()*m.Get(2,0) + y()*m.Get(2,1) + z()*m.Get(2,2));
286
/*! This method checks if the absolute value of the determinant is smaller than 1e-6. If
287
so, nothing is done and an exception is thrown. Otherwise, the
288
inverse matrix is calculated and returned. *this is not changed.
294
/*! This method checks if the absolute value of the determinant is smaller than 1e-6. If
295
so, nothing is done and an exception is thrown. Otherwise, the
296
inverse matrix is calculated and returned. *this is not changed.
290
\warning If the determinant is close to zero, but not == 0.0,
291
this method may behave in unexpected ways and return almost
292
random results; details may depend on your particular floating
293
point implementation. The use of this method is therefore highly
294
discouraged, unless you are certain that the determinant is in a
295
reasonable range, away from 0.0 (Stefan Kebekus)
297
matrix3x3 matrix3x3::inverse(void) const throw(OBError)
298
\warning If the determinant is close to zero, but not == 0.0,
299
this method may behave in unexpected ways and return almost
300
random results; details may depend on your particular floating
301
point implementation. The use of this method is therefore highly
302
discouraged, unless you are certain that the determinant is in a
303
reasonable range, away from 0.0 (Stefan Kebekus)
305
matrix3x3 matrix3x3::inverse(void) const
306
#ifdef OB_OLD_MATH_CHECKS
299
310
double det = determinant();
312
#ifdef OB_OLD_MATH_CHECKS
300
313
if (fabs(det) <= 1e-6)
302
315
OBError er("matrix3x3::invert(void)",
303
316
"The method was called on a matrix with |determinant| <= 1e-6.",
304
317
"This is a runtime or a programming error in your application.");
309
inverse.ele[0][0] = ele[1][1]*ele[2][2] - ele[1][2]*ele[2][1];
310
inverse.ele[1][0] = ele[1][2]*ele[2][0] - ele[1][0]*ele[2][2];
311
inverse.ele[2][0] = ele[1][0]*ele[2][1] - ele[1][1]*ele[2][0];
312
inverse.ele[0][1] = ele[2][1]*ele[0][2] - ele[2][2]*ele[0][1];
313
inverse.ele[1][1] = ele[2][2]*ele[0][0] - ele[2][0]*ele[0][2];
314
inverse.ele[2][1] = ele[2][0]*ele[0][1] - ele[2][1]*ele[0][0];
315
inverse.ele[0][2] = ele[0][1]*ele[1][2] - ele[0][2]*ele[1][1];
316
inverse.ele[1][2] = ele[0][2]*ele[1][0] - ele[0][0]*ele[1][2];
317
inverse.ele[2][2] = ele[0][0]*ele[1][1] - ele[0][1]*ele[1][0];
324
/* This method returns the transpose of a matrix. The original
325
matrix remains unchanged. */
326
matrix3x3 matrix3x3::transpose(void) const
322
matrix3x3 returnValue;
323
returnValue.ele[0][0] = ele[1][1]*ele[2][2] - ele[1][2]*ele[2][1];
324
returnValue.ele[1][0] = ele[1][2]*ele[2][0] - ele[1][0]*ele[2][2];
325
returnValue.ele[2][0] = ele[1][0]*ele[2][1] - ele[1][1]*ele[2][0];
326
returnValue.ele[0][1] = ele[2][1]*ele[0][2] - ele[2][2]*ele[0][1];
327
returnValue.ele[1][1] = ele[2][2]*ele[0][0] - ele[2][0]*ele[0][2];
328
returnValue.ele[2][1] = ele[2][0]*ele[0][1] - ele[2][1]*ele[0][0];
329
returnValue.ele[0][2] = ele[0][1]*ele[1][2] - ele[0][2]*ele[1][1];
330
returnValue.ele[1][2] = ele[0][2]*ele[1][0] - ele[0][0]*ele[1][2];
331
returnValue.ele[2][2] = ele[0][0]*ele[1][1] - ele[0][1]*ele[1][0];
338
/* This method returns the transpose of a matrix. The original
339
matrix remains unchanged. */
340
matrix3x3 matrix3x3::transpose(void) const
342
matrix3x3 returnValue;
330
344
for(unsigned int i=0; i<3; i++)
331
for(unsigned int j=0; j<3; j++)
332
transpose.ele[i][j] = ele[j][i];
337
double matrix3x3::determinant(void) const
341
x = ele[0][0] * (ele[1][1] * ele[2][2] - ele[1][2] * ele[2][1]);
342
y = ele[0][1] * (ele[1][2] * ele[2][0] - ele[1][0] * ele[2][2]);
343
z = ele[0][2] * (ele[1][0] * ele[2][1] - ele[1][1] * ele[2][0]);
348
/*! This method returns false if there are indices i,j such that
349
fabs(*this[i][j]-*this[j][i]) > 1e-6. Otherwise, it returns
351
bool matrix3x3::isSymmetric(void) const
353
if (fabs(ele[0][1] - ele[1][0]) > 1e-6)
355
if (fabs(ele[0][2] - ele[2][0]) > 1e-6)
357
if (fabs(ele[1][2] - ele[2][1]) > 1e-6)
362
/*! This method returns false if there are indices i != j such
363
that fabs(*this[i][j]) > 1e-6. Otherwise, it returns true. */
364
bool matrix3x3::isDiagonal(void) const
366
if (fabs(ele[0][1]) > 1e-6)
368
if (fabs(ele[0][2]) > 1e-6)
370
if (fabs(ele[1][2]) > 1e-6)
373
if (fabs(ele[1][0]) > 1e-6)
375
if (fabs(ele[2][0]) > 1e-6)
377
if (fabs(ele[2][1]) > 1e-6)
383
/*! This method returns false if there are indices i != j such
384
that fabs(*this[i][j]) > 1e-6, or if there is an index i such
385
that fabs(*this[i][j]-1) > 1e-6. Otherwise, it returns
387
bool matrix3x3::isUnitMatrix(void) const
392
if (fabs(ele[0][0]-1) > 1e-6)
394
if (fabs(ele[1][1]-1) > 1e-6)
396
if (fabs(ele[2][2]-1) > 1e-6)
402
/*! This method employs the static method matrix3x3::jacobi(...)
403
to find the eigenvalues and eigenvectors of a symmetric
404
matrix. On entry it is checked if the matrix really is
405
symmetric: if isSymmetric() returns 'false', an OBError is
408
\note The jacobi algorithm is should work great for all
409
symmetric 3x3 matrices. If you need to find the eigenvectors
410
of a non-symmetric matrix, you might want to resort to the
411
sophisticated routines of LAPACK.
413
@param eigenvals a reference to a vector3 where the
414
eigenvalues will be stored. The eigenvalues are ordered so
415
that eigenvals[0] <= eigenvals[1] <= eigenvals[2].
417
@return an orthogonal matrix whose ith column is an
418
eigenvector for the eigenvalue eigenvals[i]. Here 'orthogonal'
419
means that all eigenvectors have length one and are mutually
420
orthogonal. The ith eigenvector can thus be conveniently
421
accessed by the GetColumn() method, as in the following
424
// Calculate eigenvectors and -values
426
matrix3x3 eigenmatrix = somematrix.findEigenvectorsIfSymmetric(eigenvals);
428
// Print the 2nd eigenvector
429
cout << eigenmatrix.GetColumn(1) << endl;
431
With these conventions, a matrix is diagonalized in the following way:
433
// Diagonalize the matrix
434
matrix3x3 diagonalMatrix = eigenmatrix.inverse() * somematrix * eigenmatrix;
438
matrix3x3 matrix3x3::findEigenvectorsIfSymmetric(vector3 &eigenvals) const throw(OBError)
345
for(unsigned int j=0; j<3; j++)
346
returnValue.ele[i][j] = ele[j][i];
351
double matrix3x3::determinant(void) const
353
return( ele[0][0] * (ele[1][1] * ele[2][2] - ele[1][2] * ele[2][1])
354
+ ele[0][1] * (ele[1][2] * ele[2][0] - ele[1][0] * ele[2][2])
355
+ ele[0][2] * (ele[1][0] * ele[2][1] - ele[1][1] * ele[2][0]) );
358
/*! \return False if there are indices i,j such that
359
fabs(*this[i][j]-*this[j][i]) > 1e-6. Otherwise, it returns
361
bool matrix3x3::isSymmetric(void) const
363
return( IsApprox( ele[0][1], ele[1][0], 1e-6 )
364
&& IsApprox( ele[0][2], ele[2][0], 1e-6 )
365
&& IsApprox( ele[1][2], ele[2][1], 1e-6 ) );
368
/*! This method returns true if and only if the matrix is
369
* (approximately) a diagonal matrix. The precision used
370
* by this function is 1e-6. */
371
bool matrix3x3::isDiagonal(void) const
373
return( IsNegligible( ele[1][0], ele[0][0], 1e-6 )
374
&& IsNegligible( ele[2][0], ele[0][0], 1e-6 )
375
&& IsNegligible( ele[0][1], ele[1][1], 1e-6 )
376
&& IsNegligible( ele[2][1], ele[1][1], 1e-6 )
377
&& IsNegligible( ele[0][2], ele[2][2], 1e-6 )
378
&& IsNegligible( ele[1][2], ele[2][2], 1e-6 ) );
381
/*! This method returns true if and only if the matrix is
382
* (approximately) equal to the identity matrix. The precision used
383
* by this function is 1e-6. */
384
bool matrix3x3::isUnitMatrix(void) const
386
return ( isDiagonal()
387
&& IsApprox( ele[0][0], 1.0, 1e-6 )
388
&& IsApprox( ele[1][1], 1.0, 1e-6 )
389
&& IsApprox( ele[2][2], 1.0, 1e-6 ) );
392
/*! This method employs the static method matrix3x3::jacobi(...)
393
to find the eigenvalues and eigenvectors of a symmetric
394
matrix. On entry it is checked if the matrix really is
395
symmetric: if isSymmetric() returns 'false', an OBError is
398
\note The jacobi algorithm is should work great for all
399
symmetric 3x3 matrices. If you need to find the eigenvectors
400
of a non-symmetric matrix, you might want to resort to the
401
sophisticated routines of LAPACK.
403
@param eigenvals a reference to a vector3 where the
404
eigenvalues will be stored. The eigenvalues are ordered so
405
that eigenvals[0] <= eigenvals[1] <= eigenvals[2].
407
@return an orthogonal matrix whose ith column is an
408
eigenvector for the eigenvalue eigenvals[i]. Here 'orthogonal'
409
means that all eigenvectors have length one and are mutually
410
orthogonal. The ith eigenvector can thus be conveniently
411
accessed by the GetColumn() method, as in the following
414
// Calculate eigenvectors and -values
416
matrix3x3 eigenmatrix = somematrix.findEigenvectorsIfSymmetric(eigenvals);
418
// Print the 2nd eigenvector
419
cout << eigenmatrix.GetColumn(1) << endl;
421
With these conventions, a matrix is diagonalized in the following way:
423
// Diagonalize the matrix
424
matrix3x3 diagonalMatrix = eigenmatrix.inverse() * somematrix * eigenmatrix;
428
matrix3x3 matrix3x3::findEigenvectorsIfSymmetric(vector3 &eigenvals) const
429
#ifdef OB_OLD_MATH_CHECKS
440
433
matrix3x3 result;
435
#ifdef OB_OLD_MATH_CHECKS
442
436
if (!isSymmetric())
444
438
OBError er("matrix3x3::findEigenvectorsIfSymmetric(vector3 &eigenvals) const throw(OBError)",
445
439
"The method was called on a matrix that was not symmetric, i.e. where isSymetric() == false.",
446
440
"This is a runtime or a programming error in your application.");
451
446
matrix3x3 copyOfThis = *this;
499
483
ele[2][2] = V / (sin(Gamma)); // again, we factored out A * B when defining V
502
ostream& operator<< ( ostream& co, const matrix3x3& m )
486
/** Print a text representation of the matrix in the standardized form:
490
where the letters represent the appropriate entries in the matrix.
491
Uses the standard output format for the individual entries, separated
492
by ", " for each column, and [ ] indicating each row.
494
ostream& operator<< ( ostream& co, const matrix3x3& m )
532
/*! This static function computes the eigenvalues and
533
eigenvectors of a SYMMETRIC nxn matrix. This method is used
534
internally by OpenBabel, but may be useful as a general
537
The algorithm uses Jacobi transformations. It is described
538
e.g. in Wilkinson, Reinsch "Handbook for automatic computation,
539
Volume II: Linear Algebra", part II, contribution II/1. The
540
implementation is also similar to the implementation in this
541
book. This method is adequate to solve the eigenproblem for
542
small matrices, of size perhaps up to 10x10. For bigger
543
problems, you might want to resort to the sophisticated routines
546
\note If you plan to find the eigenvalues of a symmetric 3x3
547
matrix, you will probably prefer to use the more convenient
548
method findEigenvectorsIfSymmetric()
550
@param n the size of the matrix that should be diagonalized
552
@param a array of size n^2 which holds the symmetric matrix
553
whose eigenvectors are to be computed. The convention is that
554
the entry in row r and column c is addressed as a[n*r+c] where,
555
of course, 0 <= r < n and 0 <= c < n. There is no check that the
556
matrix is actually symmetric. If it is not, the behaviour of
557
this function is undefined. On return, the matrix is
558
overwritten with junk.
560
@param d pointer to a field of at least n doubles which will be
561
overwritten. On return of this function, the entries d[0]..d[n-1]
562
will contain the eigenvalues of the matrix.
564
@param v an array of size n^2 where the eigenvectors will be
565
stored. On return, the columns of this matrix will contain the
566
eigenvectors. The eigenvectors are normalized and mutually
569
void matrix3x3::jacobi(unsigned int n, double *a, double *d, double *v)
524
/*! This static function computes the eigenvalues and
525
eigenvectors of a SYMMETRIC nxn matrix. This method is used
526
internally by OpenBabel, but may be useful as a general
529
The algorithm uses Jacobi transformations. It is described
530
e.g. in Wilkinson, Reinsch "Handbook for automatic computation,
531
Volume II: Linear Algebra", part II, contribution II/1. The
532
implementation is also similar to the implementation in this
533
book. This method is adequate to solve the eigenproblem for
534
small matrices, of size perhaps up to 10x10. For bigger
535
problems, you might want to resort to the sophisticated routines
538
\note If you plan to find the eigenvalues of a symmetric 3x3
539
matrix, you will probably prefer to use the more convenient
540
method findEigenvectorsIfSymmetric()
542
@param n the size of the matrix that should be diagonalized
544
@param a array of size n^2 which holds the symmetric matrix
545
whose eigenvectors are to be computed. The convention is that
546
the entry in row r and column c is addressed as a[n*r+c] where,
547
of course, 0 <= r < n and 0 <= c < n. There is no check that the
548
matrix is actually symmetric. If it is not, the behaviour of
549
this function is undefined. On return, the matrix is
550
overwritten with junk.
552
@param d pointer to a field of at least n doubles which will be
553
overwritten. On return of this function, the entries d[0]..d[n-1]
554
will contain the eigenvalues of the matrix.
556
@param v an array of size n^2 where the eigenvectors will be
557
stored. On return, the columns of this matrix will contain the
558
eigenvectors. The eigenvectors are normalized and mutually
561
void matrix3x3::jacobi(unsigned int n, double *a, double *d, double *v)
571
563
double onorm, dnorm;
572
564
double b, dma, q, t, c, s;
573
565
double atemp, vtemp, dtemp;