https://github.com/geomstats/geomstats
Tip revision: a68934b61f9efca8d4315ec689950a0576b06b60 authored by Nina Miolane on 27 April 2018, 19:01:43 UTC
Merge pull request #60 from ninamiolane/nina-new-release
Merge pull request #60 from ninamiolane/nina-new-release
Tip revision: a68934b
invariant_metric.py
"""
Base class for special Riemannian metrics that
can be built on Lie groups:
- left-invariant metrics
- right-invariant metrics.
Note: Assume that the points are parameterized by
their Riemannian logarithm for the canonical left-invariant metric.
"""
import numpy as np
import scipy.linalg
from geomstats.riemannian_metric import RiemannianMetric
import geomstats.vectorization as vectorization
class InvariantMetric(RiemannianMetric):
"""
Base class for left- or right- invariant metrics
that can be defined on Lie groups.
"""
def __init__(self, group, inner_product_mat_at_identity,
left_or_right='left'):
if inner_product_mat_at_identity.ndim == 3:
n_mats, _, _ = inner_product_mat_at_identity.shape
assert n_mats == 1
inner_product_mat_at_identity = np.squeeze(
inner_product_mat_at_identity, axis=0)
matrix_shape = inner_product_mat_at_identity.shape
assert matrix_shape == (group.dimension,) * 2
assert left_or_right in ('left', 'right')
eigenvalues = np.linalg.eigvalsh(inner_product_mat_at_identity)
n_pos_eigval = np.sum(eigenvalues > 0)
n_neg_eigval = np.sum(eigenvalues < 0)
n_null_eigval = np.sum(eigenvalues == 0)
assert (n_pos_eigval + n_neg_eigval
+ n_null_eigval) == group.dimension
self.group = group
self.inner_product_mat_at_identity = inner_product_mat_at_identity
self.left_or_right = left_or_right
self.signature = (n_pos_eigval, n_null_eigval, n_neg_eigval)
def inner_product_matrix(self, base_point=None):
"""
Compute the matrix of the Riemmanian metric at point base_point,
by translating inner_product from the identity to base_point.
"""
if base_point is None:
base_point = self.group.identity
base_point = self.group.regularize(base_point)
jacobian = self.group.jacobian_translation(
point=base_point,
left_or_right=self.left_or_right)
assert jacobian.ndim == 3
inv_jacobian = np.linalg.inv(jacobian)
inv_jacobian_transposed = np.transpose(inv_jacobian, axes=(0, 2, 1))
inner_product_mat_at_id = self.inner_product_mat_at_identity
inner_product_mat_at_id = vectorization.to_ndarray(
inner_product_mat_at_id, to_ndim=2)
metric_mat = np.matmul(inv_jacobian_transposed,
inner_product_mat_at_id)
metric_mat = np.matmul(metric_mat, inv_jacobian)
return metric_mat
def left_exp_from_identity(self, tangent_vec):
"""
Compute the *left* Riemannian exponential from the identity of the
Lie group of tangent vector tangent_vec.
The left Riemannian exponential has a special role since the
left Riemannian exponential of the canonical metric parameterizes
the points.
Note: In the case where the method is called by a right-invariant
metric, it used the left-invariant metric associated to the same
inner-product at the identity.
"""
tangent_vec = vectorization.to_ndarray(tangent_vec, to_ndim=2)
tangent_vec = self.group.regularize_tangent_vec_at_identity(
tangent_vec=tangent_vec,
metric=self)
sqrt_inner_product_mat = scipy.linalg.sqrtm(
self.inner_product_mat_at_identity)
mat = sqrt_inner_product_mat.transpose()
exp = np.matmul(tangent_vec, mat)
exp = self.group.regularize(exp)
return exp
def exp_from_identity(self, tangent_vec):
"""
Compute the Riemannian exponential from the identity of the
Lie group of tangent vector tangent_vec.
"""
tangent_vec = vectorization.to_ndarray(tangent_vec, to_ndim=2)
if self.left_or_right == 'left':
exp = self.left_exp_from_identity(tangent_vec)
else:
opp_left_exp = self.left_exp_from_identity(-tangent_vec)
exp = self.group.inverse(opp_left_exp)
exp = self.group.regularize(exp)
return exp
def exp_basis(self, tangent_vec, base_point=None):
"""
Compute the Riemannian exponential at point base_point
of tangent vector tangent_vec.
"""
if base_point is None:
base_point = self.group.identity
base_point = self.group.regularize(base_point)
if base_point is self.group.identity:
return self.exp_from_identity(tangent_vec)
tangent_vec = vectorization.to_ndarray(tangent_vec, to_ndim=2)
n_tangent_vecs, _ = tangent_vec.shape
n_base_points, _ = base_point.shape
assert n_tangent_vecs == 1 and n_base_points == 1
jacobian = self.group.jacobian_translation(
point=base_point,
left_or_right=self.left_or_right)
assert jacobian.ndim == 3
inv_jacobian = np.linalg.inv(jacobian)
inv_jacobian_transposed = np.transpose(inv_jacobian, axes=(0, 2, 1))
tangent_vec_at_id = np.matmul(tangent_vec, inv_jacobian_transposed)
tangent_vec_at_id = np.squeeze(tangent_vec_at_id, axis=0)
exp_from_id = self.exp_from_identity(tangent_vec_at_id)
if self.left_or_right == 'left':
exp = self.group.compose(base_point, exp_from_id)
else:
exp = self.group.compose(exp_from_id, base_point)
exp = self.group.regularize(exp)
return exp
def left_log_from_identity(self, point):
"""
Compute the *left* Riemannian logarithm from the identity of the
Lie group of tangent vector tangent_vec.
The left Riemannian logarithm has a special role since the
left Riemannian logarithm of the canonical metric parameterizes
the points.
"""
point = self.group.regularize(point)
inner_prod_mat = self.inner_product_mat_at_identity
sqrt_inv_inner_prod_mat = scipy.linalg.sqrtm(np.linalg.inv(
inner_prod_mat))
assert sqrt_inv_inner_prod_mat.shape == (self.group.dimension,) * 2
log = np.matmul(point, sqrt_inv_inner_prod_mat.transpose())
log = self.group.regularize_tangent_vec_at_identity(
tangent_vec=log,
metric=self)
assert log.ndim == 2
return log
def log_from_identity(self, point):
"""
Compute the Riemannian logarithm of point at point base_point
of point for the invariant metric from the identity.
"""
point = self.group.regularize(point)
if self.left_or_right == 'left':
log = self.left_log_from_identity(point)
else:
inv_point = self.group.inverse(point)
left_log = self.left_log_from_identity(inv_point)
log = - left_log
assert log.ndim == 2
return log
def log_basis(self, point, base_point=None):
"""
Compute the Riemannian logarithm of point at point base_point
of point for the invariant metric.
"""
if base_point is None:
base_point = self.group.identity
base_point = self.group.regularize(base_point)
if base_point is self.group.identity:
return self.log_from_identity(point)
point = self.group.regularize(point)
n_points, _ = point.shape
n_base_points, _ = base_point.shape
assert n_points == 1 and n_base_points == 1
if self.left_or_right == 'left':
point_near_id = self.group.compose(
self.group.inverse(base_point),
point)
else:
point_near_id = self.group.compose(
point,
self.group.inverse(base_point))
log_from_id = self.log_from_identity(point_near_id)
jacobian = self.group.jacobian_translation(
base_point,
left_or_right=self.left_or_right)
log = np.matmul(log_from_id, np.transpose(jacobian, axes=(0, 2, 1)))
log = np.squeeze(log, axis=0)
assert log.ndim == 2
return log