Sunday, April 14, 2024

Posts History

I delve into a diverse range of topics, spanning programming languages, machine learning, data engineering tools, and DevOps. Our articles are enriched with practical code examples, ensuring their applicability in real-world scenarios.

Follow me on LinkedIn

2024

Friday, April 12, 2024

Geometric Learning in Python: Riemann Metric & Connection

Target audience: Advanced
Estimated reading time: 7'

Concepts from Riemannian geometry are fundamental in various fields such as Robotics, Computer Vision, Machine Learning, Quantitative Finance, and Medical Imaging. 
This article utilizes the Geomstats library to explore and experiment with metrics, connections, and parallel transport in these contexts.


Table of contents

       Vector fields
       Setup
       Riemann metric
Follow me on LinkedIn

What you will learn: How to implement Riemann metric and connection in Python using Geomstats library.


Notes
  • Environments: Python  3.10.10, Geomstats 2.7.0
  • This article assumes that the reader is somewhat familiar with differential and tensor calculus [ref 1, 2]. Please refer to the previous articles related to geometric learning [ref 5, 6, 7].
  • Source code is available at  Github.com/patnicolas/Data_Exploration/manifolds
  • To enhance the readability of the algorithm implementations, we have omitted non-essential code elements like error checking, comments, exceptions, validation of class and method arguments, scoping qualifiers, and import statements.

Introduction

Geometric learning addresses the difficulties of limited data, high-dimensional spaces, and the need for independent representations in the development of sophisticated machine learning models, including graph-based and physics-informed neural networks.

The primary goal of learning Riemannian geometry is to understand and analyze the properties of curved spaces that cannot be described adequately using Euclidean geometry alone. Riemannian geometry enables to describe the geometric structures of manifolds equipped with a metric, which defines the concept of distance and angle on these spaces.

This article is the seventh part of our ongoing series focused on geometric learning. In this installment, we utilize the Geomstats Python library [ref. 3] and explore the hypersphere manifold, which was introduced in a previous piece, Geometric Learning in Python: Manifolds - Hypersphere  and is detailed in the Geomstats API [ref. 4]. 

I highly recommend watching the comprehensive series of 22 YouTube videos Tensor Calculus - Eigenchris  to familiarize yourself with fundamental concepts of differential geometry. 
Summaries of my earlier articles on this topic can be found in the Appendix

Riemann geometry

First, let's review some basic concepts in Riemannian geometry that we described in previous articles [ref 5, 6, 7].

Vector fields

In tensor calculus a vector field is an assignment of a vector X to each point p on a Euclidean space. For instance, a vector field on a 3D sphere can be visualized as a collection of arrows with a given direction and length.

Fig. 1 Visualization of vector fields on a sphere in 3D Euclidean space

Formally, a vector field X(p) at point p is defined as: 
\[X: S \rightarrow \mathbb{R}^{n} \ \ \ \ \ X(p) = \sum_{i=1}^{n} X_{i}(p)\frac{\partial }{\partial x_{i}}\]
The gradient of a vector field on a differential manifold is defined as: \[\triangledown _{\vec{v}}f= \triangledown f. \vec{v}=\sum_{i=1}^{n}v^{i}\frac{\partial f}{\partial x^{i}} \ \ \ (1) \]

Covariant derivative

The gradient of a vector field is defined by its components along the basis vectors, which are orthogonal in Euclidean space. The covariant derivative, on the other hand, calculates a gradient along a specific vector, particularly the tangent vector of a manifold.

The covariant derivative measures the rate of change of vector fields, considering the variation of basis vectors. For instance, in flat space, the covariant derivative of a vector field corresponds to the ordinary derivative, calculating both the derivative of the vector components and the derivative of the basis vectors.
The covariant derivative can be expressed as a directional derivative in extrinsic coordinates as \[\triangledown_{\frac{\partial }{\partial u^{i}}}\vec{v}=\frac{\partial \vec{v}}{\partial u^{i}}-\vec{n} \ \ \ \ (2)\]
Fig. 2 Visualization of covariant derivative on a sphere from formula (2)


