Notebook source code: examples/geometry2D/ellipsoid/ellipsoid.ipynb
Run the notebook yourself on binder Binder badge

The ellipsoid of revolution

by O. Cots, January 2022.

d1a406b492e04c439861c80f858e119c

Abstract. In this notebook, we present how we can use numerical continuation methods (or homotopy) to compute the conjugate and cut loci of a 2-dimensional Riemannian manifold. These methods are applied to the case of an oblate ellipsoid of revolution.

Preliminary remarks. All the numerical experiments are computed with the nutopy package: see the documentation and the gallery of examples. See [Bonnard et al. 2014] for a previous work about geometric and numerical techniques (see the hampath software) to compute conjugate and cut loci on Riemannian surfaces. The main difference with the present work is that in this notebook, we combine second order optimality computations with homotopy methods.

List of numerical algorithms to compute cut loci on a Riemannian manifold.

Preliminaries

We recall standard notions of Riemannian geometry [do Carmo 1988] [Berger 2003]. On a connected Riemannian manifold \((M, g)\) of dimension \(n\), the metric \(g\) induces a distance function \(d(q_0, q_1)\) for any pair \((q_0, q_1) \in M^2\). The distance \(d(q_0, q_1)\) being the infimum of lengths of continuously differentiable curves \(q\) joining the points \(q_0\) and \(q_1\). If we denote by \(l(q)\) the length of a curve \(q\), then on a connected and complete Riemannian manifold, for any pair \((q_0, q_1) \in M^2\), there exists a continuously differentiable curve \(q\) such that \(d(q_0, q_1) = l(q)\). In this case, the solution curve \(q\) being necessarily chosen among the set of the so-called geodesic curves (or geodesics). The geodesics are so candidates as minimizers and we know that every geodesic is a minimizing curve at least on short distances.

Our main goal is to compute for a fixed \(q_0 \in M\), the partial distance function \(d(q_0, \cdot)\) together with the set of minimizing geodesics, that is we want to find for any other point \(q_1 \in M\), the distance \(d(q_0, q_1)\) and the geodesics \(q\) such that \(d(q_0, q_1) = l(q)\). To do so, we have to compute the geodesics which are projections of extremals given by the flow of the Hamiltonian vector field \(\vec{H}\) associated to the Hamiltonian \(H\) given by the Legendre transform of the metric \(g\).

The cut point of a geodesic is the point where it ceases to be minimizing. Fixing \(q_0 \in M\), the set of cut points of all the geodesics starting from \(q_0\) is the cut locus denoted \(\mathrm{Cut}(q_0)\). Our main goal is thus equivalent to compute \(\mathrm{Cut}(q_0)\). We do so in the academic case of an oblate ellipsoid of revolution to illustrate the numerical tools. The theoretical results related to this case may be found in [Itoh-Kiohara 2004]. In the Riemannian frame, the cut points may be of two kinds, either conjugate points or isochronous separating points. A conjugate point is a point where the geodesic \(q\) ceases to be optimal among the geodesics \(C^1\)-close to \(q\). The conjugate points are fold points of the geodesic flow and are due to the intrinsic curvature of the Riemannian manifold. This points may be computed by linearization of the flow of \(\vec{H}\), that is computing Jacobi fields. An isochronous separating point is a point where two distincts minimizing geodesics intersect, that is it is a self-intersection of a wavefront of the geodesic flow. The set of separating points is called the separating locus.

Summary of the main goal

Our main goal is to compute the distance together with the minimizing geodesics, to do so, we aim to compute the cut locus which is made of conjugate points and isochronous separating points.

In Section 2 (Hamiltonian), we define the only one fundamental object for our computations, that is the Hamiltonian \(H\), in the case of an oblate ellipsoid of revolution. From the Hamiltonian, we define the Hamiltonian vector field \(\vec{H}\) and its flow. In Section 3 (Geodesics), we define the geodesic flow parameterized by the initial angle of the covector. The geodesics being parameterized by arc length, setting \(H = 1/2\). In Section 4 (Conjugate locus), we compute by differential continuation method (or homotopy) the set of first conjugate points, that is the conjugate locus. In Section 5 (Wavefronts), we compute self-intersections of wavefronts. Finally, in Section 6 (Cut locus), we compute the cut locus which is composed of two conjugate points and the separating locus, which is also computed by homotopy.

Back to top

[1]:
# Packages
import numpy as np                        # scientific computing tools
import nutopy as nt                       # control toolbox: indirect methods and homotopy
import wrappers                           # for compilation of Fortran Hamiltonian codes
import plottings                          # for plots
import bdd                                # for saving data

Hamiltonian and flow

The Cartesian equation of the normalized 2-dimensional ellipsoid of revolution may be written as

\[x^2 + y^2 + \frac{z^2}{\varepsilon^2} = 1.\]

The associated ellipsoid of revolution may be generated by a rotation of axis \(Oz\) of the parameterized curve

\[y=\cos u,\quad z=\varepsilon\sin u,\]

with \(u \in [0, 2\pi]\) and where \(0<\varepsilon<1\) corresponds to the oblate (flattened) case while \(\varepsilon >1\) is the prolate (elongated) case. The Cartesian coordinates may be parameterized by the azimuth \(\theta \in [-\pi, \pi]\) and the latitude \(\varphi \in [-\pi/2, \pi/2]\), by the relations

\[x=\cos\varphi\cos\theta, \quad y=\cos\varphi\sin\theta, \quad z=\varepsilon\sin\varphi.\]

The restriction of the Euclidean metric of the ambient space \(\mathrm{R}^3\) gives the following induced metric on the ellipsoid:

\[g = g_{1}(\varphi)~\mathrm{d}\theta^{2} + g_{2}(\varphi)~\mathrm{d}\varphi^{2}\]

where \(g_{1}(\varphi)=\cos^{2}\varphi\) and \(g_{2}(\varphi)=\sin^{2}\varphi+\varepsilon^{2}\cos^{2}\varphi\). In the following, we consider the oblate case and we fix \(\varepsilon = 0.75\) and \(q_0 = (0, 0)\). The Legendre transform of the metric \(g\) gives the associated Hamiltonian

\[H=\frac{1}{2}\left(\frac{p_{\theta}^{2}}{g_{1}(\varphi)} + \frac{p_{\varphi}^{2}}{g_{2}(\varphi)}\right).\]

From the Hamiltonian \(H\), we define the Hamiltonian vector field defined on the cotangent bundle \(T^*M\) (\(M\) being the ellipsoid)

\[\vec{H}(q,p) \overset{\mathrm{def}}{=} \left(\frac{\partial H}{\partial p}(q,p), -\frac{\partial H}{\partial q}(q,p)\right).\]

