Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,169 @@
import pytest
import numpy as np
from numpy.testing import assert_array_almost_equal
from scipy.spatial.transform import Rotation
from scipy.optimize import linear_sum_assignment
from scipy.spatial.distance import cdist
from scipy.constants import golden as phi
from scipy.spatial import cKDTree
TOL = 1E-12
NS = range(1, 13)
NAMES = ["I", "O", "T"] + ["C%d" % n for n in NS] + ["D%d" % n for n in NS]
SIZES = [60, 24, 12] + list(NS) + [2 * n for n in NS]
def _calculate_rmsd(P, Q):
"""Calculates the root-mean-square distance between the points of P and Q.
The distance is taken as the minimum over all possible matchings. It is
zero if P and Q are identical and non-zero if not.
"""
distance_matrix = cdist(P, Q, metric='sqeuclidean')
matching = linear_sum_assignment(distance_matrix)
return np.sqrt(distance_matrix[matching].sum())
def _generate_pyramid(n, axis):
thetas = np.linspace(0, 2 * np.pi, n + 1)[:-1]
P = np.vstack([np.zeros(n), np.cos(thetas), np.sin(thetas)]).T
P = np.concatenate((P, [[1, 0, 0]]))
return np.roll(P, axis, axis=1)
def _generate_prism(n, axis):
thetas = np.linspace(0, 2 * np.pi, n + 1)[:-1]
bottom = np.vstack([-np.ones(n), np.cos(thetas), np.sin(thetas)]).T
top = np.vstack([+np.ones(n), np.cos(thetas), np.sin(thetas)]).T
P = np.concatenate((bottom, top))
return np.roll(P, axis, axis=1)
def _generate_icosahedron():
x = np.array([[0, -1, -phi],
[0, -1, +phi],
[0, +1, -phi],
[0, +1, +phi]])
return np.concatenate([np.roll(x, i, axis=1) for i in range(3)])
def _generate_octahedron():
return np.array([[-1, 0, 0], [+1, 0, 0], [0, -1, 0],
[0, +1, 0], [0, 0, -1], [0, 0, +1]])
def _generate_tetrahedron():
return np.array([[1, 1, 1], [1, -1, -1], [-1, 1, -1], [-1, -1, 1]])
@pytest.mark.parametrize("name", [-1, None, True, np.array(['C3'])])
def test_group_type(name):
with pytest.raises(ValueError,
match="must be a string"):
Rotation.create_group(name)
@pytest.mark.parametrize("name", ["Q", " ", "CA", "C ", "DA", "D ", "I2", ""])
def test_group_name(name):
with pytest.raises(ValueError,
match="must be one of 'I', 'O', 'T', 'Dn', 'Cn'"):
Rotation.create_group(name)
@pytest.mark.parametrize("name", ["C0", "D0"])
def test_group_order_positive(name):
with pytest.raises(ValueError,
match="Group order must be positive"):
Rotation.create_group(name)
@pytest.mark.parametrize("axis", ['A', 'b', 0, 1, 2, 4, False, None])
def test_axis_valid(axis):
with pytest.raises(ValueError,
match="`axis` must be one of"):
Rotation.create_group("C1", axis)
def test_icosahedral():
"""The icosahedral group fixes the rotations of an icosahedron. Here we
test that the icosahedron is invariant after application of the elements
of the rotation group."""
P = _generate_icosahedron()
for g in Rotation.create_group("I"):
g = Rotation.from_quat(g.as_quat())
assert _calculate_rmsd(P, g.apply(P)) < TOL
def test_octahedral():
"""Test that the octahedral group correctly fixes the rotations of an
octahedron."""
P = _generate_octahedron()
for g in Rotation.create_group("O"):
assert _calculate_rmsd(P, g.apply(P)) < TOL
def test_tetrahedral():
"""Test that the tetrahedral group correctly fixes the rotations of a
tetrahedron."""
P = _generate_tetrahedron()
for g in Rotation.create_group("T"):
assert _calculate_rmsd(P, g.apply(P)) < TOL
@pytest.mark.parametrize("n", NS)
@pytest.mark.parametrize("axis", 'XYZ')
def test_dicyclic(n, axis):
"""Test that the dicyclic group correctly fixes the rotations of a
prism."""
P = _generate_prism(n, axis='XYZ'.index(axis))
for g in Rotation.create_group("D%d" % n, axis=axis):
assert _calculate_rmsd(P, g.apply(P)) < TOL
@pytest.mark.parametrize("n", NS)
@pytest.mark.parametrize("axis", 'XYZ')
def test_cyclic(n, axis):
"""Test that the cyclic group correctly fixes the rotations of a
pyramid."""
P = _generate_pyramid(n, axis='XYZ'.index(axis))
for g in Rotation.create_group("C%d" % n, axis=axis):
assert _calculate_rmsd(P, g.apply(P)) < TOL
@pytest.mark.parametrize("name, size", zip(NAMES, SIZES))
def test_group_sizes(name, size):
assert len(Rotation.create_group(name)) == size
@pytest.mark.parametrize("name, size", zip(NAMES, SIZES))
def test_group_no_duplicates(name, size):
g = Rotation.create_group(name)
kdtree = cKDTree(g.as_quat())
assert len(kdtree.query_pairs(1E-3)) == 0
@pytest.mark.parametrize("name, size", zip(NAMES, SIZES))
def test_group_symmetry(name, size):
g = Rotation.create_group(name)
q = np.concatenate((-g.as_quat(), g.as_quat()))
distance = np.sort(cdist(q, q))
deltas = np.max(distance, axis=0) - np.min(distance, axis=0)
assert (deltas < TOL).all()
@pytest.mark.parametrize("name", NAMES)
def test_reduction(name):
"""Test that the elements of the rotation group are correctly
mapped onto the identity rotation."""
g = Rotation.create_group(name)
f = g.reduce(g)
assert_array_almost_equal(f.magnitude(), np.zeros(len(g)))
@pytest.mark.parametrize("name", NAMES)
def test_single_reduction(name):
g = Rotation.create_group(name)
f = g[-1].reduce(g)
assert_array_almost_equal(f.magnitude(), 0)
assert f.as_quat().shape == (4,)

