1 from .cybspec cimport BaseSpec
2 from .qpms_cdefs cimport
*
8 def complex_crep
(complex c
, parentheses
= False
, shortI
= True
, has_Imaginary
= False
):
10 Return a C-code compatible string representation of a (python) complex number.
12 return ( ('(' if parentheses
else '')
14 + ('+' if math
.copysign
(1, c
.imag
) >= 0 else '')
16 + ('*I' if shortI
else '*_Imaginary_I' if has_Imaginary
else '*_Complex_I')
17 + (')' if parentheses
else '')
23 Wrapper of the qpms_quat_t object, with the functionality
24 to evaluate Wigner D-matrix elements.
26 # cdef readonly qpms_quat_t q # pxd
28 def __cinit__
(self, double w
, double x
, double y
, double z
):
34 self.q
= qpms_quat_2c_from_4d
(p
)
41 def __repr__
(self): # TODO make this look like a quaternion with i,j,k
44 def __add__
(CQuat
self, CQuat other
):
45 # TODO add real numbers
47 res
.q
= qpms_quat_add
(self.q
, other
.q
)
50 def __mul__
(self, other
):
52 if isinstance(self, CQuat
):
53 if isinstance(other
, CQuat
):
54 res
.q
= qpms_quat_mult
(self.q
, other
.q
)
55 elif isinstance(other
, (int, float)):
56 res
.q
= qpms_quat_rscale
(other
, self.q
)
57 else: return NotImplemented
58 elif isinstance(self, (int, float)):
59 if isinstance(other
, CQuat
):
60 res
.q
= qpms_quat_rscale
(self, other
.q
)
61 else: return NotImplemented
64 def __neg__
(CQuat
self):
66 res
.q
= qpms_quat_rscale
(-1, self.q
)
69 def __sub__
(CQuat
self, CQuat other
):
71 res
.q
= qpms_quat_add
(self.q
, qpms_quat_rscale
(-1,other
.q
))
75 return qpms_quat_norm
(self.q
)
78 return qpms_quat_norm
(self.q
)
81 return qpms_quat_imnorm
(self.q
)
85 res
.q
= qpms_quat_exp
(self.q
)
90 res
.q
= qpms_quat_exp
(self.q
)
93 def __pow__
(CQuat
self, double other
, _
):
95 res
.q
= qpms_quat_pow
(self.q
, other
)
100 res
.q
= qpms_quat_normalise
(self.q
)
103 def isclose
(CQuat
self, CQuat other
, rtol
=1e-5, atol
=1e-8):
105 Checks whether two quaternions are "almost equal".
107 return abs(self - other
) <= (atol
+ rtol
* abs(other
))
111 Quaternion representation as two complex numbers
114 return (self.q
.a
, self.q
.b
)
115 def __set__
(self, RaRb
):
121 Quaternion representation as four real numbers
125 p
= qpms_quat_4d_from_2c
(self.q
)
126 return (p
.c1
, p
.ci
, p
.cj
, p
.ck
)
127 def __set__
(self, wxyz
):
133 self.q
= qpms_quat_2c_from_4d
(p
)
137 Returns a string that can be used in C code to initialise a qpms_irot3_t
139 return '{' + complex_crep
(self.q
.a
) + ', ' + complex_crep
(self.q
.b
) + '}'
141 def wignerDelem
(self, qpms_l_t l
, qpms_m_t mp
, qpms_m_t m
):
143 Returns an element of a bosonic Wigner matrix.
145 # don't crash on bad l, m here
146 if (abs(m
) > l
or abs(mp
) > l
):
148 return qpms_wignerD_elem
(self.q
, l
, mp
, m
)
151 def from_rotvector
(vec
):
153 Constructs a quaternion representing rotation around a given rotation vector.
157 vec: array_like of shape (3,)
158 Cartesian components of the rotation vector.
164 vec
= np
.array
(vec
, copy
=False
)
165 if vec
.shape
!= (3,):
166 raise ValueError("Single 3d vector expected")
172 res
.q
= qpms_quat_from_rotvector
(v
)
177 Wrapper over the C type qpms_irot3_t.
179 #cdef readonly qpms_irot3_t qd
181 def __cinit__
(self, *args
):
185 # TODO implement a constructor with
186 # - tuple as argument ...?
187 if (len(args
) == 0): # no args, return identity
191 elif (len(args
) == 2 and isinstance(args
[0], CQuat
) and isinstance(args
[1], (int, float))):
192 # The original __cinit__(self, CQuat q, short det) constructor
195 if (det
!= 1 and det
!= -1):
196 raise ValueError("Improper rotation determinant has to be 1 or -1")
197 self.qd
.rot
= q
.normalise
().q
199 elif (len(args
) == 1 and isinstance(args
[0], IRot3
)):
202 elif (len(args
) == 1 and isinstance(args
[0], CQuat
)):
203 # proper rotation from a quaternion
206 self.qd
.rot
= q
.normalise
().q
209 raise ValueError('Unsupported constructor arguments')
211 cdef void cset
(self, qpms_irot3_t qd
):
215 res
= IRot3
(CQuat
(1,0,0,0),1)
221 The proper rotation part of the IRot3 type.
227 def __set__
(self, CQuat r
):
228 # TODO check for non-zeroness and throw an exception if norm is zero
229 self.qd
.rot
= r
.normalise
().q
233 The determinant of the improper rotation.
237 def __set__
(self, d
):
239 if (d
!= 1 and d
!= -1):
240 raise ValueError("Improper rotation determinant has to be 1 or -1")
243 def __repr__
(self): # TODO make this look like a quaternion with i,j,k
244 return '(' + repr(self.rot
) + ', ' + repr(self.det
) + ')'
248 Returns a string that can be used in C code to initialise a qpms_irot3_t
250 return '{' + self.rot
.crepr
() + ', ' + repr(self.det
) + '}'
252 def __mul__
(IRot3
self, IRot3 other
):
253 res
= IRot3
(CQuat
(1,0,0,0), 1)
254 res
.qd
= qpms_irot3_mult
(self.qd
, other
.qd
)
257 def __pow__
(IRot3
self, n
, _
):
262 raise ValueError("The exponent of an IRot3 has to have an integer value.")
263 res
= IRot3
(CQuat
(1,0,0,0), 1)
264 res
.qd
= qpms_irot3_pow
(self.qd
, n
)
267 def isclose
(IRot3
self, IRot3 other
, rtol
=1e-5, atol
=1e-8):
269 Checks whether two (improper) rotations are "almost equal".
270 Returns always False if the determinants are different.
272 if self.det
!= other
.det
:
274 return (self.rot
.isclose
(other
.rot
, rtol
=rtol
, atol
=atol
)
275 # unit quaternions are a double cover of SO(3), i.e.
276 # minus the same quaternion represents the same rotation
277 or self.rot
.isclose
(-(other
.rot
), rtol
=rtol
, atol
=atol
)
280 # Several 'named constructors' for convenience
284 Returns an IRot3 object representing the 3D spatial inversion.
293 Returns an IRot3 object representing the 3D xy-plane mirror symmetry (z axis sign flip).
296 r
.rot
= CQuat
(0,0,0,1) # π-rotation around z-axis
297 r
.det
= -1 # inversion
303 Returns an IRot3 object representing the 3D xz-plane mirror symmetry (y axis sign flip).
306 r
.rot
= CQuat
(0,0,1,0) # π-rotation around y-axis
307 r
.det
= -1 # inversion
313 Returns an IRot3 object representing the 3D yz-plane mirror symmetry (x axis sign flip).
316 r
.rot
= CQuat
(0,1,0,0) # π-rotation around x-axis
317 r
.det
= -1 # inversion
323 Returns an IRot3 object representing a \f$ C_n $\f rotation (around the z-axis).
326 r
.rot
= CQuat
(math
.cos
(math
.pi
/n
),0,0,math
.sin
(math
.pi
/n
))
332 An alias for the constructor without arguments; returns identity.
336 def as_uvswf_matrix
(IRot3
self, BaseSpec bspec
):
338 Returns the uvswf representation of the current transform as a numpy array
340 cdef ssize_t sz
= len(bspec
)
341 cdef np
.ndarray m
= np
.empty
((sz
, sz
), dtype
=complex, order
='C') # FIXME explicit dtype
342 cdef cdouble
[:, ::1] view
= m
343 qpms_irot3_uvswfi_dense
(&view
[0,0], bspec
.rawpointer
(), self.qd
)