We finally define the Hamiltonian exponential map, \(e^{t \vec{H}} \colon T^*M \to T^*M\), by

\[e^{t \vec{H}}(q_0, p_0) \overset{\mathrm{def}}{=} z(t, q_0, p_0) = (q(t, q_0, p_0), p(t, q_0, p_0)),\]

where the extremal \(z(\cdot, q_0, p_0)\) is defined as the maximal solution of the Cauchy problem \(\dot{z}(t) = \vec{H}(z(t))\), \(z(0) = (q_0, p_0)\). Thus, the Hamiltonian exponential map gives us the flow of extremals, associated to the Hamiltonian system.

[2]:
# Fortran Hamiltonian code
#
# We use Fortran code for performances and because we need to compute the derivatives
# of the Hamiltonian up to order 3, which is done by automatic differentiation,
# thanks to tapenade (https://team.inria.fr/ecuador/fr/tapenade/) software.
!pygmentize hfun.f90
subroutine hfun(x, p, epsi, h)

    double precision, intent(in)  :: x(2), p(2), epsi
    double precision, intent(out) :: h

    ! local variables
    double precision :: theta, phi, ptheta, pphi, g1, g2

    theta   = x(1)
    phi     = x(2)

    ptheta  = p(1)
    pphi    = p(2)

    g1      = cos(phi)**2
    g2      = sin(phi)**2+epsi**2*cos(phi)**2

    h       = 0.5d0 * (ptheta**2 / g1 + pphi**2 / g2)

end subroutine hfun
[3]:
# Parameters
epsilon   = 0.75 # oblate case (epsilon < 1)
theta0    = 0.0  # initial azimuth
phi0      = 0.0  # initial latitude
t0        = 0.0  # initial time

# Initialize data
data_file = 'data.json'
restart   = True # restart or not the computations
data      = bdd.Data({'t0': t0, 'theta0': theta0, 'phi0': phi0, 'epsilon': epsilon}, data_file, restart)

# Initial point
q0        = np.array([theta0, phi0])
Initiate done
[4]:
h        = wrappers.hamiltonian(epsilon, compile=True, display=False) # Hamiltonian and derivatives up to order 3
extremal = nt.ocp.Flow(h) # Hamiltonian exponential map and its derivatives: extremals and linearizations

Back to top

Geodesics

By homogeneity, the extremals may be parameterized fixing \(H=1/2\). This amounts to parameterize the geodesics by the arc length. Thus, given an initial point \(q_0 \in M\), the initial covector \(p_0\) must satisfy \(H(q_0, p_0)=1/2\). One can write that \(p_0 \in S^1 = \{p ~|~ ||p||=1\}\), defining the norm \(||p||^2=2H(q_0,p)\) on \(T_{q_0}^*M\). Hence, the initial covector may be parameterized by its angle \(\alpha_0 \in [0,2\pi)\). We write

\[p_0 = p(\alpha_0) \overset{\mathrm{def}}{=} \left(\cos \alpha_0 \sqrt{g_1(\varphi_0)}, \sin \alpha_0 \sqrt{g_2(\varphi_0)}\right) \in T_{q_0}^*M\]

this parameterization. Finally, a geodesic starting from \(q_0\) is the projection of an extremal parameterized by its initial angle \(\alpha_0\) and is given by the following classical exponential map:

\[\exp_{q_0}(t, \alpha_0) \overset{\mathrm{def}}{=} \pi_q \left( e^{t \vec{H}}(q_0, p(\alpha_0)) \right),\]

where \(\pi_q(q, p) = q\) is the canonical projection on the state space.

[5]:
# The Riemannian metric of the ellipsoid of revolution
# We get the metric from the Hamiltonian description
def metric(q):
    h1    = h(t0, q, np.array([1.0, 0.0]))
    h2    = h(t0, q, np.array([0.0, 1.0]))
    g1    = 1.0/(2.0*h1)
    g2    = 1.0/(2.0*h2)
    return g1, g2

# Initial covector parameterization and its derivatives up to order 2
def covector(q, alpha):
    g1, g2 = metric(q)
    p0 = np.array([np.cos(alpha)*np.sqrt(g1),
                   np.sin(alpha)*np.sqrt(g2)])
    return p0

def dcovector(q, alpha, dalpha):
    g1, g2 = metric(q)
    dp0 = np.array([-np.sin(alpha)*np.sqrt(g1)*dalpha,
                     np.cos(alpha)*np.sqrt(g2)*dalpha])
    return dp0

def d2covector(q, alpha, dalpha, d2alpha):
    g1, g2 = metric(q)
    d2p0 = np.array([-np.cos(alpha)*np.sqrt(g1)*dalpha*d2alpha,
                     -np.sin(alpha)*np.sqrt(g2)*dalpha*d2alpha])
    return d2p0

covector = nt.tools.tensorize(dcovector, d2covector, tvars=(2,))(covector)
[6]:
# Compute a geodesic parameterized by alpha0 to time t
# The initial point is fixed to q0 and t0 = 0
def tspan(t0, t):
    N      = 100
    return list(np.linspace(t0, t, N+1))

def geodesic(t, alpha0):
    p0     = covector(q0, alpha0)
    q, p   = extremal(t0, q0, p0, tspan(t0, t))
    return q

# Function to plot geodesics
geodesics_plot = plottings.Geodesics(geodesic, epsilon)
[7]:
geodesics_plot.interact(embed=True)
[7]:
Geodesics

Back to top

Conjugate locus

A point \(q(t_c)\) on an extremal \(z=(q,p)\) is conjugate to \(q_0 = q(0)\) if there exists a Jacobi field \(\delta z = (\delta q, \delta p)\), solution of the linearized system along the extremal,

\[\delta \dot{z}(t) = \frac{\partial}{\partial z} \vec{H}(z(t)) \cdot \delta z(t),\]

which is non-trivial (\(\delta q \not \equiv 0\)) and vertical at \(t=0\) and \(t_c>0\) (called the conjugate time), that is \(\delta q(0) = \delta q(t_c) = 0\). The conjugate locus is the set of such first points on extremals departing from \(q_0\). Conjugacy is classicaly related to local optimality of extremals in the relevant topologies. Let \(q \colon t \mapsto \exp_{q_0}(t, \alpha_0)\) being a reference geodesic and introduce \(F_\mathrm{conj} \colon \mathrm{R}^*_+ \times [0, 2\pi) \to \mathrm{R}\) as

