result (and object interpretation) "flows" from rotation and data shapes
This commit is contained in:
parent
b3f98ab877
commit
0704f1122e
|
@ -375,6 +375,11 @@ class Rotation:
|
||||||
Return self@other.
|
Return self@other.
|
||||||
|
|
||||||
Rotate vector, second-order tensor, or fourth-order tensor.
|
Rotate vector, second-order tensor, or fourth-order tensor.
|
||||||
|
`other` is interpreted as an array of tensor quantities with the highest-possible order
|
||||||
|
considering the shape of `self`.
|
||||||
|
For instance, shapes of (2,) and (3,3) for `self` and `other` prompt interpretation of
|
||||||
|
`other` as a second-rank tensor and result in (2,) rotated tensors, whereas
|
||||||
|
shapes of (2,1) and (3,3) for `self` and `other` result in (2,3) rotated vectors.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
@ -388,27 +393,30 @@ class Rotation:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(other, np.ndarray):
|
if isinstance(other, np.ndarray):
|
||||||
if self.shape + (3,) == other.shape:
|
obs = util.shapeblender(self.shape,other.shape,keep_ones=False)[len(self.shape):]
|
||||||
q_m = self.quaternion[...,0]
|
for l in [4,2,1]:
|
||||||
p_m = self.quaternion[...,1:]
|
if obs[-l:] == l*(3,):
|
||||||
A = q_m**2 - np.einsum('...i,...i',p_m,p_m)
|
bs = util.shapeblender(self.shape,other.shape[:-l],False)
|
||||||
B = 2. * np.einsum('...i,...i',p_m,other)
|
self_ = self.broadcast_to(bs) if self.shape != bs else self
|
||||||
C = 2. * _P * q_m
|
if l==1:
|
||||||
return np.block([(A * other[...,i]).reshape(self.shape+(1,)) +
|
q_m = self_.quaternion[...,0]
|
||||||
(B * p_m[...,i]).reshape(self.shape+(1,)) +
|
p_m = self_.quaternion[...,1:]
|
||||||
(C * ( p_m[...,(i+1)%3]*other[...,(i+2)%3]\
|
A = q_m**2 - np.einsum('...i,...i',p_m,p_m)
|
||||||
- p_m[...,(i+2)%3]*other[...,(i+1)%3])).reshape(self.shape+(1,))
|
B = 2. * np.einsum('...i,...i',p_m,other)
|
||||||
for i in [0,1,2]])
|
C = 2. * _P * q_m
|
||||||
if self.shape + (3,3) == other.shape:
|
return np.block([(A * other[...,i]) +
|
||||||
R = self.as_matrix()
|
(B * p_m[...,i]) +
|
||||||
return np.einsum('...im,...jn,...mn',R,R,other)
|
(C * ( p_m[...,(i+1)%3]*other[...,(i+2)%3]
|
||||||
if self.shape + (3,3,3,3) == other.shape:
|
- p_m[...,(i+2)%3]*other[...,(i+1)%3]))
|
||||||
R = self.as_matrix()
|
for i in [0,1,2]]).reshape(bs+(3,),order='F')
|
||||||
return np.einsum('...im,...jn,...ko,...lp,...mnop',R,R,R,R,other)
|
else:
|
||||||
else:
|
return np.einsum({2: '...im,...jn,...mn',
|
||||||
raise ValueError('can only rotate vectors, second-order tensors, and fourth-order tensors')
|
4: '...im,...jn,...ko,...lp,...mnop'}[l],
|
||||||
|
*l*[self_.as_matrix()],
|
||||||
|
other)
|
||||||
|
raise ValueError('can only rotate vectors, second-order tensors, and fourth-order tensors')
|
||||||
elif isinstance(other, Rotation):
|
elif isinstance(other, Rotation):
|
||||||
raise TypeError('use "R1*R2", i.e. multiplication, to compose rotations "R1" and "R2"')
|
raise TypeError('use "R2*R1", i.e. multiplication, to compose rotations "R1" and "R2"')
|
||||||
else:
|
else:
|
||||||
raise TypeError(f'cannot rotate "{type(other)}"')
|
raise TypeError(f'cannot rotate "{type(other)}"')
|
||||||
|
|
||||||
|
|
|
@ -1065,7 +1065,7 @@ class TestRotation:
|
||||||
|
|
||||||
@pytest.mark.parametrize('data',[np.random.rand(4),
|
@pytest.mark.parametrize('data',[np.random.rand(4),
|
||||||
np.random.rand(3,2),
|
np.random.rand(3,2),
|
||||||
np.random.rand(3,2,3,3)])
|
np.random.rand(3,3,3,1)])
|
||||||
def test_rotate_invalid_shape(self,data):
|
def test_rotate_invalid_shape(self,data):
|
||||||
R = Rotation.from_random()
|
R = Rotation.from_random()
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
|
Loading…
Reference in New Issue