def test_is_unitary(self, arr): q = GeneralQuaternion(*arr) assume( q.norm() > DEFAULT_TOLERANCE ) # ignore quaternions of norm==0, whose inverse is numerically unstable assert q.normalized().is_unitary() assert not (2 * q.normalized()).is_unitary()
def __mul__(self, p): if isinstance(p, Quaternion) or isinstance(p, numbers.Number): mul = GeneralQuaternion(*self.coordinates) * p return Quaternion(*mul.coordinates) elif isinstance(p, GeneralQuaternion): return GeneralQuaternion(*self.coordinates) * p elif isinstance(p, Iterable) and len( p) == 3: # applies quaternion rotation on vector return self.matrix.dot(p) else: raise QuaternionError('cant multiply by %s' % type(p))
def from_ra_dec_roll(ra, dec, roll): '''constructs a quaternion from ra/dec/roll params using Tait-Bryan angles XYZ. ra stands for right ascencion, and usually lies in [0, 360] dec stands for declination, and usually lies in [-90, 90] roll stands for rotation/rolling, and usually lies in [0, 360] ''' raq = exp(GeneralQuaternion(0, 0, 0, -np.deg2rad(ra) / 2)) decq = exp(GeneralQuaternion(0, 0, -np.deg2rad(dec) / 2, 0)) rollq = exp(GeneralQuaternion(0, -np.deg2rad(roll) / 2, 0, 0)) q = rollq * decq * raq return Quaternion(*q.coordinates)
def test_inverse(self, arr): q = GeneralQuaternion(*arr) assume( q.norm() > DEFAULT_TOLERANCE ) # ignore quaternions of norm==0, whose inverse is numerically unstable assert q * q.inverse() == q.inverse() * q == GeneralQuaternion.unit() assert q * ~q == ~q * q == GeneralQuaternion.unit()
def from_rotation_vector(xyz): """ Returns the quaternion corresponding to the rotation xyz. Explicitly: rotation occurs along the axis xyz and has angle norm(xyz) This corresponds to the exponential of the quaternion with real part 0 and imaginary part 1/2 * xyz. """ a, b, c = .5 * np.array(xyz) q_exp = exp(GeneralQuaternion(0, a, b, c)) return Quaternion(*q_exp.coordinates)
def test_arithmetics(self, arr): q = GeneralQuaternion(*arr) assert q + q == 2 * q == q * 2 assert q - q == GeneralQuaternion.zero() assert q * GeneralQuaternion.zero() == q * 0 == GeneralQuaternion.zero() assert q * GeneralQuaternion.unit() == q * 1 == q assert GeneralQuaternion.unit() * q == 1 * q == q assert -(-q) == q
def test_log_identity(self): assert log(GeneralQuaternion.unit()) == GeneralQuaternion.zero()
def test_print(self, arr): """ make sure all coordinates are printed. """ q = GeneralQuaternion(*arr) for elem in q.coordinates: expected_string = '{elem:.6g}'.format(**{'elem': elem}) assert expected_string in str(q)
def test_exp_identity(self): assert exp(GeneralQuaternion.zero()) == GeneralQuaternion.unit()
def test_normalized(self, arr): q = GeneralQuaternion(*arr) assume(q.norm() > DEFAULT_TOLERANCE) # ignore quaternions of norm==0, whose inverse is numerically unstable assert q.normalized().norm() == pytest.approx(1, DEFAULT_TOLERANCE)
def test_coordinates(self, arr): q = GeneralQuaternion(*arr) assert q == GeneralQuaternion(*q.coordinates)
def test_conjugate(self, arr): q = GeneralQuaternion(*arr) assert (q + q.conjugate()).is_real() assert (q * q.conjugate()).is_real()
def test_distance(self, arr): q = GeneralQuaternion(*arr) assert q.euclidean_distance(q) == pytest.approx(0) assert q.norm() == q.euclidean_distance(GeneralQuaternion.zero()) == q.euclidean_distance(2 * q)
def test_exp_log(self, arr): assume(np.linalg.norm(arr) > DEFAULT_TOLERANCE) q = GeneralQuaternion(*arr).normalized() assert exp(log(q)) == q assert log(exp(q)) == GeneralQuaternion(*q.coordinates)
def test_exp_norm(self, arr1): q1 = GeneralQuaternion(*arr1) assert exp(q1).norm() == pytest.approx(np.exp(q1.qr)) # |exp(q)| == exp(real(q)|
def test_raises(self, arr): q = GeneralQuaternion(*arr) with pytest.raises(QuaternionError): q + 3
def test_real_imaginary(self, arr): q = GeneralQuaternion(*arr) i, j, k = q.imaginary assert (q.coordinates == [q.real, i, j, k]).all()
def test_equal(self, arr): q = GeneralQuaternion(*arr) assert q == q assert q == q + GeneralQuaternion(0.9 * DEFAULT_TOLERANCE, 0, 0, 0) assert q != q + GeneralQuaternion(1.1 * DEFAULT_TOLERANCE, 0, 0, 0)
def test_log_identical_both_ways(self, arr): assume(np.linalg.norm(arr) > DEFAULT_TOLERANCE) q = GeneralQuaternion(*arr) assert log(q) == q.log()
def test_exp_identical_both_ways(self, arr): q = GeneralQuaternion(*arr) assert exp(q) == q.exp()
def test_sum_commutative(self, arr1, arr2): q1 = GeneralQuaternion(*arr1) q2 = GeneralQuaternion(*arr2) assert q1 + q2 == q2 + q1 assert q1 - q2 == - (q2 - q1)
def test_repr(self): gen_quat = GeneralQuaternion(1, 2, 3, 4) assert repr(gen_quat) == 'GeneralQuaternion(1, 2, 3, 4)'