Quaternion + determinant representation of improper rotations.

Former-commit-id: fbc101d997ae85565584a3f0ea9c583360fbabec
This commit is contained in:
Marek Nečada 2019-02-25 09:52:23 +02:00
parent ee73a8f029
commit efaf3211e4
5 changed files with 92 additions and 2 deletions

View File

@ -8,7 +8,7 @@ Installation
The package depends on numpy, scipy, cython and GSL (>= 2.0).
The first three can be obtained by pip. If you have a recent enough OS,
you can get GSL easily from the repositories; on Debian and derivatives,
just run `apt-get install libgsl-dev` under root. Alternatively,
just run ``apt-get install libgsl-dev`` under root. Alternatively,
you can `get the source
<https://www.gnu.org/software/gsl/>`_ get the source and compile it yourself.

View File

@ -629,7 +629,7 @@ cdef class cquat:
Wrapper of the qpms_quat_t object, with the functionality
to evaluate Wigner D-matrix elements.
'''
cdef qpms_quat_t q
cdef readonly qpms_quat_t q
def __cinit__(self, double w, double x, double y, double z):
cdef qpms_quat4d_t p
@ -727,3 +727,54 @@ cdef class cquat:
return 0
return qpms_wignerD_elem(self.q, l, mp, m)
cdef class irot3:
'''
Wrapper over the C type qpms_irot3_t.
'''
cdef qpms_irot3_t qd
def __cinit__(self, cquat q, short det): # TODO implement a constructor with no arguments
if (det != 1 and det != -1):
raise ValueError("Improper rotation determinant has to be 1 or -1")
self.qd.rot = q.normalise().q
self.qd.det = det
property rot:
'''
The proper rotation part of the irot3 type.
'''
def __get__(self):
res = cquat(0,0,0,0)
res.q = self.qd.rot
return res
def __set__(self, cquat r):
# TODO check for non-zeroness and throw an exception if norm is zero
self.qd.rot = r.normalise().q
property det:
'''
The determinant of the improper rotation.
'''
def __get__(self):
return self.qd.det
def __set__(self, d):
d = int(d)
if (d != 1 and d != -1):
raise ValueError("Improper rotation determinant has to be 1 or -1")
self.qd.det = d
def __repr__(self): # TODO make this look like a quaternion with i,j,k
return '(' + repr(self.rot) + ', ' + repr(self.det) + ')'
def __mul__(irot3 self, irot3 other):
res = irot3(cquat(1,0,0,0), 1)
res.qd = qpms_irot3_mult(self.qd, other.qd)
return res
def __pow__(irot3 self, int n, _):
res = irot3(cquat(1,0,0,0), 1)
res.qd = qpms_irot3_pow(self.qd, n)
return res

View File

@ -97,6 +97,11 @@ cdef extern from "wigner.h":
qpms_quat_t qpms_quat_pow(qpms_quat_t q, double exponent)
cdouble qpms_wignerD_elem(qpms_quat_t q, qpms_l_t l,
qpms_m_t mp, qpms_m_t m)
struct qpms_irot3_t:
qpms_quat_t rot
short det
qpms_irot3_t qpms_irot3_mult(qpms_irot3_t p, qpms_irot3_t q)
qpms_irot3_t qpms_irot3_pow(qpms_irot3_t p, int n)
#cdef extern from "numpy/arrayobject.h":

View File

@ -6,6 +6,8 @@
#include "qpms_types.h"
#include "tiny_inlines.h"
/// Quaternion type.
/**
* Internaly represented as a pair of complex numbers,
@ -20,6 +22,12 @@ typedef struct qpms_quat4d_t {
double c1, ci, cj, ck;
} qpms_quat4d_t;
/// 3D improper rotations represented as a quaternion and a sign of the determinant.
typedef struct qpms_irot3_t {
qpms_quat_t rot; ///< Quaternion representing the rotation part.
short det; ///< Determinant of the transformation (valid values are 1 (rotation) or -1 (improper rotation)
} qpms_irot3_t;
/// Conversion from the 4*double to the 2*complex quaternion.
// TODO is this really correct?
// I.e. do the axis from moble's text match this convention?
@ -134,4 +142,29 @@ static inline qpms_quat_t qpms_quat_pow(const qpms_quat_t q, const double expone
complex double qpms_wignerD_elem(qpms_quat_t q, qpms_l_t l,
qpms_m_t mp, qpms_m_t m);
static inline int qpms_irot3_checkdet(const qpms_irot3_t p) {
if (p.det != 1 && p.det != -1) abort();
return 0;
}
/// Improper rotation multiplication.
static inline qpms_irot3_t qpms_irot3_mult(const qpms_irot3_t p, const qpms_irot3_t q) {
#ifndef NDEBUG
qpms_irot3_checkdet(p);
qpms_irot3_checkdet(q);
#endif
const qpms_irot3_t r = {qpms_quat_normalise(qpms_quat_mult(p.rot, q.rot)), p.det*q.det};
return r;
}
/// Improper rotation power \f$ p^n \f$.
static inline qpms_irot3_t qpms_irot3_pow(const qpms_irot3_t p, int n) {
#ifndef NDEBUG
qpms_irot3_checkdet(p);
#endif
const qpms_irot3_t r = {qpms_quat_normalise(qpms_quat_pow(p.rot, n)),
p.det == -1 ? min1pow(n) : 1};
return r;
}
#endif //QPMS_WIGNER_H

1
setup.py Normal file → Executable file
View File

@ -42,6 +42,7 @@ setup(name='qpms',
install_requires=['cython>=0.21','quaternion','spherical_functions',
#'py_gmm' # no longer needed
],
# TODO implement https://stackoverflow.com/questions/17366784/setuptools-unable-to-use-link-from-dependency-links and update README.md accordingly
dependency_links=['https://github.com/texnokrates/py_gmm','https://github.com/moble/quaternion','https://github.com/moble/spherical_functions'],
ext_modules=cythonize([qpms_c], include_path=['qpms']),
cmdclass = {'build_ext': build_ext},