Parallel transport

The covariant derivative is often conceptualized as an affine connection because it links the tangent spaces (or planes) at two points on the manifold. Parallel transport involves moving a geometric vector or tensor along smooth curves within the manifold. This process is facilitated by the covariant derivative, which enables the transportation of manifold vectors along these curves, ensuring they remain parallel according to the connection.

Fig. 3 Illustration of parallel transport along smooth curves on a sphere

In a parallel transport the rate of change of the tangent vector as illustrated in the previous figure is 0. Therefore the covariant derivative along the tangent vector is 0.
\[\triangledown _{\frac{\partial }{\partial u^{i}}}\vec{v} = 0 \ \ \  (3)\] 
Fig. 4 Illustration of Christoffel symbols for the covariant derivative on a sphere

The covariant derivative can be expressed using the Christoffel symbols as\[\triangledown _{\frac{\partial }{\partial u^{i}}}\vec{v} =\left \lfloor \frac{\partial v^{k}}{\partial u^{i}} +v^{j}.\Gamma^{k}_{ij}\right \rfloor \vec{e_{k}} \ \ \ (4)\]

Metric & connection

A metric tensor is a structure (such as a tensor or matrix) on a manifold which facilitates the definition of distances, angles, and norms, in a manner comparable to the inner product in Euclidean space. Given a set of basis vector ei  the metric tensor gij and the inverse metric tensor are defined as \[g_{ij}=\vec{e_{i}}.\vec{e_{j}}=\left \langle \frac{\partial }{\partial x^{i}} \frac{\partial }{\partial x^{j}} \right \rangle \ \ (5) \ \ \ Inverse \ metric \ \mathfrak{g^{ij}} \ \ g_{ij}.\mathfrak{g^{ij}} =\delta _{ij}\]

An affine connection that incorporates a metric is described as a bilinear map. Given a manifold M, a tangent spaceTM and vector fields space (TM). \[\begin{matrix} \Gamma(T_{M})\ X \ \Gamma(T_{M})\rightarrow \Gamma(T_{M}) \\ \triangledown : \ \ \ (X, Y) \rightarrow \triangledown_{X}Y \ \ \ \ \ \ \ \ \ \end{matrix}\]For any infinitely differentiable and continuous function f, \[\triangledown_{fX}Y=f\triangledown_{X}Y \ \ \ \ \triangledown_{X}(fY)=\frac{\partial f}{\partial X}Y+\triangledown_{fX}Y\]An affine connection that preserves the Riemann metric under parallel transport and is symmetric (commutative covariant derivative) is known as the Levi-Civita connection.

Levi-Civita coefficients

The Levi-Civita connection is a type of metric connection that is compatible with the metric tensor of a Riemannian manifold. Its covariant derivative of the metric tensor with respect to the Levi-Civita connection is zero. The property of parallel transport ensures that the connection preserves the inner product structure defined by the metric tensor.
It is related to the fundamental Theorem of riemannian geometry which states "There is a unique connection (or covariant derivative) that is Torsion free and has metric compatibility"
The Levi-Civita connection is essential is the computation of geodesics, curvature tensor and parallel transport on Riemannian manifolds.

The coefficients of the Levi-Civita used to compute the covariant derivative is defined by the Christoffel symbols. Using the intrinsic coordinates, the Christoffel symbols are computed from the metric tensors g as \[\Gamma _{jk}^{m}=\frac{1}{2}\mathfrak{g}^{im}\left ( \frac{\partial }{\partial u^{k}} .g_{ij} +\frac{\partial }{\partial u^{j}} .g_{ki} - \frac{\partial }{\partial u^{i}} .g_{jk} \right ) \ \ \ (6) \]


Use case: hypersphere

We will illustrate the implementation of Riemann metric and Levi-Civita connection on the hypersphere space we introduced in a previous article Geometric Learning in Python: Manifolds

