def jacobian_translation(self, point, left_or_right="left"): """Compute the Jacobian matrix of left/right translation by a point. This calculates the differential of the left translation L_(point) evaluated at 'point'. Note that it only depends on the point we are left-translating by, not on the point where the differential is evaluated. Parameters ---------- point : array-like, shape=[..., 3] Point. left_or_right : str, {'left', 'right'} Indicate whether to calculate the differential of left or right translations. Optional, default: 'left'. Returns ------- _ : array-like, shape=[..., 3, 3] Jacobian of the left/right translation by point. """ e31 = gs.array_from_sparse([(2, 0)], [1.0], (3, 3)) e32 = gs.array_from_sparse([(2, 1)], [1.0], (3, 3)) if left_or_right == "left": return (gs.eye(3) + gs.einsum("..., ij -> ...ij", -point[..., 1] / 2, e31) + gs.einsum("..., ij -> ...ij", point[..., 0] / 2, e32)) return (gs.eye(3) + gs.einsum("..., ij -> ...ij", point[..., 1] / 2, e31) + gs.einsum("..., ij -> ...ij", -point[..., 0] / 2, e32))
def from_vector(vec, dtype=gs.float32): """Convert a vector into a symmetric matrix. Parameters ---------- vec : array-like, shape=[..., n(n+1)/2] Vector. Returns ------- mat : array-like, shape=[..., n, n] Symmetric matrix. """ vec_dim = vec.shape[-1] mat_dim = (gs.sqrt(8. * vec_dim + 1) - 1) / 2 if mat_dim != int(mat_dim): raise ValueError('Invalid input dimension, it must be of the form' '(n_samples, n * (n + 1) / 2)') mat_dim = int(mat_dim) shape = (mat_dim, mat_dim) mask = 2 * gs.ones(shape) - gs.eye(mat_dim) indices = list(zip(*gs.triu_indices(mat_dim))) vec = gs.cast(vec, dtype) upper_triangular = gs.stack( [gs.array_from_sparse(indices, data, shape) for data in vec]) mat = Matrices.to_symmetric(upper_triangular) * mask return mat
def tait_bryan_angles_quaternion_test_data(self): xyz = gs.array([ [cos_angle_pi_12, 0.0, 0.0, sin_angle_pi_12], [cos_angle_pi_12, 0.0, sin_angle_pi_12, 0.0], [cos_angle_pi_12, sin_angle_pi_12, 0.0, 0.0], ]) zyx = gs.flip(xyz, axis=0) data = {"xyz": xyz, "zyx": zyx} smoke_data = [] e1 = gs.array([1.0, 0.0, 0.0, 0.0]) for coord, order in itertools.product(["intrinsic", "extrinsic"], orders): for i in range(3): vec = gs.squeeze( gs.array_from_sparse([(0, i)], [angle_pi_6], (1, 3))) smoke_data += [ dict(coord=coord, order=order, vec=vec, quat=data[order][i]) ] smoke_data += [ dict(coord=coord, order=order, vec=gs.zeros(3), quat=e1) ] return self.generate_tests(smoke_data)
def tait_bryan_angles_matrix_test_data(self): xyz = gs.array([ [ [cos_angle_pi_6, -sin_angle_pi_6, 0.0], [sin_angle_pi_6, cos_angle_pi_6, 0.0], [0.0, 0.0, 1.0], ], [ [cos_angle_pi_6, 0.0, sin_angle_pi_6], [0.0, 1.0, 0.0], [-sin_angle_pi_6, 0.0, cos_angle_pi_6], ], [ [1.0, 0.0, 0.0], [0.0, cos_angle_pi_6, -sin_angle_pi_6], [0.0, sin_angle_pi_6, cos_angle_pi_6], ], ]) zyx = gs.flip(xyz, axis=0) data = {"xyz": xyz, "zyx": zyx} smoke_data = [] for coord, order in itertools.product(coords, orders): for i in range(3): vec = gs.squeeze( gs.array_from_sparse([(0, i)], [angle_pi_6], (1, 3))) smoke_data += [ dict(coord=coord, order=order, vec=vec, mat=data[order][i]) ] smoke_data += [ dict(coord=coord, order=order, vec=gs.zeros(3), mat=gs.eye(3)) ] return self.generate_tests(smoke_data)
def __init__(self, n): dim = int(n * (n - 1) / 2) super(SkewSymmetricMatrices, self).__init__(dim, n) if n == 2: self.basis = gs.array([[[0., -1.], [1., 0.]]]) elif n == 3: self.basis = gs.array([ [[0., 0., 0.], [0., 0., -1.], [0., 1., 0.]], [[0., 0., 1.], [0., 0., 0.], [-1., 0., 0.]], [[0., -1., 0.], [1., 0., 0.], [0., 0., 0.]]]) else: self.basis = gs.zeros((dim, n, n)) basis = [] for row in gs.arange(n - 1): for col in gs.arange(row + 1, n): basis.append(gs.array_from_sparse( [(row, col), (col, row)], [1., -1.], (n, n))) self.basis = gs.stack(basis)
def get_basis(self): """Compute the basis of the vector space of symmetric matrices.""" basis = [ gs.array_from_sparse([(row, col), (col, row)], [1., 1.], (self.n, self.n)) for row in gs.arange(self.n) for col in gs.arange(row, self.n) ] basis = gs.stack(basis) * (gs.ones( (self.n, self.n)) - 1. / 2 * gs.eye(self.n)) return basis
def basis_test_data(self): smoke_data = [ dict( n=2, m=2, expected=gs.array([ gs.array_from_sparse([(i, j)], [1], (2, 2)) for i in range(2) for j in range(2) ]), ), dict( n=2, m=3, expected=gs.array([ gs.array_from_sparse([(i, j)], [1], (2, 3)) for i in range(2) for j in range(3) ]), ), ] return self.generate_tests(smoke_data)
def __init__(self, n): dimension = int(n * (n - 1) / 2) super(SkewSymmetricMatrices, self).__init__(dimension, n) self.basis = gs.zeros((dimension, n, n)) basis = [] for row in gs.arange(n - 1): for col in gs.arange(row + 1, n): basis.append(gs.array_from_sparse( [(row, col), (col, row)], [1., -1.], (n, n))) self.basis = gs.stack(basis)
def _create_basis(self): """Compute the basis of the vector space of symmetric matrices.""" basis = [] for row in gs.arange(self.n): for col in gs.arange(row, self.n): if row == col: indices = [(row, row)] values = [1.0] else: indices = [(row, col), (col, row)] values = [1.0, 1.0] basis.append(gs.array_from_sparse(indices, values, (self.n,) * 2)) basis = gs.stack(basis) return basis
def __init__(self, n): dim = int(n * (n + 1) / 2) super(SpecialEuclideanMatrixLieAlgebra, self).__init__(dim, n) self.skew = SkewSymmetricMatrices(n) basis = homogeneous_representation( self.skew.basis, gs.zeros((self.skew.dim, n)), (self.skew.dim, n + 1, n + 1), 0.) basis = list(basis) for row in gs.arange(n): basis.append(gs.array_from_sparse( [(row, n)], [1.], (n + 1, n + 1))) self.basis = gs.stack(basis)
def _create_basis(self): """Create the canonical basis.""" n = self.n basis = homogeneous_representation( self.skew.basis, gs.zeros((self.skew.dim, n)), (self.skew.dim, n + 1, n + 1), 0.0, ) basis = list(basis) for row in gs.arange(n): basis.append( gs.array_from_sparse([(row, n)], [1.0], (n + 1, n + 1))) return gs.stack(basis)
def symmetric_matrix_from_vector(vec, dtype=gs.float32): """Convert a vector into a symmetric matrix.""" vec_dim = vec.shape[-1] mat_dim = (gs.sqrt(8. * vec_dim + 1) - 1) / 2 if mat_dim != int(mat_dim): raise ValueError('Invalid input dimension, it must be of the form' '(n_samples, n * (n - 1) / 2)') mat_dim = int(mat_dim) mask = 2 * gs.ones((mat_dim, mat_dim)) - gs.eye(mat_dim) indices = list(zip(*gs.triu_indices(3))) shape = (mat_dim, mat_dim) vec = gs.cast(vec, dtype) upper_triangular = gs.stack( [gs.array_from_sparse(indices, data, shape) for data in vec]) mat = Matrices.make_symmetric(upper_triangular) * mask return mat
def _create_basis(self): """Create the canonical basis.""" n = self.n if n == 2: return gs.array([[[0.0, -1.0], [1.0, 0.0]]]) if n == 3: return gs.array([ [[0.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 1.0, 0.0]], [[0.0, 0.0, 1.0], [0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]], [[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0]], ]) basis = [] for row in gs.arange(n - 1): for col in gs.arange(row + 1, n): basis.append( gs.array_from_sparse([(row, col), (col, row)], [1.0, -1.0], (n, n))) return gs.stack(basis)
def __init__(self, n): """Instantiate the class. Parameters ---------- n: int The number of rows and columns. """ dimension = int(n * (n - 1) / 2) super(SkewSymmetricMatrices, self).__init__(dimension, n) self.basis = gs.zeros((dimension, n, n)) basis = [] for row in gs.arange(n - 1): for col in gs.arange(row + 1, n): basis.append(gs.array_from_sparse( [(row, col), (col, row)], [1., -1.], (n, n))) self.basis = gs.stack(basis)
def permute(self, graph_to_permute, permutation): r"""Permutation action applied to graph observation. Parameters ---------- graph_to_permute : array-like, shape=[..., n, n] Input graphs to be permuted. permutation: array-like, shape=[..., n] Node permutations where in position i we have the value j meaning the node i should be permuted with node j. Returns ------- graphs_permuted : array-like, shape=[..., n, n] Graphs permuted. """ nodes = self.nodes single_graph = len(graph_to_permute.shape) < 3 if single_graph: graph_to_permute = [graph_to_permute] permutation = [permutation] result = [] for i, p in enumerate(permutation): if gs.all(gs.array(nodes) == gs.array(p)): result.append(graph_to_permute[i]) else: gtype = graph_to_permute[i].dtype permutation_matrix = gs.array_from_sparse( data=gs.ones(nodes, dtype=gtype), indices=list(zip(list(range(nodes)), p)), target_shape=(nodes, nodes), ) result.append( self.adjmat.mul( permutation_matrix, graph_to_permute[i], gs.transpose(permutation_matrix), )) return result[0] if single_graph else gs.array(result)
def test_compose_matrix_form(self): point = self.group.random_point() result = self.group.compose(point, self.group.identity) expected = point self.assertAllClose(result, expected) if not geomstats.tests.tf_backend(): # Composition by identity, on the left # Expect the original transformation result = self.group.compose(self.group.identity, point) expected = point self.assertAllClose(result, expected) # Composition of translations (no rotational part) # Expect the sum of the translations point_a = gs.array([[1.0, 0.0, 1.0], [0.0, 1.0, 1.5], [0.0, 0.0, 1.0]]) point_b = gs.array([[1.0, 0.0, 2.0], [0.0, 1.0, 2.5], [0.0, 0.0, 1.0]]) result = self.group.compose(point_a, point_b) last_line_0 = gs.array_from_sparse([(0, 2), (1, 2)], [1.0, 1.0], (3, 3)) expected = point_a + point_b * last_line_0 self.assertAllClose(result, expected)
def test_array_from_sparse(self): expected = gs.array([[0, 1, 0], [0, 0, 2]]) result = gs.array_from_sparse([(0, 1), (1, 2)], [1, 2], (2, 3)) self.assertAllClose(result, expected)
def _get_permutation_matrix(indices_): return gs.array_from_sparse( data=gs.ones(self.n_nodes, dtype=gs.int64), indices=list(zip(range(self.n_nodes), indices_)), target_shape=(self.n_nodes, self.n_nodes), )