View file

@ -0,0 +1,161 @@
from itertools import product
import numpy as np
from numpy.testing import assert_allclose
from pytest import raises
from scipy.spatial.transform import Rotation, RotationSpline
from scipy.spatial.transform._rotation_spline import (
_angular_rate_to_rotvec_dot_matrix,
_rotvec_dot_to_angular_rate_matrix,
_matrix_vector_product_of_stacks,
_angular_acceleration_nonlinear_term,
_create_block_3_diagonal_matrix)
def test_angular_rate_to_rotvec_conversions():
np.random.seed(0)
rv = np.random.randn(4, 3)
A = _angular_rate_to_rotvec_dot_matrix(rv)
A_inv = _rotvec_dot_to_angular_rate_matrix(rv)
# When the rotation vector is aligned with the angular rate, then
# the rotation vector rate and angular rate are the same.
assert_allclose(_matrix_vector_product_of_stacks(A, rv), rv)
assert_allclose(_matrix_vector_product_of_stacks(A_inv, rv), rv)
# A and A_inv must be reciprocal to each other.
I_stack = np.empty((4, 3, 3))
I_stack[:] = np.eye(3)
assert_allclose(np.matmul(A, A_inv), I_stack, atol=1e-15)
def test_angular_rate_nonlinear_term():
# The only simple test is to check that the term is zero when
# the rotation vector
np.random.seed(0)
rv = np.random.rand(4, 3)
assert_allclose(_angular_acceleration_nonlinear_term(rv, rv), 0,
atol=1e-19)
def test_create_block_3_diagonal_matrix():
np.random.seed(0)
A = np.empty((4, 3, 3))
A[:] = np.arange(1, 5)[:, None, None]
B = np.empty((4, 3, 3))
B[:] = -np.arange(1, 5)[:, None, None]
d = 10 * np.arange(10, 15)
banded = _create_block_3_diagonal_matrix(A, B, d)
# Convert the banded matrix to the full matrix.
k, l = list(zip(*product(np.arange(banded.shape[0]),
np.arange(banded.shape[1]))))
k = np.asarray(k)
l = np.asarray(l)
i = k - 5 + l
j = l
values = banded.ravel()
mask = (i >= 0) & (i < 15)
i = i[mask]
j = j[mask]
values = values[mask]
full = np.zeros((15, 15))
full[i, j] = values
zero = np.zeros((3, 3))
eye = np.eye(3)
# Create the reference full matrix in the most straightforward manner.
ref = np.block([
[d[0] * eye, B[0], zero, zero, zero],
[A[0], d[1] * eye, B[1], zero, zero],
[zero, A[1], d[2] * eye, B[2], zero],
[zero, zero, A[2], d[3] * eye, B[3]],
[zero, zero, zero, A[3], d[4] * eye],
])
assert_allclose(full, ref, atol=1e-19)
def test_spline_2_rotations():
times = [0, 10]
rotations = Rotation.from_euler('xyz', [[0, 0, 0], [10, -20, 30]],
degrees=True)
spline = RotationSpline(times, rotations)
rv = (rotations[0].inv() * rotations[1]).as_rotvec()
rate = rv / (times[1] - times[0])
times_check = np.array([-1, 5, 12])
dt = times_check - times[0]
rv_ref = rate * dt[:, None]
assert_allclose(spline(times_check).as_rotvec(), rv_ref)
assert_allclose(spline(times_check, 1), np.resize(rate, (3, 3)))
assert_allclose(spline(times_check, 2), 0, atol=1e-16)
def test_constant_attitude():
times = np.arange(10)
rotations = Rotation.from_rotvec(np.ones((10, 3)))
spline = RotationSpline(times, rotations)
times_check = np.linspace(-1, 11)
assert_allclose(spline(times_check).as_rotvec(), 1, rtol=1e-15)
assert_allclose(spline(times_check, 1), 0, atol=1e-19)
assert_allclose(spline(times_check, 2), 0, atol=1e-19)
assert_allclose(spline(5.5).as_rotvec(), 1, rtol=1e-15)
assert_allclose(spline(5.5, 1), 0, atol=1e-19)
assert_allclose(spline(5.5, 2), 0, atol=1e-19)
def test_spline_properties():
times = np.array([0, 5, 15, 27])
angles = [[-5, 10, 27], [3, 5, 38], [-12, 10, 25], [-15, 20, 11]]
rotations = Rotation.from_euler('xyz', angles, degrees=True)
spline = RotationSpline(times, rotations)
assert_allclose(spline(times).as_euler('xyz', degrees=True), angles)
assert_allclose(spline(0).as_euler('xyz', degrees=True), angles[0])
h = 1e-8
rv0 = spline(times).as_rotvec()
rvm = spline(times - h).as_rotvec()
rvp = spline(times + h).as_rotvec()
assert_allclose(rv0, 0.5 * (rvp + rvm), rtol=1e-15)
r0 = spline(times, 1)
rm = spline(times - h, 1)
rp = spline(times + h, 1)
assert_allclose(r0, 0.5 * (rm + rp), rtol=1e-14)
a0 = spline(times, 2)
am = spline(times - h, 2)
ap = spline(times + h, 2)
assert_allclose(a0, am, rtol=1e-7)
assert_allclose(a0, ap, rtol=1e-7)
def test_error_handling():
raises(ValueError, RotationSpline, [1.0], Rotation.random())
r = Rotation.random(10)
t = np.arange(10).reshape(5, 2)
raises(ValueError, RotationSpline, t, r)
t = np.arange(9)
raises(ValueError, RotationSpline, t, r)
t = np.arange(10)
t[5] = 0
raises(ValueError, RotationSpline, t, r)
t = np.arange(10)
s = RotationSpline(t, r)
raises(ValueError, s, 10, -1)
raises(ValueError, s, np.arange(10).reshape(5, 2))