Illustration of Jacobi Fields

This tutorial illustrates the usage of Jacobi Fields within Manopt.jl. For this tutorial, you should be familiar with the basic terminology on a manifold like the exponential and logarithmic maps, as well as the shortest geodesics.


If you open this notebook in Pluto locally it switches between two modes. If the tutorial is within the Manopt.jl repository, this notebook tries to use the local package in development mode. Otherwise, the file uses the Pluto pacakge management version. In this case, the includsion of images might be broken. unless you create a subfolder optimize and activate asy-rendering.

Since the loading is a little complicated, we show, which versions of packages were installed in the following.

with_terminal() do
�[32m�[1mStatus�[22m�[39m `/private/var/folders/_v/wg192lpd3mb1lp55zz7drpcw0000gn/T/jl_8IOXV7/Project.toml`
 �[90m [5ae59095] �[39mColors v0.12.10
 �[90m [af67fdf4] �[39mManifoldDiff v0.2.0
 �[90m [1cead3c2] �[39mManifolds v0.8.42
 �[90m [0fc0a36d] �[39mManopt v0.4.0 `~/Repositories/Julia/Manopt.jl`
 �[90m [7f904dfe] �[39mPlutoUI v0.7.49
 �[90m [44cfe95a] �[39mPkg v1.8.0
 �[90m [9a3f8284] �[39mRandom

and we define some colors from Paul Tol

    black = RGBA{Float64}(colorant"#000000")
    TolVibrantMagenta = RGBA{Float64}(colorant"#EE3377")
    TolVibrantOrange = RGBA{Float64}(colorant"#EE7733")
    TolVibrantCyan = RGBA{Float64}(colorant"#33BBEE")
    TolVibrantTeal = RGBA{Float64}(colorant"#009988")

and setup our graphics paths

    localpath = join(splitpath(@__FILE__)[1:(end - 1)], "/") # files folder
    image_prefix = localpath * "/jacobi_fields"
    #@info image_prefix
    render_asy = false # on CI or when you do not have asymptote, this should be false

Assume we have two points on the equator of the Sphere$\mathcal M = \mathbb S^2$

M = Sphere(2)
Sphere(2, ℝ)
p, q = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
2-element Vector{Vector{Float64}}:
 [1.0, 0.0, 0.0]
 [0.0, 1.0, 0.0]

Their connecting shortest geodesic (sampled at 100 points)

geodesic_curve = shortest_geodesic(M, p, q, [0:0.1:1.0...]);

is just a curve along the equator

render_asy && asymptote_export_S2_signals(
    image_prefix * "/jacobi_geodesic.asy";
    points=[[p, q]],
    colors=Dict(:curves => [black], :points => [TolVibrantOrange]),
    camera_position=(1.0, 1.0, 0.5),
render_asy && render_asymptote(image_prefix * "/jacobi_geodesic.asy"; render=2);
PlutoUI.LocalResource(image_prefix * "/jacobi_geodesic.png")

where $p$ is on the left and $q$ on the right. We now solve the following task.

Given a direction $X ∈ T_p\mathcal M$, for example

3-element Vector{Float64}:

in which we move the starting point $x$, how does any point on the geodesic move?

Or mathematically: compute $D_p g(t; p,q)$ for some fixed $t∈[0,1]$ and a given direction $X_p$. Of course two cases are quite easy: for $t=0$ we are in $x$ and how $x$ “moves” is already known, so $D_x g(0;p,q) = X$. On the other side, for $t=1$, $g(1; p,q) = q$ which is fixed, so $D_p g(1; p,q)$ is the zero tangent vector (in $T_q\mathcal M$).

For all other cases we employ a jacobi_field, which is a (tangent) vector field along the shortest geodesic given as follows: the geodesic variation$\Gamma_{g,X}(s,t)$ is defined for some $\varepsilon > 0$ as

$$\Gamma_{g,X}(s,t):=\exp_{\gamma_{p,X}(s)}[t\log_{g(s;p,X)}p],\qquad s∈(-\varepsilon,\varepsilon),\ t∈[0,1].$$

Intuitively, we make a small step $s$ into the direction of $ξ$ using the geodesic $g(⋅; p,X)$ and from $r=g(s; p,X)$ we follow (in $t$) the geodesic $g(⋅; r,q)$. The corresponding Jacobi field $J_{g,X}$ along $g(⋅; p,q)$ is given by

$$J_{g,X}(t):=\frac{D}{\partial s}\Gamma_{g,X}(s,t)\Bigl\rvert_{s=0}$$

which is an ODE and we know the boundary conditions $J_{g,X}(0)=X$ and $J_{g,X}(t) = 0$. In symmetric spaces we can compute the solution, since the system of ODEs decouples, see for example [doCarmo1992], Chapter 4.2. Within Manopt.jl this is implemented as jacobi_field(M,p,q,t,X[,β]), where the optional parameter (function) $β$ specifies which Jacobi field we want to evaluate and the one used here is the default.

We can hence evaluate that on the points on the geodesic at

T = [0:0.1:1.0...];


r = shortest_geodesic(M, p, q, T);

The tangent vector now moves as a differential along the geodesic as

W = Manopt.jacobi_field.(Ref(M), Ref(p), Ref(q), T, Ref(X), ManifoldDiff.:βdifferential_shortest_geodesic_startpoint);

Which can also be called using differential_shortest_geodesic_startpoint. We can add to the image above by creating extended tangent vectors that include their base points

V = [Tuple([a, b]) for (a, b) in zip(r, W)];

to add the vectors as one further set to the Asymptote export.

render_asy && asymptote_export_S2_signals(
    image_prefix * "/jacobi_geodesic_diff_start.asy";
    points=[[p, q], r],
        :curves => [black],
        :points => [TolVibrantOrange, TolVibrantCyan],
        :tvectors => [TolVibrantCyan],
    dot_sizes=[3.5, 2.0],
    camera_position=(1.0, 1.0, 0.5),
render_asy && render_asymptote(image_prefix * "/jacobi_geodesic_diff_start.asy"; render=2);

The interpretation is as follows: if an infinitesimal change of the starting point in the direction of $X$ happened, the infinitesimal change of a point along the line would change as indicated.

Note that each new vector is a tangent vector to its point (up to a small numerical tolerance), so the blue vectors are not just “shifted and scaled versions” of $X$.

all([is_vector(M, a[1], a[2]; atol=1e-15) for a in V])

If we further move the end point, too, we can derive that differential in direction

    Y = [0.2, 0.0, -0.5]
    W2 = Manopt.differential_shortest_geodesic_endpoint.(Ref(M), Ref(p), Ref(q), T, Ref(Y))
    V2 = [Tuple([a, b]) for (a, b) in zip(r, W2)]

and we can combine both vector fields we obtained

V3 = [Tuple([a, b]) for (a, b) in zip(r, W2 + W)];
render_asy && asymptote_export_S2_signals(
    image_prefix * "/jacobi_geodesic_complete.asy";
    points=[[p, q], r],
    tangent_vectors=[V, V2, V3],
        :curves => [black],
        :points => [TolVibrantOrange, TolVibrantCyan],
        :tvectors => [TolVibrantCyan, TolVibrantMagenta, TolVibrantTeal],
    dot_sizes=[3.5, 2.0],
    camera_position=(1.0, 1.0, 0.0),
render_asy && render_asymptote(image_prefix * "/jacobi_geodesic_complete.asy"; render=2);
PlutoUI.LocalResource(image_prefix * "/jacobi_geodesic_complete.png")

Here, the first vector field is still in blue, the second is in magenta, and their combined effect is in teal. Sure, as a differential this does not make much sense; maybe as an infinitesimal movement of both starting point and endpoint combined.



do Carmo, Manfredo Riemannian Geometry, Birkhäuser Boston, 1992, ISBN: 0-8176-3490-8.