class ProcrustesMetric(RiemannianMetric): """Procrustes metric on the pre-shape space. Parameters ---------- k_landmarks : int Number of landmarks m_ambient : int Number of coordinates of each landmark. """ def __init__(self, k_landmarks, m_ambient): super(ProcrustesMetric, self).__init__( dim=m_ambient * (k_landmarks - 1) - 1, default_point_type='matrix') self.embedding_metric = MatricesMetric(k_landmarks, m_ambient) self.sphere_metric = Hypersphere(m_ambient * k_landmarks - 1).metric self.k_landmarks = k_landmarks self.m_ambient = m_ambient def inner_product(self, tangent_vec_a, tangent_vec_b, base_point=None): """Compute the inner-product of two tangent vectors at a base point. Parameters ---------- tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient] First tangent vector at base point. tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient] Second tangent vector at base point. base_point : array-like, shape=[..., dk_landmarks, m_ambient] Point on the pre-shape space. Returns ------- inner_prod : array-like, shape=[...,] Inner-product of the two tangent vectors. """ inner_prod = self.embedding_metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) return inner_prod def exp(self, tangent_vec, base_point): """Compute the Riemannian exponential of a tangent vector. Parameters ---------- tangent_vec : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- exp : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space equal to the Riemannian exponential of tangent_vec at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_tan = gs.reshape(tangent_vec, (-1, self.sphere_metric.dim + 1)) flat_exp = self.sphere_metric.exp(flat_tan, flat_bp) return gs.reshape(flat_exp, tangent_vec.shape) def log(self, point, base_point): """Compute the Riemannian logarithm of a point. Parameters ---------- point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- log : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at the base point equal to the Riemannian logarithm of point at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_pt = gs.reshape(point, (-1, self.sphere_metric.dim + 1)) flat_log = self.sphere_metric.log(flat_pt, flat_bp) try: log = gs.reshape(flat_log, base_point.shape) except (RuntimeError, check_tf_error(ValueError, 'InvalidArgumentError')): log = gs.reshape(flat_log, point.shape) return log
class PreShapeMetric(RiemannianMetric): """Procrustes metric on the pre-shape space. Parameters ---------- k_landmarks : int Number of landmarks m_ambient : int Number of coordinates of each landmark. """ def __init__(self, k_landmarks, m_ambient): super(PreShapeMetric, self).__init__(dim=m_ambient * (k_landmarks - 1) - 1, default_point_type="matrix") self.embedding_metric = MatricesMetric(k_landmarks, m_ambient) self.sphere_metric = Hypersphere(m_ambient * k_landmarks - 1).metric self.k_landmarks = k_landmarks self.m_ambient = m_ambient def inner_product(self, tangent_vec_a, tangent_vec_b, base_point=None): """Compute the inner-product of two tangent vectors at a base point. Parameters ---------- tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient] First tangent vector at base point. tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient] Second tangent vector at base point. base_point : array-like, shape=[..., dk_landmarks, m_ambient] Point on the pre-shape space. Returns ------- inner_prod : array-like, shape=[...,] Inner-product of the two tangent vectors. """ inner_prod = self.embedding_metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) return inner_prod def exp(self, tangent_vec, base_point, **kwargs): """Compute the Riemannian exponential of a tangent vector. Parameters ---------- tangent_vec : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- exp : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space equal to the Riemannian exponential of tangent_vec at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_tan = gs.reshape(tangent_vec, (-1, self.sphere_metric.dim + 1)) flat_exp = self.sphere_metric.exp(flat_tan, flat_bp) return gs.reshape(flat_exp, tangent_vec.shape) def log(self, point, base_point, **kwargs): """Compute the Riemannian logarithm of a point. Parameters ---------- point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- log : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at the base point equal to the Riemannian logarithm of point at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_pt = gs.reshape(point, (-1, self.sphere_metric.dim + 1)) flat_log = self.sphere_metric.log(flat_pt, flat_bp) try: log = gs.reshape(flat_log, base_point.shape) except (RuntimeError, check_tf_error(ValueError, "InvalidArgumentError")): log = gs.reshape(flat_log, point.shape) return log def curvature(self, tangent_vec_a, tangent_vec_b, tangent_vec_c, base_point): r"""Compute the curvature. For three tangent vectors at a base point :math:`x,y,z`, the curvature is defined by :math:`R(X, Y)Z = \nabla_{[X,Y]}Z - \nabla_X\nabla_Y Z + - \nabla_Y\nabla_X Z`, where :math:`\nabla` is the Levi-Civita connection. In the case of the hypersphere, we have the closed formula :math:`R(X,Y)Z = \langle X, Z \rangle Y - \langle Y,Z \rangle X`. Parameters ---------- tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at `base_point`. tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at `base_point`. tangent_vec_c : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at `base_point`. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the group. Optional, default is the identity. Returns ------- curvature : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at `base_point`. """ max_shape = base_point.shape for arg in [tangent_vec_a, tangent_vec_b, tangent_vec_c]: if arg.ndim >= 3: max_shape = arg.shape flat_shape = (-1, self.sphere_metric.dim + 1) flat_a = gs.reshape(tangent_vec_a, flat_shape) flat_b = gs.reshape(tangent_vec_b, flat_shape) flat_c = gs.reshape(tangent_vec_c, flat_shape) flat_bp = gs.reshape(base_point, flat_shape) curvature = self.sphere_metric.curvature(flat_a, flat_b, flat_c, flat_bp) curvature = gs.reshape(curvature, max_shape) return curvature def curvature_derivative( self, tangent_vec_a, tangent_vec_b=None, tangent_vec_c=None, tangent_vec_d=None, base_point=None, ): r"""Compute the covariant derivative of the curvature. For four vectors fields :math:`H|_P = tangent\_vec\_a, X|_P = tangent\_vec\_b, Y|_P = tangent\_vec\_c, Z|_P = tangent\_vec\_d` with tangent vector value specified in argument at the base point `P`, the covariant derivative of the curvature :math:`(\nabla_H R)(X, Y) Z |_P` is computed at the base point P. Since the sphere is a constant curvature space this vanishes identically. Parameters ---------- tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at `base_point` along which the curvature is derived. tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient] Unused tangent vector at `base_point` (since curvature derivative vanishes). tangent_vec_c : array-like, shape=[..., k_landmarks, m_ambient] Unused tangent vector at `base_point` (since curvature derivative vanishes). tangent_vec_d : array-like, shape=[..., k_landmarks, m_ambient] Unused tangent vector at `base_point` (since curvature derivative vanishes). base_point : array-like, shape=[..., k_landmarks, m_ambient] Unused point on the group. Returns ------- curvature_derivative : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at base point. """ return gs.zeros_like(tangent_vec_a) def parallel_transport(self, tangent_vec, base_point, direction=None, end_point=None): """Compute the Riemannian parallel transport of a tangent vector. Parameters ---------- tangent_vec : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. direction : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. Optional, default : None. end_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space, to transport to. Unused if `tangent_vec_b` is given. Optional, default : None. Returns ------- transported : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space equal to the Riemannian exponential of tangent_vec at the base point. """ if direction is None: if end_point is not None: direction = self.log(end_point, base_point) else: raise ValueError( "Either an end_point or a tangent_vec_b must be given to define the" " geodesic along which to transport.") max_shape = tangent_vec.shape if tangent_vec.ndim == 3 else direction.shape flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_tan_a = gs.reshape(tangent_vec, (-1, self.sphere_metric.dim + 1)) flat_tan_b = gs.reshape(direction, (-1, self.sphere_metric.dim + 1)) flat_transport = self.sphere_metric.parallel_transport( flat_tan_a, flat_bp, flat_tan_b) return gs.reshape(flat_transport, max_shape) def injectivity_radius(self, base_point): """Compute the radius of the injectivity domain. This is is the supremum of radii r for which the exponential map is a diffeomorphism from the open ball of radius r centered at the base point onto its image. In the case of the sphere, it does not depend on the base point and is Pi everywhere. Parameters ---------- base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the manifold. Returns ------- radius : float Injectivity radius. """ return gs.pi
class PreShapeMetric(RiemannianMetric): """Procrustes metric on the pre-shape space. Parameters ---------- k_landmarks : int Number of landmarks m_ambient : int Number of coordinates of each landmark. """ def __init__(self, k_landmarks, m_ambient): super(PreShapeMetric, self).__init__(dim=m_ambient * (k_landmarks - 1) - 1, default_point_type='matrix') self.embedding_metric = MatricesMetric(k_landmarks, m_ambient) self.sphere_metric = Hypersphere(m_ambient * k_landmarks - 1).metric self.k_landmarks = k_landmarks self.m_ambient = m_ambient def inner_product(self, tangent_vec_a, tangent_vec_b, base_point=None): """Compute the inner-product of two tangent vectors at a base point. Parameters ---------- tangent_vec_a : array-like, shape=[..., k_landmarks, m_ambient] First tangent vector at base point. tangent_vec_b : array-like, shape=[..., k_landmarks, m_ambient] Second tangent vector at base point. base_point : array-like, shape=[..., dk_landmarks, m_ambient] Point on the pre-shape space. Returns ------- inner_prod : array-like, shape=[...,] Inner-product of the two tangent vectors. """ inner_prod = self.embedding_metric.inner_product( tangent_vec_a, tangent_vec_b, base_point) return inner_prod def exp(self, tangent_vec, base_point): """Compute the Riemannian exponential of a tangent vector. Parameters ---------- tangent_vec : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at a base point. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- exp : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space equal to the Riemannian exponential of tangent_vec at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_tan = gs.reshape(tangent_vec, (-1, self.sphere_metric.dim + 1)) flat_exp = self.sphere_metric.exp(flat_tan, flat_bp) return gs.reshape(flat_exp, tangent_vec.shape) def log(self, point, base_point): """Compute the Riemannian logarithm of a point. Parameters ---------- point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. base_point : array-like, shape=[..., k_landmarks, m_ambient] Point on the pre-shape space. Returns ------- log : array-like, shape=[..., k_landmarks, m_ambient] Tangent vector at the base point equal to the Riemannian logarithm of point at the base point. """ flat_bp = gs.reshape(base_point, (-1, self.sphere_metric.dim + 1)) flat_pt = gs.reshape(point, (-1, self.sphere_metric.dim + 1)) flat_log = self.sphere_metric.log(flat_pt, flat_bp) try: log = gs.reshape(flat_log, base_point.shape) except (RuntimeError, check_tf_error(ValueError, 'InvalidArgumentError')): log = gs.reshape(flat_log, point.shape) return log def curvature(self, tangent_vec_a, tangent_vec_b, tangent_vec_c, base_point): r"""Compute the curvature. For three tangent vectors at a base point :math: `x,y,z`, the curvature is defined by :math: `R(X, Y)Z = \nabla_{[X,Y]}Z - \nabla_X\nabla_Y Z + - \nabla_Y\nabla_X Z`, where :math: `\nabla` is the Levi-Civita connection. In the case of the hypersphere, we have the closed formula :math: `R(X,Y)Z = \langle X, Z \rangle Y - \langle Y,Z \rangle X`. Parameters ---------- tangent_vec_a : array-like, shape=[..., n, n] Tangent vector at `base_point`. tangent_vec_b : array-like, shape=[..., n, n] Tangent vector at `base_point`. tangent_vec_c : array-like, shape=[..., n, n] Tangent vector at `base_point`. base_point : array-like, shape=[..., n, n] Point on the group. Optional, default is the identity. Returns ------- curvature : array-like, shape=[..., n, n] Tangent vector at `base_point`. """ max_shape = base_point.shape for arg in [tangent_vec_a, tangent_vec_b, tangent_vec_c]: if arg.ndim >= 3: max_shape = arg.shape flat_shape = (-1, self.sphere_metric.dim + 1) flat_a = gs.reshape(tangent_vec_a, flat_shape) flat_b = gs.reshape(tangent_vec_b, flat_shape) flat_c = gs.reshape(tangent_vec_c, flat_shape) flat_bp = gs.reshape(base_point, flat_shape) curvature = self.sphere_metric.curvature(flat_a, flat_b, flat_c, flat_bp) curvature = gs.reshape(curvature, max_shape) return curvature