Setup

As a reminder, we reuse the structure of data points on a manifold defined by the class ManifoldPoint described in a previous post ManifoldPoint definition

@dataclass
class ManifoldPoint:
id: AnyStr
location: np.array
tgt_vector: Optional[List[float]] = None
geodesic: Optional[bool] = False

As in previous articles, we utilize the Geomstats Python library to explore, implement, and assess the Riemannian manifold and its connection. For ease of use, we encapsulated the Riemann metric within a class named RiemannianConnection as follows:

import geomstats.backend as gs
from geomstats.geometry.riemannian_metric import RiemannianMetric
from geomstats.geometry.base import LevelSet
from typing import AnyStr, Optional
from manifoldpoint import ManifoldPoint
from geometricexception import GeometricException


class RiemannianConnection(object):

    def __init__(self, space: LevelSet, manifold_type: AnyStr):
        self.riemannian_metric = RiemannianConnection.__get_metric(space)

        self.manifold_descriptor = f'{manifold_type}\nDimension: {space.dim}\nShape: {space.shape}' \
                                   f'\nCoordinates type: {space.default_coords_type}'
        # Add 1 dimension for extrinsic coordinates
        self.ndim = space.dim+1 if space.default_coords_type == "extrinsic" else space.dim

The Riemann metric is inferred from the type of manifold (or level set), space. The variable ndim captures the dimension of tangent vectors in either intrinsic or extrinsic coordinates.


Riemann metric

Let's implement the inner product of two tangent vectors, tgt_vec1 and tgt_vec2 at a base point base_pt defined in formula (4) for the RiemannianConnection class. 

def inner_product(self, tgt_vec1: np.array, tgt_vec2: np.array, base_pt: np.array) -> np.array:
  if len(tgt_vec1) != len(tgt_vec2):
      raise GeometricException(f'Inner product of vector size {len(tgt_vec1)} and vector size {len(tgt_vec1)}')

  return 0 if len(tgt_vec1) == 0 or len(tgt_vec2) == 0 \
            else self.riemannian_metric.inner_product(tgt_vec1, tgt_vec2, base_pt)

The inner product on the hypersphere is computed for a vector and its negative value.

dim = 2
coordinates_type = 'extrinsic'

hypersphere = Hypersphere(dim=dim, equip=True, default_coords_type=coordinates_type)
riemann_connection = RiemannianConnection(hypersphere, 'hypersphere')

vector = np.array([0.4, 0.1, 0.8])
base_point = np.array([0.4, 0.1, 0.0])

