{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Manifold tutorial\n", "\n", "This notebook provides a short introduction to differentiable manifolds in SageMath. The tools described below have been implemented through the\n", "[SageManifolds](http://sagemanifolds.obspm.fr) project.\n", "\n", "Click [here](https://raw.githubusercontent.com/sagemanifolds/SageManifolds/master/Notebooks/SM_tutorial.ipynb) to download the notebook file (ipynb format). To run it, you must start SageMath with the Jupyter notebook, via the command sage -n jupyter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following assumes that you are using version 7.5 (or higher) of SageMath:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'SageMath version 9.1, Release Date: 2020-05-20'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "version()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we set up the notebook to display mathematical objects using LaTeX rendering:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "%display latex" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining a manifold\n", "\n", "As an example let us define a differentiable manifold of dimension 3 over $\\mathbb{R}$:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "M = Manifold(3, 'M', latex_name=r'\\mathcal{M}', start_index=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- The first argument, 3, is the manifold dimension. In SageManifolds, it can be any\n", " positive integer.\n", "- The second argument, 'M', is a string defining the manifold's name; it may be \n", " different from the symbol set on the left-hand side of the = sign (here M): the latter\n", " stands for a mere Python variable, which refers to the manifold object in the computer \n", " memory, while the string 'M' is the mathematical symbol chosen for the manifold.\n", "- The optional argument latex_name=r'\\mathcal{M}' sets the LaTeX\n", " symbol to display the manifold. Note the letter 'r' in front on the first quote: \n", " it indicates that the string is a *raw* one, so that the backslash character \n", " in \\mathcal is considered as an ordinary character (otherwise, the backslash is \n", " used to escape some special characters). If the argument latex_name is not \n", " provided by the user, it is set to the string used as the second argument (here 'M')\n", "- The optional argument start_index=1 defines the range of indices to be used for \n", " tensor components on the manifold: setting it to 1 means that indices will range \n", " in $\\{1,2,3\\}$. The default value is start_index=0.\n", "\n", "Note that the default base field is $\\mathbb{R}$. If we would have used the optional\n", "argument field='complex', we would have defined a manifold over $\\mathbb{C}$. See the\n", "[list of all options](http://doc.sagemath.org/html/en/reference/manifolds/sage/manifolds/manifold.html#sage.manifolds.manifold.Manifold) for more details. \n", "\n", "If we ask for M, it is displayed via its LaTeX symbol:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "3-dimensional differentiable manifold M" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we use the function print() instead, we get a short description of the object:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3-dimensional differentiable manifold M\n" ] } ], "source": [ "print(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Via the function type(), we get the type of the Python object corresponding to M (here the Python class DifferentiableManifold_with_category:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also ask for the category of M and see that it is the category of smooth manifolds over $\\mathbb{R}$:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Category of smooth manifolds over Real Field with 53 bits of precision" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "category(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The indices on the manifold are generated by the method irange(), to be used in loops:

" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n" ] } ], "source": [ "for i in M.irange():\n", " print(i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

If the parameter start_index had not been specified, the default range of the indices would have been $\\{0,1,2\\}$ instead:

" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n" ] } ], "source": [ "M0 = Manifold(3, 'M', latex_name=r'\\mathcal{M}')\n", "for i in M0.irange():\n", " print(i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

## Defining a chart on the manifold

\n", "

Let us assume that the manifold $\\mathcal{M}$ can be covered by a single chart (other cases are discussed below); the chart is declared as follows:

" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "X. = M.chart()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The writing . in the left-hand side means that the Python variables x, y and z are set to the three coordinates of the chart. This allows one to refer subsequently to the coordinates by their names.\n", "\n", "In this example, the function chart() has no arguments, which implies that the coordinate symbols will be x, y and z (i.e. exactly the characters set in the <...> operator) and that each coordinate range is $(-\\infty,+\\infty)$. For other cases, an argument must be passed to chart()  to specify the coordinate symbols and range, as well as the LaTeX symbol of a coordinate if the latter is different from the coordinate name (an example will be provided below)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The chart is displayed as a pair formed by the open set covered by it (here the whole manifold) and the coordinates:

" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Chart (M, (x, y, z))\n" ] } ], "source": [ "print(X)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Chart (M, (x, y, z))" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The coordinates can be accessed individually, by means of their indices, following the convention defined by start_index=1 in the manifold's definition:

" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "x" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[1]" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "y" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[2]" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "z" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The full set of coordinates is obtained by means of the operator [:]:

" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(x, y, z)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thanks to the operator  used in the chart declaration, each coordinate can be accessed directly via its name:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "True" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z is X[3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coordinates are SageMath symbolic expressions:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

### Functions of the chart coordinates

\n", "

Real-valued functions of the chart coordinates (mathematically speaking, functions defined on the chart codomain) are generated via the method function() acting on the chart:

" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "z^3 + y^2 + x" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = X.function(x+y^2+z^3)\n", "f" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(x, y, z) |--> z^3 + y^2 + x" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.display()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "32" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(1,2,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

They belong to SageManifolds class ChartFunction:

" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

and differ from SageMath standard symbolic functions by automatic simplifications in all operations. For instance, adding the two symbolic functions

" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "f0(x,y,z) = cos(x)^2; g0(x,y,z) = sin(x)^2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

results in

" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(x, y, z) |--> cos(x)^2 + sin(x)^2" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f0 + g0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

while the sum of the corresponding functions in the class ChartFunction is automatically simplified:

" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "1" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1 = X.function(cos(x)^2); g1 = X.function(sin(x)^2)\n", "f1 + g1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

To get the same output with symbolic functions, one has to invoke the method simplify_trig():

" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(x, y, z) |--> 1" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(f0 + g0).simplify_trig()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Another difference regards the display; if we ask for the symbolic function f0, we get:

" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(x, y, z) |--> cos(x)^2" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

while if we ask for the chart function f1, we get only the coordinate expression:

" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "cos(x)^2" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

To get an output similar to that of f0, one should call the method display():

" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(x, y, z) |--> cos(x)^2" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1.display()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the method expr() returns the underlying symbolic expression:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "cos(x)^2" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1.expr()" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(f1.expr())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Introducing a second chart on the manifold\n", "\n", "Let us first consider an open subset of $\\mathcal{M}$, for instance the complement $U$ of the region defined by $\\{y=0, x\\geq 0\\}$ (note that (y!=0, x<0) stands for $y\\not=0$ OR $x<0$; the condition $y\\not=0$ AND $x<0$ would have been written [y!=0, x<0] instead):" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "U = M.open_subset('U', coord_def={X: (y!=0, x<0)})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us call X_U the restriction of the chart X to the open subset $U$:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Chart (U, (x, y, z))" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_U = X.restrict(U)\n", "X_U" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

We introduce another chart on $U$, with spherical-type coordinates $(r,\\theta,\\phi)$:

" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Chart (U, (r, th, ph))" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y. = U.chart(r'r:(0,+oo) th:(0,pi):\\theta ph:(0,2*pi):\\phi')\n", "Y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The function chart() has now some argument; it is a string, which contains specific LaTeX symbols, hence the prefix 'r' to it (for raw string). It also contains the coordinate ranges, since they are different from the default value, which is $(-\\infty, +\\infty)$. For a given coordinate, the various fields are separated by the character ':' and a space character separates the coordinates. Note that for the coordinate $r$, there are only two fields, since the LaTeX symbol has not to be specified. The LaTeX symbols are used for the outputs:

" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(th, ph)" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "th, ph" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(th, ph)" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y[2], Y[3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The declared coordinate ranges are now known to Sage, as we may check by means of the command assumptions():

" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "[x is real,\n", " y is real,\n", " z is real,\n", " r is real,\n", " r > 0,\n", " th is real,\n", " th > 0,\n", " th < pi,\n", " ph is real,\n", " ph > 0,\n", " ph < 2*pi]" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assumptions()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

They are used in simplifications:

" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "r" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "simplify(abs(r))" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "abs(x)" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "simplify(abs(x)) # no simplification occurs since x can take any value in R" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

After having been declared, the chart Y can be fully specified by its relation to the chart X_U, via a transition map:

" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Change of coordinates from Chart (U, (r, th, ph)) to Chart (U, (x, y, z))" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "transit_Y_to_X = Y.transition_map(X_U, [r*sin(th)*cos(ph), r*sin(th)*sin(ph), r*cos(th)])\n", "transit_Y_to_X" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "x = r*cos(ph)*sin(th)\n", "y = r*sin(ph)*sin(th)\n", "z = r*cos(th)" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "transit_Y_to_X.display()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The inverse of the transition map can be specified by means of the method set_inverse():

" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Check of the inverse coordinate transformation:\n", " r == r *passed*\n", " th == arctan2(r*sin(th), r*cos(th)) **failed**\n", " ph == arctan2(r*sin(ph)*sin(th), r*cos(ph)*sin(th)) **failed**\n", " x == x *passed*\n", " y == y *passed*\n", " z == z *passed*\n", "NB: a failed report can reflect a mere lack of simplification.\n" ] } ], "source": [ "transit_Y_to_X.set_inverse(sqrt(x^2+y^2+z^2), atan2(sqrt(x^2+y^2),z), atan2(y, x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A check of the provided inverse is performed by composing it with the original transition map, on the left and on the right respectively. As indicated, the reported failure for th and ph is actually due to a lack of simplification of expressions involving arctan2. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have then" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "r = sqrt(x^2 + y^2 + z^2)\n", "th = arctan2(sqrt(x^2 + y^2), z)\n", "ph = arctan2(y, x)" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "transit_Y_to_X.inverse().display()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

At this stage, the manifold's atlas (the \"user atlas\", not the maximal atlas!) contains three charts:

" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "[Chart (M, (x, y, z)), Chart (U, (x, y, z)), Chart (U, (r, th, ph))]" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.atlas()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The first chart defined on the manifold is considered as the manifold's default chart (it can be changed by the method set_default_chart()):

" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Chart (M, (x, y, z))" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.default_chart()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Each open subset has its own atlas (since an open subset of a manifold is a manifold by itself):

" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "[Chart (U, (x, y, z)), Chart (U, (r, th, ph))]" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "U.atlas()" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Chart (U, (x, y, z))" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "U.default_chart()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can draw the chart $Y$ in terms of the chart $X$ via the command Y.plot(X), which shows the lines of constant coordinates from the $Y$ chart in a \"Cartesian frame\" based on the $X$ coordinates:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n" ], "text/plain": [ "Graphics3d Object" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y.plot(X)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The command plot() allows for many options, to control the number of coordinate lines to be drawn, their style and color, as well as the coordinate ranges (cf. the [list of all options](http://doc.sagemath.org/html/en/reference/manifolds/sage/manifolds/chart.html#sage.manifolds.chart.RealChart.plot)):" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n" ], "text/plain": [ "Graphics3d Object" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y.plot(X, ranges={r:(1,2), th:(0,pi/2)}, number_values=4, \n", " color={r:'blue', th:'green', ph:'red'}, aspect_ratio=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Conversly, the chart $X|_{U}$ can be plotted in terms of the chart $Y$ (this is not possible for the whole chart $X$ since its domain is larger than that of chart $Y$):

" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n" ], "text/plain": [ "Graphics3d Object" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "graph = X_U.plot(Y)\n", "show(graph, axes_labels=['r','theta','phi'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

## Points on the manifold

\n", "

A point on $\\mathcal{M}$ is defined by its coordinates in a given chart:

" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point p on the 3-dimensional differentiable manifold M\n" ] }, { "data": { "text/html": [ "" ], "text/plain": [ "Point p on the 3-dimensional differentiable manifold M" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = M.point((1,2,-1), chart=X, name='p')\n", "print(p)\n", "p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Since $X=(\\mathcal{M}, (x,y,z))$ is the manifold's default chart, its name can be omitted:

" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point p on the 3-dimensional differentiable manifold M\n" ] }, { "data": { "text/html": [ "" ], "text/plain": [ "Point p on the 3-dimensional differentiable manifold M" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = M.point((1,2,-1), name='p')\n", "print(p)\n", "p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Of course, $p$ belongs to $\\mathcal{M}$:

" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "True" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p in M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

It is also in $U$:

" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "True" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p in U" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Indeed the coordinates of $p$ have $y\\not=0$:

" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(1, 2, -1)" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p.coord(X)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Note in passing that since $X$ is the default chart on $\\mathcal{M}$, its name can be omitted in the arguments of coord():

" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(1, 2, -1)" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p.coord()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The coordinates of $p$ can also be obtained by letting the chart acting of the point (from the very definition of a chart!):

" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(1, 2, -1)" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Let $q$ be a point with $y = 0$ and $x \\geq 0$:

" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [], "source": [ "q = M.point((1,0,2), name='q')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

This time, the point does not belong to $U$:

" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "False" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "q in U" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Accordingly, we cannot ask for the coordinates of $q$ in the chart $Y=(U, (r,\\theta,\\phi))$:

" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error: the point does not belong to the domain of Chart (U, (r, th, ph))\n" ] } ], "source": [ "try:\n", " q.coord(Y)\n", "except ValueError as exc:\n", " print(\"Error: \" + str(exc))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

but we can for point $p$:

" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(sqrt(3)*sqrt(2), pi - arctan(sqrt(5)), arctan(2))" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p.coord(Y)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "(sqrt(3)*sqrt(2), pi - arctan(sqrt(5)), arctan(2))" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Points can be compared:

" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "False" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "q == p" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "True" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p1 = U.point((sqrt(3)*sqrt(2), pi-atan(sqrt(5)), atan(2)), chart=Y)\n", "p1 == p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

In SageMath's terminology, points are elements, whose parents are the manifold on which they have been defined:

" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "3-dimensional differentiable manifold M" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p.parent()" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "3-dimensional differentiable manifold M" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "q.parent()" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "Open subset U of the 3-dimensional differentiable manifold M" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p1.parent()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

## Scalar fields

\n", "

A scalar field is a differentiable mapping $U \\longrightarrow \\mathbb{R}$, where $U$ is an open subset of $\\mathcal{M}$.

\n", "

The scalar field is defined by its expressions in terms of charts covering its domain (in general more than one chart is necessary to cover all the domain):

" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Scalar field f on the Open subset U of the 3-dimensional differentiable manifold M\n" ] } ], "source": [ "f = U.scalar_field({X_U: x+y^2+z^3}, name='f')\n", "print(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

The coordinate expressions of the scalar field are passed as a Python dictionary, with the charts as keys, hence the writing {X_U: x+y^2+z^3}.

\n", "

Since in the present case, there is only one chart in the dictionary, an alternative writing is

" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Scalar field f on the Open subset U of the 3-dimensional differentiable manifold M\n" ] } ], "source": [ "f = U.scalar_field(x+y^2+z^3, chart=X_U, name='f')\n", "print(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Since X_U is the domain's default chart, it can be omitted in the above declaration:

" ] }, { "cell_type": "code", "execution_co