Euler Angles in PyChemia¶
Short Version¶
You can use the following routines to obtain the \(k(k-1)/2\) Generalized Euler angles from a orhogonal matrix of dimension k and for building the orthogonal matrix from a set of angles:
>>> angles_list = pychemia.utils.mathematics.gea_all_angles(ortho_matrix)
>>> ortho_matrix = gea_orthogonal_from_angles(angles_list)
Remember that the list of angles matters. The SO(k) group is non-abelian.
The algorithm is based on the paper:
Generalization of Euler Angles to N-Dimensional Orthogonal Matrices
David K. Hoffman, Richard C. Raffenetti, and Klaus Ruedenberg
Journal of Mathematical Physics 13, 528 (1972)
doi: 10.1063/1.1666011
Long Version¶
Rotation on a two-dimensional plane can be described with one angle. For a three dimensional space 3 angles are needed. You can define angles around each axis and general rotation matrices as a product of three single axis rotation matrices.
In general, for k dimensions, the number of angles is \(k(k-1)/2\). One angle for each plane that you can get from any pair among the k vectors defining the space.
Any orthogonal matrix (with determinant equal to +1) represent a rotation matrix for the space that its dimension. Sometimes could be necessary to obtain the set of independent angles that the orthogonal matrix represents and having a way of regaining the original orthogonal matrix from a given set of angles.
One the reasons for moving between one orthogonal matrix and its angles is reducing an orthogonal matrix to its minimal independent parameters. The paper intitled: “Generalization of Euler Angles to N-Dimensional Orthogonal Matrices” provides an effective way to get the so called Euler angles for matrices of arbitrary dimension and reconstitute the matrix from a given set of angles.
This algorithm is implemented on PyChemia. As an example, lets build first a 7-dimensional orthogonal matrix by a QR decomposition:
In [1]: import pychemia
In [2]: import numpy as np
In [3]: np.set_printoptions(linewidth=200, suppress=True, precision=5)
In [4]: ortho = pychemia.utils.mathematics.gram_smith_qr(7)
In [5]:ortho
Out[5]:
array([[-0.23503793, -0.6039233 , 0.31346146, 0.56383925, 0.18441349, -0.35292927, 0.07275729],
[-0.31380685, -0.21108679, -0.25878856, -0.40710624, -0.23657101, -0.54173578, -0.52423003],
[-0.48237691, 0.1056854 , -0.2376831 , 0.11195934, 0.63817833, 0.3621268 , -0.38562619],
[-0.44643339, -0.33091574, 0.37743259, -0.27151849, -0.39852265, 0.56178535, 0.02431587],
[-0.46408875, 0.67693995, 0.31398053, 0.30715275, -0.28175741, -0.23147005, -0.02194835],
[-0.43149085, 0.00696596, -0.2664925 , -0.31313612, 0.19768295, -0.20337001, 0.75117024],
[-0.11282486, -0.10838891, -0.68280287, 0.48754035, -0.47483487, 0.20071602, 0.07649819]])
The variable ‘ortho’ is an orthogonal matrix as you can easily show by computing its determinant:
In [6]: np.linalg.det(ortho)
Out[6]: 1.0
Now, for a 7-dimension space we should expect 21 generalized Euler angles. We can get them by calling the function:
In [8]: angles_list = pychemia.utils.mathematics.gea_all_angles(ortho)
In [9]: np.array(angles_list)
Out[9]:
array([ -0.1392 , -0.03975, -0.37052, -0.48339, 0.13503, 0.39547, -0.22866, -0.31214, -0.57967, 0.84238,
-2.66098, 0.05294, 0.3535 , -0.83619, -0.06953, -0.51353, -1.01991, 2.28529, -0.25373, -0.33108,
-2.27736])
In fact we got 21 angles, all the angles in the range \([-\pi, \pi]\) We can rebuild the original orthogonal matrix from those angles with:
In [10]: matrix=pychemia.utils.mathematics.gea_orthogonal_from_angles(angles)
In fact, we can verify that we recover the original matrix:
In [11]: np.max(matrix-ortho)
Out[11]: 3.3306690738754696e-16