inner_product = riemann_connection.inner_product(vector, -vector, base_point   #. -0.81

Output:
Inner product: -0.81


Riemann connection

Parallel transport
Let's implement the formula (5) by adding the method parallel_transport to the class RiemannianConnection. Its implementation invoked the Geomstats method, RiemannianMetric.parallel_transport. The parallel transport is initialized from the manifold base point, manifold_base_pt, to an optional end point, manifold_end_pt. The direction for the parallel transport can be optionally specified with the tangent vector at the base point as default value.

def parallel_transport(
   self,
   manifold_base_pt: ManifoldPoint,
   manifold_end_pt: Optional[ManifoldPoint] = None,
   direction: Optional[np.array] = None) -> np.array:
        
   if manifold_base_pt.ndim() != self.ndim:
      raise GeometricException(f'Base pt dimension {manifold_base_pt.ndim()} should be {self.ndim}')

   return self.riemannian_metric.parallel_transport(
       manifold_base_pt.tgt_vector,
       manifold_base_pt.location,
       direction,
       manifold_end_pt.location)

The evaluation consists of one manifold base point with a tangent vector and another manifold end point with a translation +0.4

vector = np.array([0.5, 0.1, 2.1])
base_pt = np.array([1.5, 2.0, 1.6])

manifold_base_pt = ManifoldPoint(id='base', location=base_pt, tgt_vector=vector
manifold_end_pt = ManifoldPoint(id='end',location=base_pt + 0.4)

parallel_trans = riemannian_connection.parallel_transport(manifold_base_pt, manifold_end_pt)
print(f'Parallel transport: {parallel_trans}')

Output:
Parallel transport: [[-1.20097215 -2.09879718  0.29946284]]


Christoffel symbols 
Lastly, let's compute the Christoffel symbols for the hypersphere as a given point of intrinsic coordinates (u, v).\[\begin{bmatrix} \Gamma _{11}^{1} & \Gamma _{12}^{1}\\ \Gamma _{11}^{1} & \Gamma _{22}^{1} \end{bmatrix} = \begin{bmatrix} 0 & 0\\ 0 & - \frac{1}{2}sin\left ( 2u \right ) \end{bmatrix} \begin{bmatrix} \Gamma _{11}^{2} & \Gamma _{12}^{2}\\ \Gamma _{11}^{2} & \Gamma _{22}^{2} \end{bmatrix} = \begin{bmatrix} cot(u) & 0\\ 0 & cot(u) \end{bmatrix} \ (7) \]We add the method levi_civita_coefficients to the class RiemannianConnection. Its implementation of this formula leverages the Geomstats, RiemannianMetric.christoffels method.

def levi_civita_coefficients(self, base_pt: np.array) -> np.array:
   if len(base_pt) != self.ndim:
       raise GeometricException(f'Base pt dimension {len(base_pt)} should be {self.ndim}')
   
   return self.riemannian_metric.christoffels(base_pt)

Let's validate the formula (6) for the Christoffel symbols on the Hypersphere with u = 1.5 and v = 2.0

base_pt = np.array([1.5, 2.0, 1.6])
levi_civita_coefs = riemann_connection.levi_civita_coefficients(base_pt)
print(f'Levi-Civita coefficients:\n{str(levi_civita_coefs)}')

Output:
Levi-Civita coefficients:
[[[ 0.          0.        ]
  [ 0.         -0.07056   ]]

 [[ 0.          0.07091484]
  [ 0.07091484  0.        ]]]

We validate that 0.5*sin(2u) = 0.0756 and cot(u) = 0.0709148

References




-------------
Patrick Nicolas has over 25 years of experience in software and data engineering, architecture design and end-to-end deployment and support with extensive knowledge in machine learning. 
He has been director of data engineering at Aideo Technologies since 2017 and he is the author of "Scala for Machine Learning", Packt Publishing ISBN 978-1-78712-238-3


Appendix


#geometriclearning #riemanngeometry #manifold #differential geometry #ai #python #geomstats

Sunday, April 7, 2024

Geometric Learning in Python: Functional Data Analysis

Target audience: Advanced
Estimated reading time: 7'

In the realms of healthcare and IT monitoring, I encountered the challenge of managing multiple data points across various variables, features, or observations. Functional data analysis (FDA) is well-suited for addressing this issue. 
This article explores how the Hilbert sphere can be used to conduct FDA in non-linear spaces.

Table of contents
        FDA methods
        Formal notation
        Manifold structure
        Inner product
        Exponential map
        Logarithm map
References
Follow me on LinkedIn

What you will learnBasic concepts of functional data analysis in non-linear spaces through the use of manifolds, along with a hands-on application of Hilbert space using Geomstats in Python.

Notes
  • Environments: Python  3.10.10, Geomstats 2.7.0
  • This article assumes that the reader is somewhat familiar with differential and tensor calculus [ref 1]. Please refer to the previous articles related to geometric learning [ref 2, 3].
  • Source code is available at  Github.com/patnicolas/Data_Exploration/manifolds
  • To enhance the readability of the algorithm implementations, we have omitted non-essential code elements like error checking, comments, exceptions, validation of class and method arguments, scoping qualifiers, and import statements.'

Introduction

This article provides a summary of functional data analysis and then proceeds to introduce and implement a technique specifically for non-linear manifolds: Hilbert sphere.

This article is the 6th installment in our series on Geometric Learning in Python following

Functional data analysis

Functional data analysis (FDA) is a statistical approach designed for analyzing curves, images, or functions that exist within higher-dimensional spaces [ref 4].

Observation data types

Panel Data:
In fields like health sciences, data collected through repeated observations over time on the same individuals is typically known as panel data or longitudinal data. Such data often includes only a limited number of repeated measurements for each unit or subject, with varying time points across different subjects.

Time Series:
This type of data comprises single observations made at regular time intervals, such as those seen in financial markets.

Functional Data:
Functional data involves diverse measurement points across different observations (or subjects). Typically, this data is recorded over consistent time intervals and frequencies, featuring a high number of measurements per observational unit/subjects.

FDA methods

Methods in Functional Data Analysis are classified based on the type of manifold (linear or nonlinear) and the dimensionality or feature count of the space (finite or infinite). The categorization and examples of FDA techniques are demonstrated in the table below.

In Functional Data Analysis (FDA), the primary subjects of study are random functions, which are elements in a function space representing curves, trajectories, or surfaces. The statistical modeling and inference occur within this function space. Due to its infinite dimensionality, the function space requires a metric structure, typically a Hilbert structure, to define its geometry and facilitate analysis.

When the function space is properly established, a data scientist can perform various analytical tasks, including:
  • Computing statistics such as mean, covariance, and mode
  • Conducting classification and regression analyses
  • Performing hypothesis testing with methods like T-tests and ANOVA
  • Executing clustering
  • Carrying out inference
The following diagram illustrates a set of random functions around a smooth function X(tilde) over the interval [0, 1] \[\tilde{X}(t)=3e^{-t^{2}}.sin(25t)-2t^{2}+5 \ \ \ t\in [0, 1]\]

Fig. 1 Visualization of a random functions on Hilbert space

Methods in FDA are classified based on the type of manifold (linear or nonlinear) and the dimensionality or feature count of the space (finite or infinite). The categorization and examples of FDA techniques are demonstrated in the table below.

 Manifold
Dimension
Linear
Non-linear
Finite       Euclidean R(n)Special orthogonal SO(3)
InfiniteSquare IntegrableHilbert sphere
          Table 1: Illustration of categorization of FDA techniques

This article focuses on Hilbert space which is specific function space equipped with a Riemann metric (inner product).

Formal notation

Let's consider a sample {x} generated by n Xi random functions as \[x_{i}(t)=X_{i}(t)_{i:1, n} \in \mathbb{R}\ \ \ \ t\in \top \subset \mathbb{R}\]
The function space is a manifold of square integrable functions defined as\[\textit{L}^{2}(T)=\left \{ f: T\rightarrow \mathbb{R}| \int_{T}^{.} f(t)^{T}f(t)dt < \infty \right \}\]The Riemann metric tensor is defined for tangent vectors f and is induced from and equal to the inner product:\[\left \langle f, g \right \rangle = \int_{T}^{} f(t)^{T}g(t)dt\ \ \ \left \| f \right \| _{\mathit{L}^{2}}=\sqrt{\left \langle f, f \right \rangle} \ \ \ \  (1)\]

Hilbert sphere

Hilbert space is a type of vector space that comes with an inner product, which establishes a distance function, making it a complete metric space. In the context of functional data analysis, attention is primarily given to functions that are square-integrable [ref 5].

Hilbert space has numerous important applications:
  • Probability theory: The space of random variables centered by the expectation
  • Quantum mechanics:
  • Differential equations:
  • Biological structures: (Protein structures, folds,..)
  • Medical imaging (MRI, CT-SCAN,...)
  • Meteorology

The Hilbert sphere S, which is infinite-dimensional, has been extensively used for modeling density functions and shapes, surpassing its finite-dimensional equivalent. This spherical Hilbert geometry facilitates invariant properties and allows for the efficient computation of geometric measures.

The Hilbert sphere is a particular case of function space defined as:\[H(T)=\left \{ f: T\rightarrow \mathbb{R} | \ \ \left \| f \right \|_{L^{2}}= 1 \right \}\]The Riemannian exponential map at p from the tangent space to the Hilbert sphere preserves the distance to origin and defined as:\[exp_{p}(f)=cos\left ( \left \| f \right \|_{E} \right )p+sin\left ( \left \| f \right \|_{E} \right)\frac{f}{\left \| f \right \|_{E}} \ \ \ \ (2) \] where ||f||E is the norm of f in the Eculidean space.
The logarithm (or inverse exponential) map is defined at point p, is defined as \[log_{p}(f)=arccos\left (\left \langle p, f \right \rangle_{p} \right )\frac{f}{\left \| f \right \|} \ \ \ \  (3) \]

Implementation

We will illustrate the various coordinates on the hypersphere space we introduced in a previous article Geometric Learning in Python: Manifolds
We leverage class ManifoldPoint introduced in our previous post, ManifoldPoint definition and used across our series on geometric learning. 
As a reminder:

@dataclass
class ManifoldPoint:
id: AnyStr
location: np.array
tgt_vector: List[float] = None
geodesic: bool = False
intrinsic: bool = False

Manifold structure

Let's develop a wrapper class named FunctionSpace to facilitate the creation of points on the Hilbert sphere and to carry out the calculation of the inner product, as well as the exponential and logarithm maps related to the tangent space. 

Our implementation relies on Geomstats library [ref 6] introduced in Geometric Learning in Python: Manifolds 

The function space will be constructed using num_domain_samples, which are evenly spaced real values within the interval [0, 1]. Points on a manifold can be generated using either the Geomstats HilbertSphere.random_point method or by specifying a base point, base_point, and a directional vector.

from geomstats.geometry.functions import HilbertSphere, HilbertSphereMetric


class FunctionSpace(HilbertSphere):
  def __init__(self, num_domain_samples: int):
      domain_samples = gs.linspace(0, 1, num=num_domain_samples)
      super(FunctionSpace, self).__init__(domain_samples, True)

  @staticmethod
  def create_manifold_point(id: AnyStr, vector: np.array, base_point: np.array) -> ManifoldPoint:
     
    # Compute the tangent vector using the direction 'vector' and point 'base_point'
     tgt_vector =  self.to_tangent(vector, base_point)
     return ManifoldPoint(id, base_point, tgt_vector)

  def random_manifold_points(self, n_samples: int) -> List[ManifoldPoint]: 
     return [ManifoldPoint(
           id=f'rand_{n+1}',
           location=random_pt) 
           for n, random_pt in enumerate(self.random_point(n_samples))]

Let's generate a point on the Hilbert sphere using a random base point on the manifold and a 4 dimension vector.

num_samples = 4
function_space = FunctionSpace(num_samples)
random_base_pt = function_space.random_point()

vector = np.array([1.0, 0.5, 1.0, 0.0])
manifold_pt = function_space.create_manifold_point('id', vector, random_pt)

Output:
Manifold point: 
    Base point=[[0.13347 0.85738 1.48770 0.29235]], 
    Tangent Vector=[[ 0.91176 -0.0667 0.01656 -0.19326]],
    No Geodesic, 
    Extrinsic

Inner product

Let's wrap the formula (1) into a method. We introduce the inner_product method to the FunctionSpace class, which serves to encapsulate the call to self.metric.inner_product from the Geomstats method HilbertSphere.inner_product

This method requires two parameters:
  • vector_1: The first vector used in the computation of the inner product
  • vector_2: The second vector used in the computation of the inner product
The second method, manifold_point_inner_product, adds the base point on the manifold without assumption of parallel transport. The base point is origin of both the tangent vector associated with the base point, manifold_base_pt and the tangent vector associated with the second point, manifold_pt.

def inner_product(self, tgt_vector1: np.array, tgt_vector2: np.array) -> np.array:
   return self.metric.inner_product(tgt_vector1,tgt_vector2)

def manifold_point_inner_product(
       self, 
       manifold_base_pt: ManifoldPoint, 
       manifold_pt: ManifoldPoint) -> np.array:

   return self.metric.inner_product(
               manifold_base_pt.tgt_vector,
               manifold_pt.tgt_vector,
            manifold_base_pt.location)

Let's calculate the inner product of two specific numpy vectors in an 8-dimensional space, using our class, FunctionSpace and focusing on the Euclidean inner product and the norm on the tangent space for one of the vectors.

num_Hilbert_samples = 8
functions_space = FunctionSpace(num_Hilbert_samples)
        
vector1 = np.array([0.5, 1.0, 0.0, 0.4, 0.7, 0.6, 0.2, 0.9])
vector2 = np.array([0.5, 0.5, 0.2, 0.4, 0.6, 0.6, 0.5, 0.5])
inner_prod = functions_space.inner_product(vector1, vector2)
print(f'Inner product of vectors 1 & 2: {str(inner_prod)}')
print(f'Euclidean norm of vector 1: {np.linalg.norm(vector)}')
print(f'Norm of vector 1: {str(math.sqrt(inner_prd))}')

Output:
Inner product of vectors1 & 2: 0.2700
Euclidean norm of vector 1: 1.7635
Norm of vector 1: 0.6071

Exponential map

Let's wrap the formula (2) into a method. We introduce the exp method to the FunctionSpace class, which serves to encapsulate the call to self.metric.exp from the Geomstats method HilbertSphere.exp

This method requires two parameters:
  • vector: The directional vector used in the computation the exponential map
  • manifold_base_pt: The base point on the manifold.
def exp(self, vector: np.array, manifold_base_pt: ManifoldPoint) -> np.array:
   return self.metric.exp(tangent_vec=vector, base_point=manifold_base_pt.location)

Let's compute the exponential map at a random base point on the manifold, for a numpy vector of 8-dimensional, using the class, FunctionSpace.

num_Hilbert_samples = 8
function_space = FunctionSpace(num_Hilbert_samples)

vector = np.array([0.5, 1.0, 0.0, 0.4, 0.7, 0.6, 0.2, 0.9])
assert num_Hilbert_samples == len(vector)
        
exp_map_pt = function_space.exp(vector, function_space.random_manifold_points(1)[0])
print(f'Exponential on Hilbert Sphere:\n{str(exp_map_pt)}')

Output:
Exponential on Hilbert Sphere: 
[0.97514 1.6356 0.15326 0.59434 1.06426 0.74871 0.24672 0.95872]

Logarithm map

Let's wrap the formula (3) into a method. We introduce the log method to the FunctionSpace class, which serves to encapsulate the call to self.metric.log from the Geomstats method HilbertSphere.log

This method requires two parameters:
  • manifold_base_pt: The base point on the manifold.
  • target_pt: Another point on the manifold, used to produce the log map.

def log(self, manifold_base_pt: ManifoldPoint, target_pt: ManifoldPoint) ->np.array:
   return self.metric.log(point=manifold_base_pt.location, base_point=target_pt.location)

Let's compute the exponential map at a random base point on the manifold, for a numpy vector of 8-dimensional, using the class, FunctionSpace.

num_Hilbert_samples = 8
function_space = FunctionSpace(num_Hilbert_samples)

random_points = function_space.random_manifold_points(2)
log_map_pt = function_space.log(random_points[0], random_points[1])
print(f'Logarithm from Hilbert Sphere {str(log_map_pt)}')

Output:
Logarithm from Hilbert Sphere 
[1.39182 -0.08986 0.32836 -0.24003 0.30639 -0.28862 -0.431680 4.15148]


References




-------------
Patrick Nicolas has over 25 years of experience in software and data engineering, architecture design and end-to-end deployment and support with extensive knowledge in machine learning. 
He has been director of data engineering at Aideo Technologies since 2017 and he is the author of "Scala for Machine Learning", Packt Publishing ISBN 978-1-78712-238-3