\[F_\mathrm{conj}(t, \alpha) \overset{\mathrm{def}}{=} \det\left( \exp_{q_0}'(t, \alpha) \right).\]

Then, in the Riemannian setting, \(q(t_c)\) is conjugate to \(q_0\) if and only if \(F_\mathrm{conj}(t_c, \alpha_0) = 0\). The conjugate locus from \(q_0\) is thus given by

\[\mathrm{Conj}(q_0) = \bigcup_{\alpha_0 \in [0, 2\pi)} \{ q(t_{1c}) ~|~ q(t_{1c}) = \exp_{q_0}(t_{1c}, \alpha_0),~ F_\mathrm{conj}(t_{1c}, \alpha_0) = 0\},\]

where \(t_{1c}\) has to be understood in the sense that it is the first conjugate time. To compute the conjugate locus, we need to compute the first conjugate times, that is to compute

\[\bigcup_{\alpha_0 \in [0, 2\pi)} \{ (t_{1c}, \alpha_0) ~|~ F_\mathrm{conj}(t_{1c}, \alpha_0) = 0\} \subset F_\mathrm{conj}^{-1}(\{0\}).\]

Algorithm

We have to compute a subset of \(F_\mathrm{conj}^{-1}(\{0\})\). We use the numerical continuation method (or homotopy method) [Allgower-Georg 2003] implemented in the nutopy package. Under some regularity assumptions, the set \(F_\mathrm{conj}^{-1}(\{0\})\) is a disjoint union of differential curves, each curve being called a path of zeros. To compute a path of zeros, we search a first point on the curve by fixing the homotopic parameter (here it will be \(\alpha_0\)) to a certain value \(\alpha_0^*\) and then calling a Newton method to solve \(F_\mathrm{conj}(\cdot, \alpha_0^*) = 0\). The Newton solver in the nutopy package is the hybrj code from the minpack library [Moré et al. 1980].

When we have our initial point on the path of zeros, we use a Predictor-Corrector (PC) method with arc length parameterization to compute the differential curve. The nutopy package implements a PC method with a high-order Runge-Kutta scheme with adaptive step size for the prediction [Hairer et al. 1993], so we compute the path of zeros with high precision and we let the integration solver to construct the discretization grid of the homotopic parameter. The prediction part is the same as in the hampath software [Caillau et al. 2011]. The correction step is performed by a simplified Newton algorithm [Hairer-Wanner 1996, page 119] with few iterations and where the Jacobian of the associated system to solve is not updated along the iterations.

For the prediction and the correction steps, we need to compute the Jacobian of \(F_\mathrm{conj}\). It is computed by a combination of automatic differentiation (to get \(\vec{H}\) and its derivatives up to order 2) performed with tapenade software [Hascoët-Pascual 2012] and variational equations to compute Jacobi fields and their derivatives with respect to the initial angle \(\alpha_0\).

[8]:
# Jacobi field: dz(t, p(alpha0), dp(alpha0))
@nt.tools.vectorize(vvars=(1,))
def jacobi(t, alpha0):
    p0, dp0  = covector(q0, (alpha0, 1.))
    (q, dq), (p, dp) = extremal(t0, q0, (p0, dp0), t)
    return (q, dq), (p, dp)

# Derivative of dq w.r.t. t and alpha0
def djacobi(t, alpha0):
    #
    p0, dp0, d2p0 = covector(q0, (alpha0, 1., 1.))
    #
    (q, dq1, d2q), (p, dp1, _) = extremal(t0, q0, (p0, dp0, dp0), t)
    (q, dq2), (p, dp2)         = extremal(t0, q0, (p0, d2p0), t)
    #
    hv, dhv   = h.vec(t, (q, dq1), (p, dp1))
    #
    ddqda     = d2q+dq2  # ddq/dalpha
    ddqdt     = dhv[0:2] # ddq/dt
    return (q, dq1), (p, dp1), (ddqdt, ddqda)
[9]:
# Function to compute conjugate time together with the initial angle
# and the associated conjugate point
#
# conjugate(tc, qc, a0) = ( det( dq(tc, a0), Hv(tc, z(tc, a0)) ),
#                        qc - pi_q(z(tc, a0)) ),
#
# where pi_q(q, p) = q and z(t, a) = extremal(t0, q0, p(a), t).
#
# Remark: y = (tc, qc)
#
def conjugate(y, a):
    tc     = y[0]
    qc     = y[1:3]
    alpha0 = a[0]
    #
    (q, dq), (p, dp) = jacobi(tc, alpha0)
    hv     = h.vec(tc, q, p)[0:2]
    #
    c      = np.zeros(3)
    c[0]   = np.linalg.det([hv, dq])
    c[1:3] = qc - q
    return c

# Derivative of conjugate
def dconjugate(y, a):
    tc     = y[0]
    qc     = y[1:3]
    alpha0 = a[0]
    #
    (q, dq), (p, dp), (ddqdt, ddqda) = djacobi(tc, alpha0)
    #
    # dc/da
    hv, dhv     = h.vec(tc, (q, dq), (p, dp))
    dcda        = np.zeros((3, 1))
    dcda[0,0]   = np.linalg.det([dhv[0:2], dq])+np.linalg.det([hv[0:2], ddqda])
    dcda[1:3,0] = -dq
    #
    # dc/dy = (dc/dt, dc/dq)
    hv, dhv     = h.vec(tc, (q, hv[0:2]), (p, hv[2:4]))
    dcdy        = np.zeros((3, 3))
    dcdy[0,0]   = np.linalg.det([dhv[0:2], dq])+np.linalg.det([hv[0:2], ddqdt])
    dcdy[1:3,0] = -hv[0:2]
    dcdy[1,1]   = 1.
    dcdy[2,2]   = 1.
    return dcdy, dcda
[10]:
def get_conjugate_locus():

    # -------------------------
    # Get first conjugate point

    # Initial guess
    gap    = 1e-2
    tci    = np.pi
    alpha  = np.array([-np.pi/2+gap])
    p0     = covector(q0, float(alpha))
    xi, pi = extremal(t0, q0, p0, tci)

    yi      = np.zeros(3)
    yi[0]   = tci
    yi[1:3] = xi

    # Equations and derivative
    fun   = lambda t: conjugate(t, alpha)
    dfun  = lambda t: dconjugate(t, alpha)[0]

    # Callback
    def print_conjugate_time(infos):
        print('    Conjugate time estimation: tc = %e for alpha = %e' % (infos.x[0], alpha), end='\r')

    # Options
    opt  = nt.nle.Options(Display='on')

    # Conjugate point calculation for initial homotopic parameter
    print(' > Get first conjugate time and point:\n')
    sol   = nt.nle.solve(fun, yi, df=dfun, callback=print_conjugate_time, options=opt)

    # -------------------
    # Get conjugate locus

    # Options
    opt = nt.path.Options(MaxStepSizeHomPar=0.05, Display='on');

    # Homotopic parameter range: we compute only half of the conjugate locus
    # the other part is obtained by symmetry
    alpha0  = alpha
    alphaf  = np.pi/2.-gap

    # Initial solution
    y0 = sol.x

    # Callback
    def progress(infos):
        current   = float(infos.pars)-alpha0
        total     = alphaf-alpha0
        barLength = 50
        percent   = float(current * 100.0 / total)
        arrow     = '-' * int(percent/100 * barLength - 1) + '>'
        spaces    = ' ' * (barLength - len(arrow))

        print('    Progress: [%s%s] %1.2f %%' % (arrow, spaces, round(percent, 2)), end='\r')

    # Conjugate locus calculation
    print('\n\n > Get conjugate locus:\n')
    sol = nt.path.solve(conjugate, y0, alpha0, alphaf, options=opt, df=dconjugate) #, callback=progress)
    print('\n')

    return sol.xout
[11]:
# Get conjugate locus
if data.contains('conjugate_locus') and not restart:
    conjugate_locus = np.array(data.get('conjugate_locus'));   print('Conjugate locus loaded')
else:
    conjugate_locus = get_conjugate_locus()
    data.update({'conjugate_locus':conjugate_locus.tolist()}); print('Conjugate locus saved')

# Function to plot the conjugate locus
conjugate_plot = plottings.Conjugate_Locus(conjugate_locus, epsilon)
 > Get first conjugate time and point:


     Calls  |f(x)|                 |x|

         1  3.936995823975282e-01  4.467647749516624e+00
         2  4.278155636398811e-02  4.672169273455422e+00ha = -1.560796e+00
         3  4.722661649310230e-03  4.650876408102643e+00ha = -1.560796e+00
         4  3.351515768332736e-05  4.652974457351447e+00ha = -1.560796e+00
         5  4.566345728662711e-07  4.652989155900476e+00ha = -1.560796e+00
         6  6.933441770142508e-09  4.652988953911271e+00ha = -1.560796e+00
         7  6.032213481406154e-12  4.652988950795741e+00ha = -1.560796e+00
    Conjugate time estimation: tc = 3.351569e+00 for alpha = -1.560796e+00
 Results of the nle solver method:

 xsol    =  [3.35156853 3.14159181 0.7400645 ]
 f(xsol) =  [-5.89486114e-12 -8.34887715e-14  1.27720057e-12]
 nfev    =  7
 njev    =  1
 status  =  1
 success =  True

 Successfully completed: relative error between two consecutive iterates is at most TolX.



 > Get conjugate locus:


     Calls  |f(x,pars)|     |x|                Homotopic param    Arclength s     det A(s)        Dot product

         1  3.36217081e-16  4.65298895079e+00 -1.56079632679e+00  0.00000000e+00 -2.10225656e+00  0.00000000e+00
         2  5.77742394e-13  4.65283083245e+00 -1.55223936044e+00  8.56107018e-03 -2.10300750e+00  9.99832773e-01
         3  5.84715180e-13  4.65228799944e+00 -1.53660176544e+00  2.42242662e-02 -2.10553835e+00  9.99443401e-01
         4  7.74366996e-14  4.65032495696e+00 -1.50702697627e+00  5.39659520e-02 -2.11437003e+00  9.98033109e-01
         5  2.10209079e-14  4.64452284645e+00 -1.46009822249e+00  1.01725692e-01 -2.13885089e+00  9.95225781e-01
         6  3.95429722e-16  4.63442490080e+00 -1.41009822249e+00  1.53822109e-01 -2.17786443e+00  9.94965694e-01
         6  6.55629916e-12  4.63052344604e+00 -1.39495252337e+00  1.69926644e-01 -2.17786443e+00  9.94965694e-01
         7  2.62314084e-16  4.61473732598e+00 -1.34495252337e+00  2.24404281e-01 -2.24515108e+00  9.92513686e-01
         7  3.69787613e-10  4.60685712041e+00 -1.32423756361e+00  2.47639686e-01 -2.24515108e+00  9.92513686e-01
         8  1.10196606e-15  4.58452059946e+00 -1.27423756361e+00  3.05557633e-01 -2.33239968e+00  9.92870424e-01
         8  1.30628830e-10  4.57198025255e+00 -1.24999003099e+00  3.34658131e-01 -2.33239968e+00  9.92870424e-01
         9  4.80644813e-16  4.54257820059e+00 -1.19999003099e+00  3.96931156e-01 -2.43025014e+00  9.94102156e-01
         9  2.27122364e-11  4.52870510519e+00 -1.17883539796e+00  4.24240654e-01 -2.43025014e+00  9.94102156e-01
        10  3.26545324e-16  4.49256405662e+00 -1.12883539796e+00  4.91157125e-01 -2.52107133e+00  9.96125663e-01
        10  1.41916037e-11  4.47697946530e+00 -1.10904985207e+00  5.18574158e-01 -2.52107133e+00  9.96125663e-01
        11  1.44051684e-16  4.43443170871e+00 -1.05904985207e+00  5.90256707e-01 -2.60055332e+00  9.97385979e-01
        11  1.38531041e-11  4.41615748110e+00 -1.03903368375e+00  6.19915012e-01 -2.60055332e+00  9.97385979e-01
        12  9.00468956e-17  4.36752408119e+00 -9.89033683753e-01  6.96371942e-01 -2.66597520e+00  9.98178838e-01
        12  1.11933162e-11  4.34484215037e+00 -9.67053497959e-01  7.31032647e-01 -2.66597520e+00  9.98178838e-01
        13  1.59663385e-16  4.29044172045e+00 -9.17053497959e-01  8.12169167e-01 -2.71466712e+00  9.98683996e-01
        13  8.87178375e-12  4.26152038299e+00 -8.91765435103e-01  8.54361387e-01 -2.71466712e+00  9.98683996e-01
        14  6.11328735e-16  4.20177049944e+00 -8.41765435103e-01  9.39890557e-01 -2.74211822e+00  9.99022494e-01
        14  6.84569137e-12  4.16506343481e+00 -8.12277990008e-01  9.91525561e-01 -2.74211822e+00  9.99022494e-01
        15  4.59628602e-16  4.10063443824e+00 -7.62277990008e-01  1.08080777e+00 -2.74107317e+00  9.99238774e-01
        15  2.53598171e-12  4.05579607582e+00 -7.28520549530e-01  1.14209503e+00 -2.74107317e+00  9.99238774e-01
        16  1.50373649e-16  3.98783318359e+00 -6.78520549530e-01  1.23391294e+00 -2.70141488e+00  9.99312873e-01
        16  3.77405158e-12  3.93654088059e+00 -6.41411148461e-01  1.30250105e+00 -2.70141488e+00  9.99312873e-01
        17  5.70033059e-16  3.86688402129e+00 -5.91411148461e-01  1.39488960e+00 -2.61242143e+00  9.99151101e-01
        17  4.66747130e-12  3.81620674579e+00 -5.55004158048e-01  1.46170323e+00 -2.61242143e+00  9.99151101e-01
        18  1.38940538e-16  3.74740098143e+00 -5.05004158048e-01  1.55212918e+00 -2.47233825e+00  9.98684418e-01
        18  1.21855294e-12  3.72614170756e+00 -4.89315634020e-01  1.58005587e+00 -2.47233825e+00  9.98684418e-01
        19  2.50057839e-16  3.65997685872e+00 -4.39315634020e-01  1.66718032e+00 -2.33187599e+00  9.98729410e-01
        19  1.81254752e-12  3.65207762296e+00 -4.33193369808e-01  1.67762307e+00 -2.33187599e+00  9.98729410e-01
        20  7.84874384e-16  3.59117371607e+00 -3.84445674940e-01  1.75871308e+00 -2.19457718e+00  9.98625018e-01
        21  1.16016696e-15  3.54086839454e+00 -3.41357577646e-01  1.82697003e+00 -2.07650992e+00  9.98766677e-01
        22  1.33453408e-15  3.49915191638e+00 -3.02691869545e-01  1.88516005e+00 -1.96520489e+00  9.98622707e-01
        23  1.91116368e-15  3.46440245247e+00 -2.67471198096e-01  1.93547554e+00 -1.86159075e+00  9.98448019e-01
        24  5.22445396e-16  3.43245589811e+00 -2.31467781133e-01  1.98418043e+00 -1.75602853e+00  9.97806297e-01
        25  1.55933646e-15  3.40604611640e+00 -1.97657553185e-01  2.02741557e+00 -1.65995527e+00  9.97394802e-01
        26  1.70599868e-15  3.38425814214e+00 -1.65290125315e-01  2.06661846e+00 -1.57360520e+00  9.96850879e-01
        27  5.64633118e-17  3.36664567448e+00 -1.34036425052e-01  2.10258991e+00 -1.49831689e+00  9.96219777e-01
        28  4.57610102e-16  3.35240967335e+00 -1.02438470058e-01  2.13729718e+00 -1.43331244e+00  9.95155254e-01
        29  1.82964717e-15  3.34210940178e+00 -7.16657098651e-02  2.16977429e+00 -1.38364908e+00  9.94444267e-01
        30  7.88965034e-16  3.33617700593e+00 -4.54805550564e-02  2.19663541e+00 -1.35393633e+00  9.95402636e-01
        31  7.07375853e-17  3.33292560626e+00 -1.98205498465e-02  2.22251113e+00 -1.33728275e+00  9.95218862e-01
        32  5.67289216e-16  3.33219350111e+00  4.01312224174e-03  2.24636455e+00 -1.33349558e+00  9.95727846e-01
        33  5.69372621e-16  3.33352498588e+00  2.64849308369e-02  2.26888288e+00 -1.34037302e+00  9.96223509e-01
        34  5.28997031e-16  3.33678438236e+00  4.88050525766e-02  2.29144678e+00 -1.35701778e+00  9.96423325e-01
        35  1.66947380e-15  3.34211508798e+00  7.16862645022e-02  2.31494958e+00 -1.38367716e+00  9.96510538e-01
        36  6.30365696e-16  3.34983204654e+00  9.56508988420e-02  2.34014070e+00 -1.42110290e+00  9.96565863e-01
        37  2.39890791e-15  3.36077349326e+00  1.21959163489e-01  2.36866417e+00 -1.47198451e+00  9.96425954e-01
        38  5.52061780e-16  3.37828603309e+00  1.55353615342e-01  2.40645602e+00 -1.54866626e+00  9.95313888e-01
        39  1.06248862e-15  3.39778614853e+00  1.85988510549e-01  2.44291366e+00 -1.62803471e+00  9.96920072e-01
        40  1.23730409e-15  3.41916995400e+00  2.15036688685e-01  2.47921164e+00 -1.70878480e+00  9.97834829e-01
        41  2.57517180e-15  3.44303786314e+00  2.43877850653e-01  2.51699742e+00 -1.79219613e+00  9.98330374e-01
        42  1.04724911e-15  3.46981667094e+00  2.73188101615e-01  2.55721552e+00 -1.87845571e+00  9.98654678e-01
        43  1.07548426e-15  3.50013832931e+00  3.03646149909e-01  2.60094198e+00 -1.96799327e+00  9.98871713e-01
        44  1.08113343e-15  3.53466766725e+00  3.35806922017e-01  2.64919504e+00 -2.06078575e+00  9.99029444e-01
        45  1.51719842e-15  3.57418040225e+00  3.70236659686e-01  2.70310315e+00 -2.15650514e+00  9.99149143e-01
        46  1.40745439e-15  3.61955004831e+00  4.07534031825e-01  2.76392073e+00 -2.25434537e+00  9.99244595e-01
        47  1.66778075e-15  3.67177076663e+00  4.48387076033e-01  2.83309210e+00 -2.35291509e+00  9.99324010e-01
        48  9.98599896e-16  3.73194506621e+00  4.93612941474e-01  2.91227133e+00 -2.45001350e+00  9.99393033e-01
        49  2.01048489e-16  3.80042294229e+00  5.43612941474e-01  3.00223675e+00 -2.54137311e+00  9.99465904e-01
        49  8.59699028e-14  3.80128195501e+00  5.44233827455e-01  3.00336630e+00 -2.54137311e+00  9.99465904e-01
        50  1.17826554e-16  3.87082079240e+00  5.94233827455e-01  3.09501534e+00 -2.61611734e+00  9.99602147e-01
        50  1.35606344e-12  3.88096499022e+00  6.01506565208e-01  3.10843018e+00 -2.61611734e+00  9.99602147e-01
        51  4.75742083e-16  3.95054629753e+00  6.51506565208e-01  3.20087675e+00 -2.67926744e+00  9.99614698e-01
        51  1.91926025e-12  3.97192024909e+00  6.66963967585e-01  3.22945227e+00 -2.67926744e+00  9.99614698e-01
        52  1.33372867e-16  4.04023538910e+00  7.16963967585e-01  3.32147842e+00 -2.72488759e+00  9.99576808e-01
        52  1.61160577e-12  4.07404137602e+00  7.42169640568e-01  3.36747094e+00 -2.72488759e+00  9.99576808e-01
        53  5.64454588e-16  4.13946386345e+00  7.92169640568e-01  3.45751274e+00 -2.74536964e+00  9.99415300e-01
        53  4.21965273e-12  4.18442298114e+00  8.27726876347e-01  3.52033237e+00 -2.74536964e+00  9.99415300e-01
        54  4.29729479e-17  4.24507866673e+00  8.77726876347e-01  3.60660080e+00 -2.73221579e+00  9.98983514e-01
        54  1.07032852e-11  4.29578645734e+00  9.21816899287e-01  3.68039616e+00 -2.73221579e+00  9.98983514e-01
        55  1.61817654e-16  4.34982324419e+00  9.71816899287e-01  3.76123467e+00 -2.67945262e+00  9.98063478e-01
        55  2.80432977e-11  4.39829638798e+00  1.02016290030e+00  3.83632657e+00 -2.67945262e+00  9.98063478e-01
        56  3.12232205e-16  4.44427459354e+00  1.07016290030e+00  3.91067519e+00 -2.58874713e+00  9.96459022e-01
        56  3.58128429e-11  4.47375387033e+00  1.10506499383e+00  3.96054157e+00 -2.58874713e+00  9.96459022e-01
        57  5.48533528e-16  4.51210725427e+00  1.15506499383e+00  4.02906912e+00 -2.48842282e+00  9.95738990e-01
        57  1.85351298e-11  4.53251580448e+00  1.18451907649e+00  4.06785685e+00 -2.48842282e+00  9.95738990e-01
        58  7.34614025e-16  4.56339256750e+00  1.23451907649e+00  4.13110903e+00 -2.38459058e+00  9.94417320e-01
        58  1.57446508e-10  4.58140126273e+00  1.26799906949e+00  4.17172367e+00 -2.38459058e+00  9.94417320e-01
        59  6.09843207e-16  4.60432733985e+00  1.31799906949e+00  4.22998168e+00 -2.27712077e+00  9.91240521e-01
        59  3.85307694e-10  4.61612211201e+00  1.34882433425e+00  4.26459538e+00 -2.27712077e+00  9.91240521e-01
        60  4.84023117e-16  4.63155904485e+00  1.39882433425e+00  4.31890889e+00 -2.18830430e+00  9.89099925e-01
        60  1.15124381e-09  4.63883737408e+00  1.42947533172e+00  4.35125080e+00 -2.18830430e+00  9.89099925e-01
        61  4.85400046e-16  4.64733335647e+00  1.47947533172e+00  4.40280386e+00 -2.12723441e+00  9.86868814e-01
        61  4.54871888e-11  4.65069994450e+00  1.51146292969e+00  4.43521088e+00 -2.12723441e+00  9.86868814e-01
        62  3.87323870e-16  4.65298895079e+00  1.56079632679e+00  4.48470255e+00 -2.10225656e+00  9.85263608e-01

 Results of the path solver method:

 xf            =  [ 3.35156853  3.14159181 -0.7400645 ]
 parsf         =  [1.56079633]
 |f(xf,parsf)| =  3.87323869939913e-16
 steps         =  89
 status        =  1
 success       =  True

 Homotopy successfully completed.



Update done
Conjugate locus saved
[12]:
# Plots on the 2D chart
fig = geodesics_plot(nb_geodesics=5) # Plot geodesics
conjugate_plot(fig=fig)               # Plot conjugate locus
[12]:
<Figure size 675x720 with 1 Axes>
[ ]:
# Plots on the Ellipsoid
fig = geodesics_plot(coords=plottings.Coords.ELLIPSOID, nb_geodesics=5) # Plot geodesics
conjugate_plot(coords=plottings.Coords.ELLIPSOID, fig=fig)               # Plot conjugate locus

Figures

Geodesics departing from \(q_0=(0,0)\) for different \(\alpha_0 \in [0, 2\pi)\). Note the envelope when the flow of the geodesics is folding. This is the conjugate locus (in red).

Back to top

Wavefronts

The wavefront at time \(t \ge 0\) departing from \(q_0 \in M\) is given by

\[\bigcup_{\alpha_0 \in [0, 2\pi)} \{ \exp_{q_0}(t, \alpha_0) \}.\]

To emphasize the numerical methodology that we use to compute the wavefront, we define

\[\mathrm{Front}(q_0, t) \overset{\mathrm{def}}{=} \bigcup_{\alpha_0 \in [0, 2\pi)} \{ (q, \alpha) ~|~ q=\exp_{q_0}(t, \alpha),~ \alpha=\alpha_0\}.\]

Fixing \(q_0 \in M\) and \(t\ge 0\) and introducing \(F_\mathrm{front}\colon M^2 \times [0,2\pi) \to \mathrm{R}^2\) by

\[F_\mathrm{front}(q, \alpha) \overset{\mathrm{def}}{=} q-\exp_{q_0}(t, \alpha),\]

then we have \(\mathrm{Front}(q_0, t) = F_\mathrm{front}^{-1}(\{0\})\). Hence, \(F_\mathrm{front}\) is an homotopic function and \(\mathrm{Front}(q_0, t)\) is a path of zeros of \(F_\mathrm{front}\) that can be computed by differential homotopy methods. Once we have computed a wavefront, we compute a self-intersection of it, if any, to get our first cut point which is used in the following to compute the cut locus.

[ ]:
# Equation to calculate wavefronts
def wavefront_eq(q, alpha0, tf):
    p0    = covector(q0, float(alpha0))
    qf, _ = extremal(t0, q0, p0, tf)
    return q - qf

# Derivative
def dwavefront_eq(q, dq, alpha0, dalpha0, tf):
    p0, dp0      = covector(q0, (float(alpha0), float(dalpha0)))
    (qf, dqf), _ = extremal(t0, q0, (p0, dp0), tf)
    return q-qf, dq - dqf

wavefront_eq = nt.tools.tensorize(dwavefront_eq, tvars=(1, 2), full=True)(wavefront_eq)
[ ]:
# Function to compute wavefront at time tf, q0 being fixed
def get_wavefront(tf):

    # Options
    opt = nt.path.Options(Display='off', MaxStepSizeHomPar=0.1, MaxIterCorrection=0);

    # Homotopic parameter range
    epsi    = 1e-4
    alpha0  = -np.pi/2.0+epsi
    alphaf  =  np.pi/2.0-epsi

    # Initial solution
    p0     = covector(q0, alpha0)
    xf0, _ = extremal(t0, q0, p0, tf)

    # callback
    def progress(infos):
        current   = float(infos.pars)-alpha0
        total     = alphaf-alpha0
        barLength = 50
        percent   = float(current * 100.0 / total)
        arrow     = '-' * int(percent/100.0 * barLength - 1) + '>'
        spaces    = ' ' * (barLength - len(arrow))

        print('    Progress: [%s%s] %1.2f %%' % (arrow, spaces, round(percent, 2)), end='\r')

    # wavefront computation
    print('\n > Get wavefront for tf =', tf, '\n')
    sol = nt.path.solve(wavefront_eq, xf0, alpha0, alphaf, args=tf, options=opt, df=wavefront_eq, callback=progress)
    print('\n')

    wavefront = (sol.xout, sol.parsout, tf)

    return wavefront
[ ]:
# Get wavefronts
if data.contains('wavefronts') and not restart:
    wavefronts = data.get('wavefronts');    print('Wavefronts loaded')
else:
    wavefronts = []
    wavefront  = get_wavefront(tf=2.7);
    wavefronts.append((wavefront[0].tolist(), wavefront[1].tolist(), wavefront[2]))
    data.update({'wavefronts':wavefronts}); print('wavefronts saved')

# Function to plot wavefronts
wavefronts_plot = plottings.Wavefronts(wavefronts, epsilon)
[ ]:
# Plots on the 2D chart
fig = geodesics_plot(nb_geodesics=5, tf=2.7) # geodesics
conjugate_plot(fig=fig)                       # conjugate locus
wavefronts_plot(fig=fig)                     # wavefronts
[ ]:
# Plots on the Ellipsoid
fig = geodesics_plot(coords=plottings.Coords.ELLIPSOID, nb_geodesics=7, tf=2.7) # geodesics
conjugate_plot(coords=plottings.Coords.ELLIPSOID, fig=fig)                       # conjugate locus
wavefronts_plot(coords=plottings.Coords.ELLIPSOID, fig=fig)                     # wavefronts

Figures

Geodesics departing from \(q_0=(0,0)\) for different \(\alpha_0 \in [0, 2\pi)\) with \(t_f=2.7\). The conjugate locus is in red. The wavefront at time \(t_f = 2.7\) is represented in green. The self-intersections of the wavefront are isochronous separating points.

[ ]:
# Function to computes self-intersections of a curve in a 2-dimensional space
def get_self_intersections(curve):
    n = curve[:,0].size
    intersections = []
    for i in range(n-3):
        A = curve[i,:]
        B = curve[i+1,:]
        for j in range(i+2,n-1):
            C = curve[j,:]
            D = curve[j+1,:]
            # Matrice M : M z = b
            m11 = B[0] - A[0]
            m12 = C[0] - D[0]
            m21 = B[1] - A[1]
            m22 = C[1] - D[1]
            det = m11*m22-m12*m21
            if(np.abs(det)>1e-8):
                b1 = C[0] - A[0]
                b2 = C[1] - A[1]
                la = (m22*b1-m12*b2)/det
                mu = (m11*b2-m21*b1)/det
                if(la>=0. and la<=1. and mu>=0. and mu<=1.):
                    xx = {'i': i, 'j': j, 'x': np.array(A + la * (B-A)), 'la': la, 'mu': mu}
                    intersections.append(xx)
    return intersections
[ ]:
# We compute one self-intersection of the wavefront
wa        = wavefronts[0]
wavefront = np.array(wa[0])
alphas    = np.array(wa[1])
tf        = wa[2]
xxs       = get_self_intersections(wavefront)
xx        = xxs[0]
x         = xx.get('x')
i         = xx.get('i')
j         = xx.get('j')
la        = xx.get('la')
mu        = xx.get('mu')
alpha1    = alphas[i]+la*(alphas[i+1]-alphas[i])
alpha2    = alphas[j]+mu*(alphas[j+1]-alphas[j])

print('Self-intersection of the wavefront for tf =', tf, 'at q = (', x[0], ',', x[1], ')\n')
print('alpha1 =', alpha1)
print('alpha2 = ', alpha2)

Back to top

Cut locus

The cut locus \(\mathrm{Cut}(q_0)\) is the union of the cut points of the geodesics departing from \(q_0\). A cut point is either a conjugate point or an isochronous separating point [do Carmo 1988, page 267 Prop. 2.2], that is a point where two minimizing geodesics intersect with the same time (or length). A separating point (or splitting point) corresponds to a self-intersection of a wavefront. To compute the cut locus we need at this stage, to compute the separating locus (or splitting locus) and then compare for each geodesic its first conjugate time with its separating time. We introduce the following mapping:

\[F_\mathrm{split}(t, \alpha_1, q, \alpha_2) \overset{\mathrm{def}}{=} \left( q - \exp_{q_0}(t, \alpha_1),~ q - \exp_{q_0}(t, \alpha_2)\right).\]

The splitting locus is then given by solving \(F_\mathrm{split} = 0\) since we have

\[\mathrm{Split}(q_0) \subset \{ q \in M ~|~ \exists\, (t, \alpha_1, \alpha_2) ~\text{s.t.}~ \alpha_1\neq\alpha_2 ~\text{and}~ F_\mathrm{split}(t, \alpha_1, q, \alpha_2) = 0\}.\]

The splitting locus is also computed by homotopy. At the end, the cut locus of the oblate ellipsoid of revolution is the union of the splitting locus with the two conjugate points at the extremity of \(\mathrm{Split}(q_0)\) [Itoh-Kiohara 2004], that is

\[\mathrm{Cut}(q_0) = \mathrm{Split}(q_0) \cup \{ \exp_{q_0}(t^*, 0), \exp_{q_0}(t^*, \pi) \},\]

where \(t^*\) is by symmetry, the first positive time solution of

\[F_{\mathrm{conj}}(t^*, 0) = F_{\mathrm{conj}}(t^*, \pi) = 0.\]

Note that \(t^*\) is the so-called injectivity radius.

2105833c193645128089f58f8fb6daac

46fa5de361ea4419a081ee2a78a516da

Separating point

Conjugate point

[ ]:
# Equations to compute Split(q0)
def split_eq(y, alpha2):
    # y = (t, alpha1, q)
    t     = y[0]
    a1    = y[1]
    q     = y[2:4]
    a2    = float(alpha2)
    q1, _ = extremal(t0, q0, covector(q0, a1), t)
    q2, _ = extremal(t0, q0, covector(q0, a2), t)
    eq    = np.zeros(4)
    eq[0:2] = q-q1
    eq[2:4] = q-q2
    return eq

# Derivative
def dsplit_eq(y, dy, alpha2, dalpha2):
    t, dt   = y[0], dy[0]
    a1, da1 = y[1], dy[1]
    q, dq   = y[2:4], dy[2:4]
    a2, da2 = float(alpha2), float(dalpha2)
    (q1, dq1), _ = extremal(t0, q0, covector(q0, (a1, da1)), (t, dt))
    (q2, dq2), _ = extremal(t0, q0, covector(q0, (a2, da2)), (t, dt))
    eq, deq      = np.zeros(4), np.zeros(4)
    eq[0:2], deq[0:2] = q-q1, dq-dq1
    eq[2:4], deq[2:4] = q-q2, dq-dq2
    return eq, deq

split_eq = nt.tools.tensorize(dsplit_eq, tvars=(1, 2), full=True)(split_eq)
[ ]:
# Function to compute the splitting locus
def get_splitting_locus(q, a1, t, a2):

    # Options
    opt  = nt.path.Options(MaxStepSizeHomPar=0.05, Display='off');

    # Homotopic parameter range
    epsi    = 1e-3
    alpha0  = 0.+epsi
    alphaf  = np.pi/2.0-epsi

    # Initial solution
    y0 = np.array([t, a1, q[0], q[1]])
    b0 = a2

    # callback
    def progress_bis(infos):
        current   = b0-float(infos.pars)
        total     = alphaf-alpha0+b0-alpha0
        barLength = 50
        percent   = float(current * 100.0 / total)
        arrow     = '-' * int(percent/100.0 * barLength - 1) + '>'
        spaces    = ' ' * (barLength - len(arrow))

        print('    Progress: [%s%s] %1.2f %%' % (arrow, spaces, round(percent, 2)), end='\r')

    # First homotopy
    print('\n > Get splitting locus\n')
    sol  = nt.path.solve(split_eq, y0, b0, alpha0, options=opt, df=split_eq, callback=progress_bis)
    ysol = sol.xf

    # callback
    def progress(infos):
        current   = b0-alpha0+float(infos.pars)-alpha0
        total     = alphaf-alpha0+b0-alpha0
        barLength = 50
        percent   = float(current * 100.0 / total)
        arrow     = '-' * int(percent/100.0 * barLength - 1) + '>'
        spaces    = ' ' * (barLength - len(arrow))

        print('    Progress: [%s%s] %1.2f %%' % (arrow, spaces, round(percent, 2)), end='\r')

    # Splitting locus computation
    sol = nt.path.solve(split_eq, ysol, alpha0, alphaf, options=opt, df=split_eq, callback=progress)
    print('\n')

    splitting_locus = sol.xout

    return splitting_locus
[ ]:
# Get splitting locus
if data.contains('splitting_locus') and not restart:
    splitting_locus = np.array(data.get('splitting_locus'));   print('Splitting locus loaded')
else:
    splitting_locus = get_splitting_locus(x, alpha1, tf, alpha2)
    data.update({'splitting_locus':splitting_locus.tolist()}); print('Splitting locus saved')

# Function to plot the splitting locus
splitting_plot = plottings.Splitting_Locus(splitting_locus, epsilon)
[ ]:
# Plots on the 2D chart
fig = geodesics_plot(nb_geodesics=9, cut={'split_locus':splitting_locus, 'split_eq':split_eq}) # geodesics
conjugate_plot(fig=fig)  # conjugate locus
splitting_plot(fig=fig) # splitting locus
[ ]:
# Plots on the Ellipsoid
fig = geodesics_plot(coords=plottings.Coords.ELLIPSOID, nb_geodesics=9, cut={'split_locus':splitting_locus, 'split_eq':split_eq}) # geodesics
conjugate_plot(coords=plottings.Coords.ELLIPSOID, fig=fig)  # conjugate locus
splitting_plot(coords=plottings.Coords.ELLIPSOID, fig=fig) # splitting locus

Figures

Geodesics departing from \(q_0=(0,0)\) for different \(\alpha_0 \in [0, 2\pi)\) until cut point. The conjugate locus is in red. The cut locus is represented in black. It is the segment of the equator contained in the conjugate locus. The extremities of the cut locus are cusps of the conjugate locus while interior points are isochronous separating points.

Back to top

References

[Allgower-Georg 2003] E. Allgower & K. Georg, Introduction to numerical continuation methods, vol. 45 of Classics in Applied Mathematics, Soc. for Industrial and Applied Math., Philadelphia, PA, USA, (2003), 388 pages.

[Berger 2003] M. Berger, A panoramic view of Riemannian geometry, Springer, 2003.

[Bonnard et al. 2014] B. Bonnard, O. Cots & L. Jassionnesse, Geometric and numerical techniques to compute conjugate and cut loci on Riemannian surfaces, in INDAM Series vol. 5, Geometric Control and sub-Riemannian Geometry, (2014). Proceedings of the conference “Geometric Control and sub-Riemannian geometry”, Cortona, Italy, May 2012.

[Caillau et al. 2011] J.-B. Caillau, O. Cots & J. Gergaud, Differential continuation for regular optimal control problems, Optim. Methods Softw., 27 (2011), no. 2, pp. 177–196.

[do Carmo 1988] M. P. Do Carmo, Riemannian geometry, Birkhäuser, Mathematics: Theory & applications, second edn 1988.

[Facca et al. 2021] E. Facca, L. Berti, F. Fassó and M. Putti. Computing the Cut Locus of a Riemannian Manifold via Optimal Transport. 2021. ⟨hal-03467888⟩

[Hairer et al. 1993] E. Hairer, S. P. Nørsett & G. Wanner, Solving Ordinary Differential Equations I, Nonstiff Problems, vol 8 of Springer Serie in Computational Mathematics, Springer-Verlag, second edn (1993).

[Hairer-Wanner 1996] E. Hairer & G. Wanner, Solving Ordinary Differential Equations II, Stiff and Differential-Algebraic Problems, vol 14 of Springer Serie in Computational Mathematics, Springer-Verlag, second edn (1996).

[Hascoët-Pascual 2012] L. Hascoët & V. Pascual, The Tapenade Automatic Differentiation tool: principles, model, and specification, Rapport de recherche RR-7957, INRIA (2012).

[Itoh-Kiohara 2004] J. Itoh and K. Kiyohara, The cut loci and the conjugate loci on ellipsoids, Manuscripta math., 114 (2004), no. 2, pp. 247-264.

[Itoh-Sinclair 2004] J. Itoh. and R. Sinclair. Thaw: A Tool for Approximating Cut Loci on a Triangulation of a Surface. Experiment. Math. 13 (2004), no. 3, 309-325.

[Moré et al. 1980] J. J. Moré, B. S. Garbow & K. E. Hillstrom, User Guide for MINPACK-1, ANL-80-74, Argonne National Laboratory, (1980).

[Sinclair-Tanaka 2002] R. Sinclair and M. Tanaka. Loki: Software for Computing Cut Loci. Exper. Math. 11 (2002), no. 1, 1–25.

thumbnail

Back to top

Annexes

wrappers.py

[ ]:
!pygmentize wrappers.py

plottings.py

[ ]:
!pygmentize plottings.py

bdd.py

[ ]:
!pygmentize bdd.py