1 from .cybspec cimport BaseSpec
2 from .qpms_cdefs cimport
*
7 def complex_crep
(complex c
, parentheses
= False
, shortI
= True
, has_Imaginary
= False
):
9 Return a C-code compatible string representation of a (python) complex number.
11 return ( ('(' if parentheses
else '')
13 + ('+' if math
.copysign
(1, c
.imag
) >= 0 else '')
15 + ('*I' if shortI
else '*_Imaginary_I' if has_Imaginary
else '*_Complex_I')
16 + (')' if parentheses
else '')
22 Wrapper of the qpms_quat_t object, with the functionality
23 to evaluate Wigner D-matrix elements.
25 # cdef readonly qpms_quat_t q # pxd
27 def __cinit__
(self, double w
, double x
, double y
, double z
):
33 self.q
= qpms_quat_2c_from_4d
(p
)
40 def __repr__
(self): # TODO make this look like a quaternion with i,j,k
43 def __add__
(CQuat
self, CQuat other
):
44 # TODO add real numbers
46 res
.q
= qpms_quat_add
(self.q
, other
.q
)
49 def __mul__
(self, other
):
51 if isinstance(self, CQuat
):
52 if isinstance(other
, CQuat
):
53 res
.q
= qpms_quat_mult
(self.q
, other
.q
)
54 elif isinstance(other
, (int, float)):
55 res
.q
= qpms_quat_rscale
(other
, self.q
)
56 else: return NotImplemented
57 elif isinstance(self, (int, float)):
58 if isinstance(other
, CQuat
):
59 res
.q
= qpms_quat_rscale
(self, other
.q
)
60 else: return NotImplemented
63 def __neg__
(CQuat
self):
65 res
.q
= qpms_quat_rscale
(-1, self.q
)
68 def __sub__
(CQuat
self, CQuat other
):
70 res
.q
= qpms_quat_add
(self.q
, qpms_quat_rscale
(-1,other
.q
))
74 return qpms_quat_norm
(self.q
)
77 return qpms_quat_norm
(self.q
)
80 return qpms_quat_imnorm
(self.q
)
84 res
.q
= qpms_quat_exp
(self.q
)
89 res
.q
= qpms_quat_exp
(self.q
)
92 def __pow__
(CQuat
self, double other
, _
):
94 res
.q
= qpms_quat_pow
(self.q
, other
)
99 res
.q
= qpms_quat_normalise
(self.q
)
102 def isclose
(CQuat
self, CQuat other
, rtol
=1e-5, atol
=1e-8):
104 Checks whether two quaternions are "almost equal".
106 return abs(self - other
) <= (atol
+ rtol
* abs(other
))
110 Quaternion representation as two complex numbers
113 return (self.q
.a
, self.q
.b
)
114 def __set__
(self, RaRb
):
120 Quaternion representation as four real numbers
124 p
= qpms_quat_4d_from_2c
(self.q
)
125 return (p
.c1
, p
.ci
, p
.cj
, p
.ck
)
126 def __set__
(self, wxyz
):
132 self.q
= qpms_quat_2c_from_4d
(p
)
136 Returns a string that can be used in C code to initialise a qpms_irot3_t
138 return '{' + complex_crep
(self.q
.a
) + ', ' + complex_crep
(self.q
.b
) + '}'
140 def wignerDelem
(self, qpms_l_t l
, qpms_m_t mp
, qpms_m_t m
):
142 Returns an element of a bosonic Wigner matrix.
144 # don't crash on bad l, m here
145 if (abs(m
) > l
or abs(mp
) > l
):
147 return qpms_wignerD_elem
(self.q
, l
, mp
, m
)
150 def from_rotvector
(vec
):
151 if vec
.shape
!= (3,):
152 raise ValueError("Single 3d vector expected")
158 res
.q
= qpms_quat_from_rotvector
(v
)
163 Wrapper over the C type qpms_irot3_t.
165 #cdef readonly qpms_irot3_t qd
167 def __cinit__
(self, *args
):
171 # TODO implement a constructor with
172 # - tuple as argument ...?
173 if (len(args
) == 0): # no args, return identity
177 elif (len(args
) == 2 and isinstance(args
[0], CQuat
) and isinstance(args
[1], (int, float))):
178 # The original __cinit__(self, CQuat q, short det) constructor
181 if (det
!= 1 and det
!= -1):
182 raise ValueError("Improper rotation determinant has to be 1 or -1")
183 self.qd
.rot
= q
.normalise
().q
185 elif (len(args
) == 1 and isinstance(args
[0], IRot3
)):
188 elif (len(args
) == 1 and isinstance(args
[0], CQuat
)):
189 # proper rotation from a quaternion
192 self.qd
.rot
= q
.normalise
().q
195 raise ValueError('Unsupported constructor arguments')
197 cdef void cset
(self, qpms_irot3_t qd
):
201 res
= IRot3
(CQuat
(1,0,0,0),1)
207 The proper rotation part of the IRot3 type.
213 def __set__
(self, CQuat r
):
214 # TODO check for non-zeroness and throw an exception if norm is zero
215 self.qd
.rot
= r
.normalise
().q
219 The determinant of the improper rotation.
223 def __set__
(self, d
):
225 if (d
!= 1 and d
!= -1):
226 raise ValueError("Improper rotation determinant has to be 1 or -1")
229 def __repr__
(self): # TODO make this look like a quaternion with i,j,k
230 return '(' + repr(self.rot
) + ', ' + repr(self.det
) + ')'
234 Returns a string that can be used in C code to initialise a qpms_irot3_t
236 return '{' + self.rot
.crepr
() + ', ' + repr(self.det
) + '}'
238 def __mul__
(IRot3
self, IRot3 other
):
239 res
= IRot3
(CQuat
(1,0,0,0), 1)
240 res
.qd
= qpms_irot3_mult
(self.qd
, other
.qd
)
243 def __pow__
(IRot3
self, n
, _
):
248 raise ValueError("The exponent of an IRot3 has to have an integer value.")
249 res
= IRot3
(CQuat
(1,0,0,0), 1)
250 res
.qd
= qpms_irot3_pow
(self.qd
, n
)
253 def isclose
(IRot3
self, IRot3 other
, rtol
=1e-5, atol
=1e-8):
255 Checks whether two (improper) rotations are "almost equal".
256 Returns always False if the determinants are different.
258 if self.det
!= other
.det
:
260 return (self.rot
.isclose
(other
.rot
, rtol
=rtol
, atol
=atol
)
261 # unit quaternions are a double cover of SO(3), i.e.
262 # minus the same quaternion represents the same rotation
263 or self.rot
.isclose
(-(other
.rot
), rtol
=rtol
, atol
=atol
)
266 # Several 'named constructors' for convenience
270 Returns an IRot3 object representing the 3D spatial inversion.
279 Returns an IRot3 object representing the 3D xy-plane mirror symmetry (z axis sign flip).
282 r
.rot
= CQuat
(0,0,0,1) # π-rotation around z-axis
283 r
.det
= -1 # inversion
289 Returns an IRot3 object representing the 3D xz-plane mirror symmetry (y axis sign flip).
292 r
.rot
= CQuat
(0,0,1,0) # π-rotation around y-axis
293 r
.det
= -1 # inversion
299 Returns an IRot3 object representing the 3D yz-plane mirror symmetry (x axis sign flip).
302 r
.rot
= CQuat
(0,1,0,0) # π-rotation around x-axis
303 r
.det
= -1 # inversion
309 Returns an IRot3 object representing a \f$ C_n $\f rotation (around the z-axis).
312 r
.rot
= CQuat
(math
.cos
(math
.pi
/n
),0,0,math
.sin
(math
.pi
/n
))
318 An alias for the constructor without arguments; returns identity.
322 def as_uvswf_matrix
(IRot3
self, BaseSpec bspec
):
324 Returns the uvswf representation of the current transform as a numpy array
326 cdef ssize_t sz
= len(bspec
)
327 cdef np
.ndarray m
= np
.empty
((sz
, sz
), dtype
=complex, order
='C') # FIXME explicit dtype
328 cdef cdouble
[:, ::1] view
= m
329 qpms_irot3_uvswfi_dense
(&view
[0,0], bspec
.rawpointer
(), self.qd
)