**diff options**

author | Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> | 2015-12-18 00:09:35 +0100 |
---|---|---|

committer | Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> | 2015-12-18 00:09:35 +0100 |

commit | 2df7af72f8d583b427bdc11eff07c02d10d59a7c (patch) | |

tree | 03a74124d05bf30bdd8477d279754e71b80cb6f2 | |

parent | Morphisms on the refactored topoological manifolds (diff) | |

parent | Differentiable manifolds: basics with the change in symbolic expression logic... (diff) |

Refactorization of differentiable manifolds, with the mixin class DifferentiableMixin

-rw-r--r-- | src/doc/en/reference/manifolds/diff_manifold.rst | 15 | ||||

-rw-r--r-- | src/doc/en/reference/manifolds/diff_map.rst | 9 | ||||

-rw-r--r-- | src/doc/en/reference/manifolds/diff_scalarfield.rst | 9 | ||||

-rw-r--r-- | src/doc/en/reference/manifolds/index.rst | 2 | ||||

-rw-r--r-- | src/sage/manifolds/all.py | 1 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/__init__.py | 1 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/chart.py | 701 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/diff_map.py | 484 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/manifold.py | 953 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/manifold_homset.py | 199 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/scalarfield.py | 676 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/scalarfield_algebra.py | 440 | ||||

-rw-r--r-- | src/sage/manifolds/differentiable/subset.py | 127 | ||||

-rw-r--r-- | src/sage/manifolds/manifold.py | 120 | ||||

-rw-r--r-- | src/sage/manifolds/structure.py | 56 | ||||

-rw-r--r-- | src/sage/manifolds/subset.py | 12 |

16 files changed, 3778 insertions, 27 deletions

diff --git a/src/doc/en/reference/manifolds/diff_manifold.rst b/src/doc/en/reference/manifolds/diff_manifold.rst new file mode 100644 index 00000000..1eb8dd1 --- /dev/null +++ b/src/doc/en/reference/manifolds/diff_manifold.rst @@ -0,0 +1,15 @@ +Differentiable manifolds +======================== + +.. toctree:: + :maxdepth: 2 + + sage/manifolds/differentiable/manifold + + sage/manifolds/differentiable/subset + + sage/manifolds/differentiable/chart + + diff_scalarfield + + diff_map diff --git a/src/doc/en/reference/manifolds/diff_map.rst b/src/doc/en/reference/manifolds/diff_map.rst new file mode 100644 index 00000000..51162b4 --- /dev/null +++ b/src/doc/en/reference/manifolds/diff_map.rst @@ -0,0 +1,9 @@ +Differentiable maps +=================== + +.. toctree:: + :maxdepth: 2 + + sage/manifolds/differentiable/manifold_homset + + sage/manifolds/differentiable/diff_map diff --git a/src/doc/en/reference/manifolds/diff_scalarfield.rst b/src/doc/en/reference/manifolds/diff_scalarfield.rst new file mode 100644 index 00000000..2991cd9 --- /dev/null +++ b/src/doc/en/reference/manifolds/diff_scalarfield.rst @@ -0,0 +1,9 @@ +Scalar fields +============= + +.. toctree:: + :maxdepth: 2 + + sage/manifolds/differentiable/scalarfield_algebra + + sage/manifolds/differentiable/scalarfield diff --git a/src/doc/en/reference/manifolds/index.rst b/src/doc/en/reference/manifolds/index.rst index 13995d6..dd546af 100644 --- a/src/doc/en/reference/manifolds/index.rst +++ b/src/doc/en/reference/manifolds/index.rst @@ -15,6 +15,8 @@ More documentation (in particular example worksheets) can be found manifold + diff_manifold + sage/manifolds/utilities .. include:: ../footer.txt diff --git a/src/sage/manifolds/all.py b/src/sage/manifolds/all.py index 01834c1..0a3bbc0 100644 --- a/src/sage/manifolds/all.py +++ b/src/sage/manifolds/all.py @@ -2,3 +2,4 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.manifolds.manifold', 'Manifold') lazy_import('sage.manifolds.utilities', 'nice_derivatives') lazy_import('sage.manifolds.utilities', 'omit_function_args') +lazy_import('sage.manifolds.utilities', 'set_axes_labels') diff --git a/src/sage/manifolds/differentiable/__init__.py b/src/sage/manifolds/differentiable/__init__.py new file mode 100644 index 00000000..932b798 --- /dev/null +++ b/src/sage/manifolds/differentiable/__init__.py @@ -0,0 +1 @@ +# Empty file diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py new file mode 100644 index 00000000..2e37a74 --- /dev/null +++ b/src/sage/manifolds/differentiable/chart.py @@ -0,0 +1,701 @@ +r""" +Coordinate charts on differentiable manifolds + +The class :class:`DiffChart` implements coordinate charts on a differentiable +manifold over a topological field `K` (in most applications, `K = \RR` or +`K = \CC`). + +The subclass :class:`RealDiffChart` is devoted +to the case `K=\RR`, for which the concept of coordinate range is meaningful. +Moreover, :class:`RealDiffChart` is endowed with some plotting +capabilities (cf. method :meth:`~sage.manifolds.chart.RealChart.plot`). + +Transition maps between charts are implemented via the class +:class:`DiffCoordChange`. + +AUTHORS: + +- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version + +REFERENCES: + +.. [1] Chap. 1 of J.M. Lee : *Introduction to Smooth Manifolds*, 2nd ed., + Springer (New York) (2013) + +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> +# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl> +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.manifolds.chart import Chart, RealChart, CoordChange + +class DiffChart(Chart): + r""" + Chart on a differentiable manifold. + + Given a differentiable manifold `M` of dimension `n` over a topological + field `K`, a *chart* is a member `(U,\varphi)` of the manifold's + differentiable atlas; `U` is then an open subset of `M` and + `\varphi: U \rightarrow V \subset K^n` is a homeomorphism from + `U` to an open subset `V` of `K^n`. + + The components `(x^1,\ldots,x^n)` of `\varphi`, defined by + `\varphi(p) = (x^1(p),\ldots,x^n(p))\in K^n` for any point `p\in U`, are + called the *coordinates* of the chart `(U,\varphi)`. + + INPUT: + + - ``domain`` -- open subset `U` on which the chart is defined + - ``coordinates`` -- (default: '' (empty string)) single string defining + the coordinate symbols, with ' ' (whitespace) as a separator; each item + has at most two fields, separated by ':': + + 1. The coordinate symbol (a letter or a few letters) + 2. (optional) The LaTeX spelling of the coordinate; if not provided the + coordinate symbol given in the first field will be used. + + If it contains any LaTeX expression, the string ``coordinates`` must be + declared with the prefix 'r' (for "raw") to allow for a proper treatment + of LaTeX's backslash character (see examples below). + If no LaTeX spelling is to be set for any coordinate, the argument + ``coordinates`` can be omitted when the shortcut operator ``<,>`` is + used via Sage preparser (see examples below) + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used). + + EXAMPLES: + + A chart on a complex 2-dimensional differentiable manifold:: + + sage: M = Manifold(2, 'M', field='complex') + sage: X = M.chart('x y'); X + Chart (M, (x, y)) + sage: latex(X) + \left(M,(x, y)\right) + sage: type(X) + <class 'sage.manifolds.differentiable.chart.DiffChart'> + + To manipulate the coordinates `(x,y)` as global variables, one has to set:: + + sage: x,y = X[:] + + However, a shortcut is to use the declarator ``<x,y>`` in the left-hand + side of the chart declaration (there is then no need to pass the string + ``'x y'`` to ``chart()``):: + + sage: M = Manifold(2, 'M', field='complex') + sage: X.<x,y> = M.chart(); X + Chart (M, (x, y)) + + The coordinates are then immediately accessible:: + + sage: y + y + sage: x is X[0] and y is X[1] + True + + The trick is performed by Sage preparser:: + + sage: preparse("X.<x,y> = M.chart()") + "X = M.chart(names=('x', 'y',)); (x, y,) = X._first_ngens(2)" + + Note that ``x`` and ``y`` declared in ``<x,y>`` are mere Python variable + names and do not have to coincide with the coordinate symbols; + for instance, one may write:: + + sage: M = Manifold(2, 'M', field='complex') + sage: X.<x1,y1> = M.chart('x y'); X + Chart (M, (x, y)) + + Then ``y`` is not known as a global Python variable and the + coordinate `y` is accessible only through the global variable ``y1``:: + + sage: y1 + y + sage: latex(y1) + y + sage: y1 is X[1] + True + + However, having the name of the Python variable coincide with the + coordinate symbol is quite convenient; so it is recommended to declare:: + + sage: M = Manifold(2, 'M', field='complex') + sage: X.<x,y> = M.chart() + + In the above example, the chart X covers entirely the manifold M:: + + sage: X.domain() + 2-dimensional complex manifold M + + Of course, one may declare a chart only on an open subset of M:: + + sage: U = M.open_subset('U') + sage: Y.<z1, z2> = U.chart(r'z1:\zeta_1 z2:\zeta_2'); Y + Chart (U, (z1, z2)) + sage: Y.domain() + Open subset U of the 2-dimensional complex manifold M + + In the above declaration, we have also specified some LaTeX writing + of the coordinates different from the text one:: + + sage: latex(z1) + {\zeta_1} + + Note the prefix ``r`` in front of the string ``r'z1:\zeta_1 z2:\zeta_2'``; + it makes sure that the backslash character is treated as an ordinary + character, to be passed to the LaTeX interpreter. + + Coordinates are Sage symbolic variables (see + :mod:`sage.symbolic.expression`):: + + sage: type(z1) + <type 'sage.symbolic.expression.Expression'> + + In addition to the Python variable name provided in the operator ``<.,.>``, + the coordinates are accessible by their indices:: + + sage: Y[0], Y[1] + (z1, z2) + + The index range is that declared during the creation of the manifold. By + default, it starts at 0, but this can be changed via the parameter + ``start_index``:: + + sage: M1 = Manifold(2, 'M_1', field='complex', start_index=1) + sage: Z.<u,v> = M1.chart() + sage: Z[1], Z[2] + (u, v) + + The full set of coordinates is obtained by means of the operator + ``[:]``:: + + sage: Y[:] + (z1, z2) + + Each constructed chart is automatically added to the manifold's user + atlas:: + + sage: M.atlas() + [Chart (M, (x, y)), Chart (U, (z1, z2))] + + and to the atlas of the chart's domain:: + + sage: U.atlas() + [Chart (U, (z1, z2))] + + Manifold subsets have a *default chart*, which, unless changed via the + method + :meth:`~sage.manifolds.manifold.TopologicalManifold.set_default_chart`, + is the first defined chart on the subset (or on a open subset of it):: + + sage: M.default_chart() + Chart (M, (x, y)) + sage: U.default_chart() + Chart (U, (z1, z2)) + + The default charts are not privileged charts on the manifold, but rather + charts whose name can be skipped in the argument list of functions having + an optional ``chart=`` argument. + + The action of the chart map `\varphi` on a point is obtained by means of + the call operator, i.e. the operator ``()``:: + + sage: p = M.point((1+i, 2), chart=X); p + Point on the 2-dimensional complex manifold M + sage: X(p) + (I + 1, 2) + sage: X(p) == p.coord(X) + True + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.chart.RealDiffChart` for charts + on differentiable manifolds over `\RR`. + + """ + def __init__(self, domain, coordinates='', names=None): + r""" + Construct a chart. + + TESTS:: + + sage: M = Manifold(2, 'M', field='complex') + sage: X.<x,y> = M.chart() + sage: X + Chart (M, (x, y)) + sage: type(X) + <class 'sage.manifolds.differentiable.chart.DiffChart'> + sage: assumptions() # no assumptions on x,y set by X._init_coordinates + [] + sage: TestSuite(X).run() + + """ + Chart.__init__(self, domain, coordinates=coordinates, names=names) + + + def transition_map(self, other, transformations, intersection_name=None, + restrictions1=None, restrictions2=None): + r""" + Construct the transition map between the current chart, + `(U,\varphi)` say, and another one, `(V,\psi)` say. + + If `n` is the manifold's dimension, the *transition map* is the + map + + .. MATH:: + + \psi\circ\varphi^{-1}: \varphi(U\cap V) \subset K^n + \rightarrow \psi(U\cap V) \subset K^n, + + where `K` is the manifold's base field. In other words, the + transition map expresses the coordinates `(y^1,\ldots,y^n)` of + `(V,\psi)` in terms of the coordinates `(x^1,\ldots,x^n)` of + `(U,\varphi)` on the open subset where the two charts intersect, i.e. + on `U\cap V`. + + By definition, the transition map `\psi\circ\varphi^{-1}` must be + of classe `C^k`, where `k` is the degree of differentiability of the + manifold (cf. + :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_degree`). + + INPUT: + + - ``other`` -- the chart `(V,\psi)` + - ``transformations`` -- tuple (or list) `(Y_1,\ldots,Y_2)`, where + `Y_i` is the symbolic expression of the coordinate `y^i` in terms + of the coordinates `(x^1,\ldots,x^n)` + - ``intersection_name`` -- (default: ``None``) name to be given to the + subset `U\cap V` if the latter differs from `U` or `V` + - ``restrictions1`` -- (default: ``None``) list of conditions on the + coordinates of the current chart that define `U\cap V` if the + latter differs from `U`. ``restrictions1`` must be a list of + of symbolic equalities or inequalities involving the + coordinates, such as x>y or x^2+y^2 != 0. The items of the list + ``restrictions1`` are combined with the ``and`` operator; if some + restrictions are to be combined with the ``or`` operator instead, + they have to be passed as a tuple in some single item of the list + ``restrictions1``. For example, ``restrictions1`` = [x>y, + (x!=0, y!=0), z^2<x] means (x>y) and ((x!=0) or (y!=0)) and (z^2<x). + If the list ``restrictions1`` contains only one item, this item can + be passed as such, i.e. writing x>y instead of the single-element + list [x>y]. + - ``restrictions2`` -- (default: ``None``) list of conditions on the + coordinates of the chart `(V,\psi)` that define `U\cap V` if the + latter differs from `V` (see ``restrictions1`` for the syntax) + + OUTPUT: + + - The transition map `\psi\circ\varphi^{-1}` defined on `U\cap V`, as an + instance of :class:`DiffCoordChange`. + + EXAMPLES: + + Transition map between two stereographic charts on the circle `S^1`:: + + sage: M = Manifold(1, 'S^1') + sage: U = M.open_subset('U') # Complement of the North pole + sage: cU.<x> = U.chart() # Stereographic chart from the North pole + sage: V = M.open_subset('V') # Complement of the South pole + sage: cV.<y> = V.chart() # Stereographic chart from the South pole + sage: M.declare_union(U,V) # S^1 is the union of U and V + sage: trans = cU.transition_map(cV, 1/x, intersection_name='W', + ....: restrictions1= x!=0, restrictions2 = y!=0) + sage: trans + Change of coordinates from Chart (W, (x,)) to Chart (W, (y,)) + sage: trans.display() + y = 1/x + + The subset `W`, intersection of `U` and `V`, has been created by + ``transition_map()``:: + + sage: M.list_of_subsets() + [1-dimensional differentiable manifold S^1, + Open subset U of the 1-dimensional differentiable manifold S^1, + Open subset V of the 1-dimensional differentiable manifold S^1, + Open subset W of the 1-dimensional differentiable manifold S^1] + sage: W = M.list_of_subsets()[3] + sage: W is U.intersection(V) + True + sage: M.atlas() + [Chart (U, (x,)), Chart (V, (y,)), Chart (W, (x,)), Chart (W, (y,))] + + Transition map between the polar chart and the Cartesian one on + `\RR^2`:: + + sage: M = Manifold(2, 'R^2') + sage: c_cart.<x,y> = M.chart() + sage: U = M.open_subset('U') # the complement of the half line {y=0, x >= 0} + sage: c_spher.<r,phi> = U.chart(r'r:(0,+oo) phi:(0,2*pi):\phi') + sage: trans = c_spher.transition_map(c_cart, (r*cos(phi), r*sin(phi)), + ....: restrictions2=(y!=0, x<0)) + sage: trans + Change of coordinates from Chart (U, (r, phi)) to Chart (U, (x, y)) + sage: trans.display() + x = r*cos(phi) + y = r*sin(phi) + + In this case, no new subset has been created since `U\cap M = U`:: + + sage: M.list_of_subsets() + [2-dimensional differentiable manifold R^2, + Open subset U of the 2-dimensional differentiable manifold R^2] + + but a new chart has been created: `(U, (x, y))`:: + + sage: M.atlas() + [Chart (R^2, (x, y)), Chart (U, (r, phi)), Chart (U, (x, y))] + + """ + dom1 = self._domain + dom2 = other._domain + dom = dom1.intersection(dom2, name=intersection_name) + if dom is dom1: + chart1 = self + else: + chart1 = self.restrict(dom, restrictions1) + if dom is dom2: + chart2 = other + else: + chart2 = other.restrict(dom, restrictions2) + if not isinstance(transformations, (tuple, list)): + transformations = [transformations] + return DiffCoordChange(chart1, chart2, *transformations) + + +#***************************************************************************** + +class RealDiffChart(DiffChart, RealChart): + r""" + Chart on a differentiable manifold over `\RR`. + + Given a differentiable manifold `M` of dimension `n` over `\RR`, + a *chart* is a member `(U,\varphi)` of the manifold's + differentiable atlas; `U` is then an open subset of `M` and + `\varphi: U \rightarrow V \subset \RR^n` is a homeomorphism from + `U` to an open subset `V` of `\RR^n`. + + The components `(x^1,\ldots,x^n)` of `\varphi`, defined by + `\varphi(p) = (x^1(p),\ldots,x^n(p))\in \RR^n` for any point `p\in U`, are + called the *coordinates* of the chart `(U,\varphi)`. + + INPUT: + + - ``domain`` -- open subset `U` on which the chart is defined + - ``coordinates`` -- (default: '' (empty string)) single string defining + the coordinate symbols and ranges, with ' ' (whitespace) as a separator; + each item has at most three fields, separated by ':': + + 1. The coordinate symbol (a letter or a few letters) + 2. (optional) The interval `I` defining the coordinate range: if not + provided, the coordinate is assumed to span all `\RR`; otherwise + `I` must be provided in the form ``(a,b)`` (or equivalently + ``]a,b[``). The bounds ``a`` and ``b`` can be ``+/-Infinity``, + ``Inf``, ``infinity``, ``inf`` or ``oo``. + For *singular* coordinates, non-open intervals such as ``[a,b]`` and + ``(a,b]`` (or equivalently ``]a,b]``) are allowed. + Note that the interval declaration must not contain any whitespace. + 3. (optional) The LaTeX spelling of the coordinate; if not provided the + coordinate symbol given in the first field will be used. + + The order of the fields 2 and 3 does not matter and each of them can be + omitted. + If it contains any LaTeX expression, the string ``coordinates`` must be + declared with the prefix 'r' (for "raw") to allow for a proper treatment + of LaTeX backslash characters (see examples below). + If no interval range and no LaTeX spelling is to be set for any + coordinate, the argument ``coordinates`` can be omitted when the + shortcut operator ``<,>`` is used via Sage preparser (see examples below) + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used). + + EXAMPLES: + + Cartesian coordinates on `\RR^3`:: + + sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) + sage: c_cart = M.chart('x y z'); c_cart + Chart (R^3, (x, y, z)) + sage: type(c_cart) + <class 'sage.manifolds.differentiable.chart.RealDiffChart'> + + To have the coordinates accessible as global variables, one has to set:: + + sage: (x,y,z) = c_cart[:] + + However, a shortcut is to use the declarator ``<x,y,z>`` in the left-hand + side of the chart declaration (there is then no need to pass the string + ``'x y z'`` to ``chart()``):: + + sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) + sage: c_cart.<x,y,z> = M.chart(); c_cart + Chart (R^3, (x, y, z)) + + The coordinates are then immediately accessible:: + + sage: y + y + sage: y is c_cart[2] + True + + The trick is performed by Sage preparser:: + + sage: preparse("c_cart.<x,y,z> = M.chart()") + "c_cart = M.chart(names=('x', 'y', 'z',)); (x, y, z,) = c_cart._first_ngens(3)" + + Note that ``x, y, z`` declared in ``<x,y,z>`` are mere Python variable + names and do not have to coincide with the coordinate symbols; for instance, + one may write:: + + sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) + sage: c_cart.<x1,y1,z1> = M.chart('x y z'); c_cart + Chart (R^3, (x, y, z)) + + Then ``y`` is not known as a global variable and the coordinate `y` + is accessible only through the global variable ``y1``:: + + sage: y1 + y + sage: y1 is c_cart[2] + True + + However, having the name of the Python variable coincide with the + coordinate symbol is quite convenient; so it is recommended to declare:: + + sage: forget() # for doctests only + sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) + sage: c_cart.<x,y,z> = M.chart() + + Spherical coordinates on the subset `U` of `\RR^3` that is the + complement of the half-plane `\{y=0, x\geq 0\}`:: + + sage: U = M.open_subset('U') + sage: c_spher.<r,th,ph> = U.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: c_spher + Chart (U, (r, th, ph)) + + Note the prefix 'r' for the string defining the coordinates in the + arguments of ``chart``. + + Coordinates are Sage symbolic variables (see + :mod:`sage.symbolic.expression`):: + + sage: type(th) + <type 'sage.symbolic.expression.Expression'> + sage: latex(th) + {\theta} + sage: assumptions(th) + [th is real, th > 0, th < pi] + + Coordinate are also accessible by their indices:: + + sage: x1 = c_spher[1]; x2 = c_spher[2]; x3 = c_spher[3] + sage: print x1, x2, x3 + r th ph + sage: (x1, x2, x3) == (r, th, ph) + True + + The full set of coordinates is obtained by means of the operator [:]:: + + sage: c_cart[:] + (x, y, z) + sage: c_spher[:] + (r, th, ph) + + Let us check that the declared coordinate ranges have been taken into + account:: + + sage: c_cart.coord_range() + x: (-oo, +oo); y: (-oo, +oo); z: (-oo, +oo) + sage: c_spher.coord_range() + r: (0, +oo); th: (0, pi); ph: (0, 2*pi) + sage: bool(th>0 and th<pi) + True + sage: assumptions() # list all current symbolic assumptions + [x is real, y is real, z is real, r is real, r > 0, th is real, + th > 0, th < pi, ph is real, ph > 0, ph < 2*pi] + + The coordinate ranges are used for simplifications:: + + sage: simplify(abs(r)) # r has been declared to lie in the interval (0,+oo) + r + sage: simplify(abs(x)) # no positive range has been declared for x + abs(x) + + Each constructed chart is automatically added to the manifold's user atlas:: + + sage: M.atlas() + [Chart (R^3, (x, y, z)), Chart (U, (r, th, ph))] + + and to the atlas of its domain:: + + sage: U.atlas() + [Chart (U, (r, th, ph))] + + + Manifold subsets have a *default chart*, which, unless changed via the + method + :meth:`~sage.manifolds.manifold.TopologicalManifold.set_default_chart`, + is the first defined chart on the subset (or on a open subset of it):: + + sage: M.default_chart() + Chart (R^3, (x, y, z)) + sage: U.default_chart() + Chart (U, (r, th, ph)) + + The default charts are not privileged charts on the manifold, but rather + charts whose name can be skipped in the argument list of functions having + an optional ``chart=`` argument. + + The action of the chart map `\varphi` on a point is obtained by means of + the call operator, i.e. the operator ``()``:: + + sage: p = M.point((1,0,-2)); p + Point on the 3-dimensional differentiable manifold R^3 + sage: c_cart(p) + (1, 0, -2) + sage: c_cart(p) == p.coord(c_cart) + True + sage: q = M.point((2,pi/2,pi/3), chart=c_spher) # point defined by its spherical coordinates + sage: c_spher(q) + (2, 1/2*pi, 1/3*pi) + sage: c_spher(q) == q.coord(c_spher) + True + sage: a = U.point((1,pi/2,pi)) # the default coordinates on U are the spherical ones + sage: c_spher(a) + (1, 1/2*pi, pi) + sage: c_spher(a) == a.coord(c_spher) + True + + Cartesian coordinates on `U` as an example of chart construction with + coordinate restrictions: since `U` is the complement of the half-plane + `\{y=0, x\geq 0\}`, we must have `y\not=0` or `x<0` on U. Accordingly, + we set:: + + sage: c_cartU.<x,y,z> = U.chart() + sage: c_cartU.add_restrictions((y!=0, x<0)) # the tuple (y!=0, x<0) means y!=0 or x<0 + sage: # c_cartU.add_restrictions([y!=0, x<0]) would have meant y!=0 AND x<0 + sage: U.atlas() + [Chart (U, (r, th, ph)), Chart (U, (x, y, z))] + sage: M.atlas() + [Chart (R^3, (x, y, z)), Chart (U, (r, th, ph)), Chart (U, (x, y, z))] + sage: c_cartU.valid_coordinates(-1,0,2) + True + sage: c_cartU.valid_coordinates(1,0,2) + False + sage: c_cart.valid_coordinates(1,0,2) + True + + Chart grids can be drawn in 2D or 3D graphics thanks to the method + :meth:`~sage.manifolds.chart.RealChart.plot`. + + """ + def __init__(self, domain, coordinates='', names=None): + r""" + Construct a chart on a real differentiable manifold. + + TESTS:: + + sage: forget() # for doctests only + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: X + Chart (M, (x, y)) + sage: type(X) + <class 'sage.manifolds.differentiable.chart.RealDiffChart'> + sage: assumptions() # assumptions set in X._init_coordinates + [x is real, y is real] + sage: TestSuite(X).run() + + """ + RealChart.__init__(self, domain, coordinates=coordinates, names=names) + #!# vector frame shall be initialized here in ticket #18843 + +#***************************************************************************** + +class DiffCoordChange(CoordChange): + r""" + Transition map between two charts of a differentiable manifold. + + Giving two coordinate charts `(U,\varphi)` and `(V,\psi)` on a + differentiable manifold `M` of dimension `n` over a topological field `K`, + the *transition map from* `(U,\varphi)` *to* `(V,\psi)` is the map + + .. MATH:: + + \psi\circ\varphi^{-1}: \varphi(U\cap V) \subset K^n + \rightarrow \psi(U\cap V) \subset K^n, + + In other words, the transition map `\psi\circ\varphi^{-1}` expresses the + coordinates `(y^1,\ldots,y^n)` of `(V,\psi)` in terms of the coordinates + `(x^1,\ldots,x^n)` of `(U,\varphi)` on the open subset where the two + charts intersect, i.e. on `U\cap V`. + + By definition, the transition map `\psi\circ\varphi^{-1}` must be + of classe `C^k`, where `k` is the degree of differentiability of the + manifold (cf. + :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_degree`). + + INPUT: + + - ``chart1`` -- chart `(U,\varphi)` + - ``chart2`` -- chart `(V,\psi)` + - ``transformations`` -- tuple (or list) `(Y_1,\ldots,Y_2)`, where + `Y_i` is the symbolic expression of the coordinate `y^i` in terms + of the coordinates `(x^1,\ldots,x^n)` + + EXAMPLES: + + Transition map on a 2-dimensional differentiable manifold:: + + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: Y.<u,v> = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + sage: type(X_to_Y) + <class 'sage.manifolds.differentiable.chart.DiffCoordChange'> + sage: X_to_Y.display() + u = x + y + v = x - y + + """ + def __init__(self, chart1, chart2, *transformations): + r""" + Construct a transition map. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: Y.<u,v> = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + sage: type(X_to_Y) + <class 'sage.manifolds.differentiable.chart.DiffCoordChange'> + sage: TestSuite(X_to_Y).run(skip='_test_pickling') + + .. TODO:: + + fix _test_pickling + + """ + CoordChange.__init__(self, chart1, chart2, *transformations) + # Jacobian matrix: + self._jacobian = self._transf.jacobian() + # Jacobian determinant: + if self._n1 == self._n2: + self._jacobian_det = self._transf.jacobian_det() diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py new file mode 100644 index 00000000..8235a76 --- /dev/null +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -0,0 +1,484 @@ +r""" +Differentiable maps between differentiable manifolds + +The class :class:`DiffMap` implements differentiable maps from a differentiable +manifold `M` to a differentiable manifold `N` over the same topological field +`K` as `M` (in most applications, `K = \RR` or `K = \CC`): + +.. MATH:: + + \Phi: M \longrightarrow N + + +AUTHORS: + +- Eric Gourgoulhon, Michal Bejger (2013-2015): initial version + +REFERENCES: + +.. [1] Chap. 1 of S. Kobayashi & K. Nomizu : *Foundations of Differential + Geometry*, vol. 1, Interscience Publishers (New York) (1963) +.. [2] Chaps. 2 and 3 of J.M. Lee : *Introduction to Smooth Manifolds*, + 2nd ed., Springer (New York) (2013) + +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> +# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl> +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.manifolds.continuous_map import ContinuousMap + +class DiffMap(ContinuousMap): + r""" + Differentiable map between two differentiable manifolds. + + This class implements differentiable maps of the type + + .. MATH:: + + \Phi: M \longrightarrow N + + where `M` and `N` are differentiable manifolds over the same topological + field `K` (in most applications, `K = \RR` or `K = \CC`). + + Differentiable maps are the *morphisms* of the *category* of + differentiable manifolds. The set of all differentiable maps from `M` to + `N` is therefore the homset between `M` and `N`, which is denoted by + `\mathrm{Hom}(M,N)`. + + The class :class:`DiffMap` is a Sage *element* class, whose *parent* + class is + :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableManifoldHomset`. + It inherits from the class + :class:`~sage.manifolds.continuous_map.ContinuousMap` since a + differentiable map is obviously a continuous one. + + INPUT: + + - ``parent`` -- homset `\mathrm{Hom}(M,N)` to which the differentiable + map belongs + - ``coord_functions`` -- (default: ``None``) if not ``None``, must be + a dictionary of the coordinate expressions (as lists (or tuples) of the + coordinates of the image expressed in terms of the coordinates of + the considered point) with the pairs of charts (chart1, chart2) + as keys (chart1 being a chart on `M` and chart2 a chart on `N`). + If the dimension of the map's codomain is 1, a single coordinate + expression can be passed instead of a tuple with a single element + - ``name`` -- (default: ``None``) name given to the differentiable map + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + differentiable map; if ``None``, the LaTeX symbol is set to ``name`` + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a isomorphism (i.e. a diffeomorphism); if set to + ``True``, then the manifolds `M` and `N` must have the same dimension. + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `N` must be `M` and the entry ``coord_functions`` is not used. + + .. NOTE:: + + If the information passed by means of the argument ``coord_functions`` + is not sufficient to fully specify the differentiable map, + further coordinate expressions, in other charts, can be subsequently + added by means of the method + :meth:`~sage.manifolds.continuous_map.ContinuousMap.add_expr` + + EXAMPLES: + + The standard embedding of the sphere `S^2` into `\RR^3`:: + + sage: M = Manifold(2, 'S^2') # the 2-dimensional sphere S^2 + sage: U = M.open_subset('U') # complement of the North pole + sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole + sage: V = M.open_subset('V') # complement of the South pole + sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole + sage: M.declare_union(U,V) # S^2 is the union of U and V + sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)), + ....: intersection_name='W', restrictions1= x^2+y^2!=0, + ....: restrictions2= u^2+v^2!=0) + sage: uv_to_xy = xy_to_uv.inverse() + sage: N = Manifold(3, 'R^3', r'\RR^3') # R^3 + sage: c_cart.<X,Y,Z> = N.chart() # Cartesian coordinates on R^3 + sage: Phi = M.diff_map(N, + ....: {(c_xy, c_cart): [2*x/(1+x^2+y^2), 2*y/(1+x^2+y^2), (x^2+y^2-1)/(1+x^2+y^2)], + ....: (c_uv, c_cart): [2*u/(1+u^2+v^2), 2*v/(1+u^2+v^2), (1-u^2-v^2)/(1+u^2+v^2)]}, + ....: name='Phi', latex_name=r'\Phi') + sage: Phi + Differentiable map Phi from the 2-dimensional differentiable manifold + S^2 to the 3-dimensional differentiable manifold R^3 + sage: Phi.parent() + Set of Morphisms from 2-dimensional differentiable manifold S^2 to + 3-dimensional differentiable manifold R^3 in Category of smooth + manifolds over Real Field with 53 bits of precision + sage: Phi.parent() is Hom(M, N) + True + sage: type(Phi) + <class 'sage.manifolds.differentiable.diff_map.DifferentiableManifoldHomset_with_category.element_class'> + sage: Phi.display() + Phi: S^2 --> R^3 + on U: (x, y) |--> (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), + (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) + on V: (u, v) |--> (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1), + -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) + + It is possible to create the map via the method + :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_map` + only in a single pair of charts: the argument ``coord_functions`` is then + a mere list of coordinate expressions (and not a dictionary) and the + arguments ``chart1`` and ``chart2`` have to be provided if the charts + differ from the default ones on the domain and/or the codomain:: + + sage: Phi1 = M.diff_map(N, [2*x/(1+x^2+y^2), 2*y/(1+x^2+y^2), + ....: (x^2+y^2-1)/(1+x^2+y^2)], + ....: chart1=c_xy, chart2=c_cart, name='Phi', + ....: latex_name=r'\Phi') + + Since ``c_xy`` and ``c_cart`` are the default charts on respectively ``M`` + and ``N``, they can be omitted, so that the above declaration is equivalent + to:: + + sage: Phi1 = M.diff_map(N, [2*x/(1+x^2+y^2), 2*y/(1+x^2+y^2), + ....: (x^2+y^2-1)/(1+x^2+y^2)], + ....: name='Phi', latex_name=r'\Phi') + + With such a declaration, the differentiable map is only partially defined + on the manifold `S^2`, being known in only one chart:: + + sage: Phi1.display() + Phi: S^2 --> R^3 + on U: (x, y) |--> (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), + (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) + + The definition can be completed by means of the method + :meth:`~sage.manifolds.continuous_map.ContinuousMap.add_expr`:: + + sage: Phi1.add_expr(c_uv, c_cart, [2*u/(1+u^2+v^2), 2*v/(1+u^2+v^2), + ....: (1-u^2-v^2)/(1+u^2+v^2)]) + sage: Phi1.display() + Phi: S^2 --> R^3 + on U: (x, y) |--> (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), + (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) + on V: (u, v) |--> (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1), + -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) + + At this stage, ``Phi1`` and ``Phi`` are fully equivalent:: + + sage: Phi1 == Phi + True + + The test suite is passed:: + + sage: TestSuite(Phi).run() + sage: TestSuite(Phi1).run() + + The map acts on points:: + + sage: np = M.point((0,0), chart=c_uv, name='N') # the North pole + sage: Phi(np) + Point Phi(N) on the 3-dimensional differentiable manifold R^3 + sage: Phi(np).coord() # Cartesian coordinates + (0, 0, 1) + sage: sp = M.point((0,0), chart=c_xy, name='S') # the South pole + sage: Phi(sp).coord() # Cartesian coordinates + (0, 0, -1) + + Differentiable maps can be composed by means of the operator ``*``: let + us introduce the map `\RR^3\rightarrow \RR^2` corresponding to + the projection from the point `(X,Y,Z)=(0,0,1)` onto the equatorial plane + `Z=0`:: + + sage: P = Manifold(2, 'R^2', r'\RR^2') # R^2 (equatorial plane) + sage: cP.<xP, yP> = P.chart() + sage: Psi = N.diff_map(P, (X/(1-Z), Y/(1-Z)), name='Psi', + ....: latex_name=r'\Psi') + sage: Psi + Differentiable map Psi from the 3-dimensional differentiable manifold + R^3 to the 2-dimensional differentiable manifold R^2 + sage: Psi.display() + Psi: R^3 --> R^2 + (X, Y, Z) |--> (xP, yP) = (-X/(Z - 1), -Y/(Z - 1)) + + Then we compose ``Psi`` with ``Phi``, thereby getting a map + `S^2\rightarrow \RR^2`:: + + sage: ster = Psi*Phi ; ster + Differentiable map from the 2-dimensional differentiable manifold S^2 + to the 2-dimensional differentiable manifold R^2 + + Let us test on the South pole (``sp``) that ``ster`` is indeed the + composite of ``Psi`` and ``Phi``:: + + sage: ster(sp) == Psi(Phi(sp)) + True + + Actually ``ster`` is the stereographic projection from the North pole, as + its coordinate expression reveals:: + + sage: ster.display() + S^2 --> R^2 + on U: (x, y) |--> (xP, yP) = (x, y) + on V: (u, v) |--> (xP, yP) = (u/(u^2 + v^2), v/(u^2 + v^2)) + + If its codomain is 1-dimensional, a differentiable map must be + defined by a single symbolic expression for each pair of charts, and not + by a list/tuple with a single element:: + + sage: N = Manifold(1, 'N') + sage: c_N = N.chart('X') + sage: Phi = M.diff_map(N, {(c_xy, c_N): x^2+y^2, + ....: (c_uv, c_N): 1/(u^2+v^2)}) # not ...[1/(u^2+v^2)] or (1/(u^2+v^2),) + + An example of differentiable map `\RR \rightarrow \RR^2`:: + + sage: R = Manifold(1, 'R') # field R + sage: T.<t> = R.chart() # canonical chart on R + sage: R2 = Manifold(2, 'R^2') # R^2 + sage: c_xy.<x,y> = R2.chart() # Cartesian coordinates on R^2 + sage: Phi = R.diff_map(R2, [cos(t), sin(t)], name='Phi') ; Phi + Differentiable map Phi from the 1-dimensional differentiable manifold R + to the 2-dimensional differentiable manifold R^2 + sage: Phi.parent() + Set of Morphisms from 1-dimensional differentiable manifold R to + 2-dimensional differentiable manifold R^2 in Category of smooth + manifolds over Real Field with 53 bits of precision + sage: Phi.parent() is Hom(R, R2) + True + sage: Phi.display() + Phi: R --> R^2 + t |--> (x, y) = (cos(t), sin(t)) + + An example of diffeomorphism between the unit open disk and the Euclidean + plane `\RR^2`:: + + sage: D = R2.open_subset('D', coord_def={c_xy: x^2+y^2<1}) # the open unit disk + sage: Phi = D.diffeomorphism(R2, [x/sqrt(1-x^2-y^2), y/sqrt(1-x^2-y^2)], + ....: name='Phi', latex_name=r'\Phi') + sage: Phi + Diffeomorphism Phi from the Open subset D of the 2-dimensional + differentiable manifold R^2 to the 2-dimensional differentiable + manifold R^2 + sage: Phi.parent() + Set of Morphisms from Open subset D of the 2-dimensional differentiable + manifold R^2 to 2-dimensional differentiable manifold R^2 in Join of + Category of subobjects of sets and Category of smooth manifolds over + Real Field with 53 bits of precision + sage: Phi.parent() is Hom(D, R2) + True + sage: Phi.display() + Phi: D --> R^2 + (x, y) |--> (x, y) = (x/sqrt(-x^2 - y^2 + 1), y/sqrt(-x^2 - y^2 + 1)) + + The image of a point:: + + sage: p = D.point((1/2,0)) + sage: q = Phi(p) ; q + Point on the 2-dimensional differentiable manifold R^2 + sage: q.coord() + (1/3*sqrt(3), 0) + + The inverse diffeomorphism is computed by means of the method + :meth:`~sage.manifolds.continuous_map.ContinuousMap.inverse`:: + + sage: Phi.inverse() + Diffeomorphism Phi^(-1) from the 2-dimensional differentiable manifold R^2 + to the Open subset D of the 2-dimensional differentiable manifold R^2 + sage: Phi.inverse().display() + Phi^(-1): R^2 --> D + (x, y) |--> (x, y) = (x/sqrt(x^2 + y^2 + 1), y/sqrt(x^2 + y^2 + 1)) + + Equivalently, one may use the notations ``^(-1)`` or ``~`` to get the + inverse:: + + sage: Phi^(-1) is Phi.inverse() + True + sage: ~Phi is Phi.inverse() + True + + Check that ``~Phi`` is indeed the inverse of ``Phi``:: + + sage: (~Phi)(q) == p + True + sage: Phi * ~Phi == R2.identity_map() + True + sage: ~Phi * Phi == D.identity_map() + True + + The coordinate expression of the inverse diffeomorphism:: + + sage: (~Phi).display() + Phi^(-1): R^2 --> D + (x, y) |--> (x, y) = (x/sqrt(x^2 + y^2 + 1), y/sqrt(x^2 + y^2 + 1)) + + A special case of diffeomorphism: the identity map of the open unit disk:: + + sage: id = D.identity_map() ; id + Identity map Id_D of the Open subset D of the 2-dimensional + differentiable manifold R^2 + sage: latex(id) + \mathrm{Id}_{D} + sage: id.parent() + Set of Morphisms from Open subset D of the 2-dimensional differentiable + manifold R^2 to Open subset D of the 2-dimensional differentiable + manifold R^2 in Join of Category of subobjects of sets and Category of + smooth manifolds over Real Field with 53 bits of precision + sage: id.parent() is Hom(D, D) + True + sage: id is Hom(D,D).one() # the identity element of the monoid Hom(D,D) + True + + The identity map acting on a point:: + + sage: id(p) + Point on the 2-dimensional differentiable manifold R^2 + sage: id(p) == p + True + sage: id(p) is p + True + + The coordinate expression of the identity map:: + + sage: id.display() + Id_D: D --> D + (x, y) |--> (x, y) + + The identity map is its own inverse:: + + sage: id^(-1) is id + True + sage: ~id is id + True + + """ + def __init__(self, parent, coord_functions=None, name=None, + latex_name=None, is_isomorphism=False, is_identity=False): + r""" + Construct a differentiable map. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: N = Manifold(3, 'N') + sage: Y.<u,v,w> = N.chart() + sage: f = Hom(M,N)({(X,Y): (x+y, x*y, x-y)}, name='f') ; f + Differentiable map f from the 2-dimensional differentiable manifold + M to the 3-dimensional differentiable manifold N + sage: f.display() + f: M --> N + (x, y) |--> (u, v, w) = (x + y, x*y, x - y) + sage: TestSuite(f).run() + + The identity map:: + + sage: f = Hom(M,M)({}, is_identity=True) ; f + Identity map Id_M of the 2-dimensional differentiable manifold M + sage: f.display() + Id_M: M --> M + (x, y) |--> (x, y) + sage: TestSuite(f).run() + + """ + ContinuousMap.__init__(self, parent, coord_functions=coord_functions, + name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) + + # + # SageObject methods + # + + def _repr_(self): + r""" + String representation of the object. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: N = Manifold(2, 'N') + sage: Y.<u,v> = N.chart() + sage: f = Hom(M,N)({(X,Y): (x+y,x*y)}) + sage: f._repr_() + 'Differentiable map from the 2-dimensional differentiable manifold M to + the 2-dimensional differentiable manifold N' + sage: f = Hom(M,N)({(X,Y): (x+y,x*y)}, name='f') + sage: f._repr_() + 'Differentiable map f from the 2-dimensional differentiable manifold M to + the 2-dimensional differentiable manifold N' + sage: f = Hom(M,N)({(X,Y): (x+y,x-y)}, name='f', is_isomorphism=True) + sage: f._repr_() + 'Diffeomorphism f from the 2-dimensional differentiable manifold M to + the 2-dimensional differentiable manifold N' + sage: f = Hom(M,M)({(X,X): (x+y,x-y)}, name='f', is_isomorphism=True) + sage: f._repr_() + 'Diffeomorphism f of the 2-dimensional differentiable manifold M' + sage: f = Hom(M,M)({}, name='f', is_identity=True) + sage: f._repr_() + 'Identity map f of the 2-dimensional differentiable manifold M' + + """ + if self._is_identity: + return "Identity map " + self._name + \ + " of the {}".format(self._domain) + if self._is_isomorphism: + description = "Diffeomorphism" + else: + description = "Differentiable map" + if self._name is not None: + description += " " + self._name + if self._domain == self._codomain: + if self._is_isomorphism: + description += " of the {}".format(self._domain) + else: + description += " from the {} to itself".format(self._domain) + else: + description += " from the {} to the {}".format(self._domain, + self._codomain) + return description + + def _init_derived(self): + r""" + Initialize the derived quantities. + + TEST:: + + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: f = M.diffeomorphism(M, [x+y, x-y]) + sage: f._init_derived() + sage: f._restrictions + {} + sage: f._inverse + + """ + ContinuousMap._init_derived(self) # derived quantities of the mother + # class + self._diff = {} # dict. of the coord. expressions of the differential + # keys: pair of charts + + def _del_derived(self): + r""" + Delete the derived quantities. + + TEST:: + + sage: M = Manifold(2, 'M') + sage: X.<x,y> = M.chart() + sage: f = M.diffeomorphism(M, [x+y, x-y]) + sage: f^(-1) + Diffeomorphism of the 2-dimensional differentiable manifold M + sage: f._inverse # was set by f^(-1) + Diffeomorphism of the 2-dimensional differentiable manifold M + sage: f._del_derived() + sage: f._inverse # has been set to None by _del_derived() + + """ + ContinuousMap._del_derived(self) # derived quantities of the mother + # class + self._diff.clear() diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py new file mode 100644 index 00000000..93db5c9 --- /dev/null +++ b/src/sage/manifolds/differentiable/manifold.py @@ -0,0 +1,953 @@ +r""" +Differentiable manifolds + +Given a non-discrete topological field `K` (in most applications, `K = \RR` or +`K = \CC`; see however [4]_ for `K = \QQ_p` and [5]_ for other fields), +a *differentiable manifold over* `K` is a topological manifold `M` over `K` +equipped with an atlas whose transitions maps are of class `C^k` (i.e. +`k`-times continuously differentiable) for a fixed positive integer `k` +(possibly `k=\infty`). `M` is then called a `C^k`-*manifold over* `K`. + +Note that + +- if the mention of `K` is omitted, then `K=\RR` is assumed; +- if `K=\CC`, any `C^k`-manifold with `k\geq 1` is actually a + `C^\infty`-manifold (even an analytic manifold); +- if `K=\RR`, any `C^k`-manifold with `k\geq 1` admits a compatible + `C^\infty`-structure (Whitney's smoothing theorem). + +Differentiable manifolds are implemented via the class +:class:`DifferentiableManifold`. +Open subsets of differentiable manifolds are also implemented via +:class:`DifferentiableManifold`, since they are differentiable manifolds by +themselves. + +The user interface is provided by the generic function +:func:`~sage.manifolds.manifold.Manifold`, with +the argument ``structure`` set to ``'differentiable'`` and the argument +``diff_degree`` set to `k`, or the argument ``structure`` set to ``'smooth'`` +(the default value). + +.. RUBRIC:: Example 1: the 2-sphere as a differentiable manifold of dimension + 2 over `\RR` + +One starts by declaring `S^2` as a 2-dimensional differentiable manifold:: + + sage: M = Manifold(2, 'S^2') + sage: M + 2-dimensional differentiable manifold S^2 + +Since the base topological field has not been specified in the argument list +of ``Manifold``, `\RR` is assumed:: + + sage: M.base_field() + Real Field with 53 bits of precision + sage: dim(M) + 2 + +By default, the created object is a smooth manifold:: + + sage: M.diff_degree() + +Infinity + +Let us consider the complement of a point, the "North pole" say; this is an +open subset of `S^2`, which we call `U`:: + + sage: U = M.open_subset('U'); U + Open subset U of the 2-dimensional differentiable manifold S^2 + +A standard chart on `U` is provided by the stereographic projection from the +North pole to the equatorial plane:: + + sage: stereoN.<x,y> = U.chart(); stereoN + Chart (U, (x, y)) + +Thanks to the operator ``<x,y>`` on the left-hand side, the coordinates +declared in a chart (here `x` and `y`), are accessible by their names; they are +Sage's symbolic variables:: + + sage: y + y + sage: type(y) + <type 'sage.symbolic.expression.Expression'> + +The South pole is the point of coordinates `(x,y)=(0,0)` in the above +chart:: + + sage: S = U.point((0,0), chart=stereoN, name='S'); S + Point S on the 2-dimensional differentiable manifold S^2 + +Let us call `V` the open subset that is the complement of the South pole and +let us introduce on it the chart induced by the stereographic projection from +the South pole to the equatorial plane:: + + sage: V = M.open_subset('V'); V + Open subset V of the 2-dimensional differentiable manifold S^2 + sage: stereoS.<u,v> = V.chart(); stereoS + Chart (V, (u, v)) + +The North pole is the point of coordinates `(u,v)=(0,0)` in this chart:: + + sage: N = V.point((0,0), chart=stereoS, name='N'); N + Point N on the 2-dimensional differentiable manifold S^2 + +To fully construct the manifold, we declare that it is the union of `U` +and `V`:: + + sage: M.declare_union(U,V) + +and we provide the transition map between the charts ``stereoN`` = `(U, (x, y))` +and ``stereoS`` = `(V, (u, v))`, denoting by `W` the intersection of `U` and +`V` (`W` is the subset of `U` defined by `x^2+y^2\not=0`, as well as the subset +of `V` defined by `u^2+v^2\not=0`):: + + sage: stereoN_to_S = stereoN.transition_map(stereoS, + ....: [x/(x^2+y^2), y/(x^2+y^2)], intersection_name='W', + ....: restrictions1= x^2+y^2!=0, restrictions2= u^2+v^2!=0) + sage: stereoN_to_S + Change of coordinates from Chart (W, (x, y)) to Chart (W, (u, v)) + sage: stereoN_to_S.display() + u = x/(x^2 + y^2) + v = y/(x^2 + y^2) + +We give the name ``W`` to the Python variable representing `W=U\cap V`:: + + sage: W = U.intersection(V) + +The inverse of the transition map is computed by the method ``inverse()``:: + + sage: stereoN_to_S.inverse() + Change of coordinates from Chart (W, (u, v)) to Chart (W, (x, y)) + sage: stereoN_to_S.inverse().display() + x = u/(u^2 + v^2) + y = v/(u^2 + v^2) + +At this stage, we have four open subsets on `S^2`:: + + sage: M.list_of_subsets() + [2-dimensional differentiable manifold S^2, + Open subset U of the 2-dimensional differentiable manifold S^2, + Open subset V of the 2-dimensional differentiable manifold S^2, + Open subset W of the 2-dimensional differentiable manifold S^2] + +`W` is the open subset that is the complement of the two poles:: + + sage: N in W or S in W + False + +The North pole lies in `V` and the South pole in `U`:: + + sage: N in V, N in U + (True, False) + sage: S in U, S in V + (True, False) + +The manifold's (user) atlas contains four charts, two of them +being restrictions of charts to a smaller domain:: + + sage: M.atlas() + [Chart (U, (x, y)), Chart (V, (u, v)), Chart (W, (x, y)), Chart (W, (u, v))] + +Let us consider the point of coordinates (1,2) in the chart ``stereoN``:: + + sage: p = M.point((1,2), chart=stereoN, name='p'); p + Point p on the 2-dimensional differentiable manifold S^2 + sage: p.parent() + 2-dimensional differentiable manifold S^2 + sage: p in W + True + +The coordinates of `p` in the chart ``stereoS`` are computed by letting +the chart act on the point:: + + sage: stereoS(p) + (1/5, 2/5) + +Given the definition of `p`, we have of course:: + + sage: stereoN(p) + (1, 2) + +Similarly:: + + sage: stereoS(N) + (0, 0) + sage: stereoN(S) + (0, 0) + +A differentiable scalar field on the sphere:: + + sage: f = M.scalar_field({stereoN: atan(x^2+y^2), stereoS: pi/2-atan(u^2+v^2)}, + ....: name='f') + sage: f + Scalar field f on the 2-dimensional differentiable manifold S^2 + sage: f.display() + f: S^2 --> R + on U: (x, y) |--> arctan(x^2 + y^2) + on V: (u, v) |--> 1/2*pi - arctan(u^2 + v^2) + sage: f(p) + arctan(5) + sage: f(N) + 1/2*pi + sage: f(S) + 0 + sage: f.parent() + Algebra of differentiable scalar fields on the 2-dimensional differentiable + manifold S^2 + sage: f.parent().category() + Category of commutative algebras over Symbolic Ring + + +.. RUBRIC:: Example 2: the Riemann sphere as a differentiable manifold of + dimension 1 over `\CC` + +We declare the Riemann sphere `\CC^*` as a 1-dimensional differentiable +manifold over `\CC`:: + + sage: M = Manifold(1, 'C*', field='complex'); M + 1-dimensional complex manifold C* + +We introduce a first open subset, which is actually +`\CC = \CC^*\setminus\{\infty\}` if we interpret `\CC^*` as the Alexandroff +one-point compactification of `\CC`:: + + sage: U = M.open_subset('U') + +A natural chart on `U` is then nothing but the identity map of `\CC`, hence +we denote the associated coordinate by `z`:: + + sage: Z.<z> = U.chart() + +The origin of the complex plane is the point of coordinate `z=0`:: + + sage: O = U.point((0,), chart=Z, name='O'); O + Point O on the 1-dimensional complex manifold C* + +Another open subset of `\CC^*` is `V = \CC^*\setminus\{O\}`:: + + sage: V = M.open_subset('V') + +We define a chart on `V` such that the point at infinity is the point of +coordinate 0 in this chart:: + + sage: W.<w> = V.chart(); W + Chart (V, (w,)) + sage: inf = M.point((0,), chart=W, name='inf', latex_name=r'\infty') + sage: inf + Point inf on the 1-dimensional complex manifold C* + +To fully construct the Riemann sphere, we declare that it is the union of `U` +and `V`:: + + sage: M.declare_union(U,V) + +and we provide the transition map between the two charts as `w=1/z` on +on `A = U\cap V`:: + + sage: Z_to_W = Z.transition_map(W, 1/z, intersection_name='A', + ....: restrictions1= z!=0, restrictions2= w!=0) + sage: Z_to_W + Change of coordinates from Chart (A, (z,)) to Chart (A, (w,)) + sage: Z_to_W.display() + w = 1/z + sage: Z_to_W.inverse() + Change of coordinates from Chart (A, (w,)) to Chart (A, (z,)) + sage: Z_to_W.inverse().display() + z = 1/w + +Let consider the complex number `i` as a point of the Riemann sphere:: + + sage: i = M((I,), chart=Z, name='i'); i + Point i on the 1-dimensional complex manifold C* + +Its coordinates w.r.t. the charts ``Z`` and ``W`` are:: + + sage: Z(i) + (I,) + sage: W(i) + (-I,) + +and we have:: + + sage: i in U + True + sage: i in V + True + +The following subsets and charts have been defined:: + + sage: M.list_of_subsets() + [Open subset A of the 1-dimensional complex manifold C*, + 1-dimensional complex manifold C*, + Open subset U of the 1-dimensional complex manifold C*, + Open subset V of the 1-dimensional complex manifold C*] + sage: M.atlas() + [Chart (U, (z,)), Chart (V, (w,)), Chart (A, (z,)), Chart (A, (w,))] + +A constant map `\CC^* \rightarrow \CC`:: + + sage: f = M.constant_scalar_field(3+2*I, name='f'); f + Scalar field f on the 1-dimensional complex manifold C* + sage: f.display() + f: C* --> C + on U: z |--> 2*I + 3 + on V: w |--> 2*I + 3 + sage: f(O) + 2*I + 3 + sage: f(i) + 2*I + 3 + sage: f(inf) + 2*I + 3 + sage: f.parent() + Algebra of differentiable scalar fields on the 1-dimensional complex + manifold C* + sage: f.parent().category() + Category of commutative algebras over Symbolic Ring + +AUTHORS: + +- Eric Gourgoulhon (2015): initial version + +REFERENCES: + +.. [1] J.M. Lee : *Introduction to Smooth Manifolds*, 2nd ed., Springer + (New York) (2012); :doi:`10.1007/978-1-4419-9982-5` +.. [2] S. Kobayashi & K. Nomizu : *Foundations of Differential Geometry*, + vol. 1, Interscience Publishers (New York) (1963) +.. [3] D. Huybrechts : *Complex Geometry*, Springer (Berlin) (2005); + :doi:`10.1007/b137952` +.. [4] J.-P. Serre : *Lie Algebras and Lie Groups*, 2nd ed., Springer + (Berlin) (1992); :doi:`10.1007/978-3-540-70634-2` +.. [5] W. Bertram : *Differential Geometry, Lie Groups and Symmetric Spaces + over General Base Fields and Rings*, Memoirs of the American Mathematical + Society, vol. 192 (2008); :doi:`10.1090/memo/0900`; :arxiv:`math/0502168` +.. [6] M. Berger & B. Gostiaux : *Differential Geometry: Manifolds, Curves and + Surfaces*, Springer (New York) (1988); :doi:`10.1007/978-1-4612-1033-7` + +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr> +# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl> +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.manifolds import Manifolds +from sage.categories.homset import Hom +from sage.rings.all import CC +from sage.rings.real_mpfr import RR +from sage.rings.infinity import infinity +from sage.rings.integer import Integer +from sage.misc.latex import latex +from sage.manifolds.manifold import TopologicalManifold +from sage.manifolds.differentiable.scalarfield_algebra import \ + DiffScalarFieldAlgebra + +class DifferentiableMixin(object): + r""" + Mixin class for differentiable manifolds over a topological field `K`. + + This class gathers attributes and methods characteristic of differentiable + manifolds. It is intended to serve as a base class for both + :class:`DifferentiableManifold` and + :class:`~sage.manifolds.differentiable.subset.OpenDifferentiableSubmanifold` + + INPUT: + + - ``diff_degree`` -- (default: ``infinity``) degree `k` of + differentiability + + """ + def __init__(self, diff_degree=infinity): + r""" + Construct the differentiable mixin part. + + TESTS:: + + sage: M = Manifold(3, 'M', latex_name=r'\mathbb{M}', + ....: start_index=1) + sage: M + 3-dimensional differentiable manifold M + sage: X.<x,y,z> = M.chart() + sage: TestSuite(M).run() + + """ + # The degree of differentiability: + if diff_degree == infinity: + self._diff_degree = infinity + elif not isinstance(diff_degree, (int, Integer)): + raise TypeError("the argument 'diff_degree' must be an integer") + elif diff_degree < 1: + raise ValueError("the argument 'diff_degree' must be a positive " + + "integer") + else: + self._diff_degree = diff_degree + + def diff_degree(self): + r""" + Return the manifold's degree of differentiability. + + The degree of differentiability is the integer `k` (possibly + `k=\infty`) such that the manifold is a `C^k`-manifold over its base + field. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: M.diff_degree() + +Infinity + sage: M = Manifold(2, 'M', structure='differentiable', diff_degree=3) + sage: M.diff_degree() + 3 + + """ + return self._diff_degree + + def open_subset(self, name, latex_name=None, coord_def={}): + r""" + Create an open subset of the manifold. + + An open subset is a set that is (i) included in the manifold and (ii) + open with respect to the manifold's topology. It is a differentiable + manifold by itself. + + INPUT: + + - ``name`` -- name given to the open subset + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + subset; if none is provided, it is set to ``name`` + - ``coord_def`` -- (default: {}) definition of the subset in + terms of coordinates; ``coord_def`` must a be dictionary with keys + charts in the manifold's atlas and values the symbolic expressions + formed by the coordinates to define the subset. + + OUTPUT: + + - the open subset, as an instance of + :class:`~sage.manifolds.differentiable.subset.OpenDifferentiableSubmanifold` + + EXAMPLES: + + Creating an open subset of a manifold:: + + sage: M = Manifold(2, 'M') + sage: A = M.open_subset('A'); A + Open subset A of the 2-dimensional differentiable manifold M + + As an open subset of a differentiable manifold, A is itself a + differentiable manifold, on the same topological field and of the same + dimension as M:: + + sage: A.category() + Join of Category of subobjects of sets and Category of smooth + manifolds over Real Field with 53 bits of precision + sage: A.base_field() == M.base_field() + True + sage: dim(A) == dim(M) + True + + Creating an open subset of A:: + + sage: B = A.open_subset('B'); B + Open subset B of the 2-dimensional differentiable manifold M + + We have then:: + + sage: A.subsets() # random (set output) + {Open subset B of the 2-dimensional differentiable manifold M, + Open subset A of the 2-dimensional differentiable manifold M} + sage: B.is_subset(A) + True + sage: B.is_subset(M) + True + + Defining an open subset by some coordinate restrictions: the open + unit disk in `\RR^2`:: + + sage: M = Manifold(2, 'R^2') + sage: c_cart.<x,y> = M.chart() # Cartesian coordinates on R^2 + sage: U = M.open_subset('U', coord_def={c_cart: x^2+y^2<1}); U + Open subset U of the 2-dimensional differentiable manifold R^2 + + Since the argument ``coord_def`` has been set, U is automatically + provided with a chart, which is the restriction of the Cartesian one + to U:: + + sage: U.atlas() + [Chart (U, (x, y))] + + Therefore, one can immediately check whether a point belongs to U:: + + sage: M.point((0,0)) in U + True + sage: M.point((1/2,1/3)) in U + True + sage: M.point((1,2)) in U + False + + """ + from sage.manifolds.differentiable.subset import \ + OpenDifferentiableSubmanifold + resu = OpenDifferentiableSubmanifold(self.manifold(), name, + latex_name=latex_name) + resu._supersets.update(self._supersets) + for sd in self._supersets: + sd._subsets.add(resu) + self._top_subsets.add(resu) + # Charts on the result from the coordinate definition: + for chart, restrictions in coord_def.iteritems(): + if chart not in self._atlas: + raise ValueError("the {} does not belong to ".format(chart) + + "the atlas of {}".format(self)) + chart.restrict(resu, restrictions) + # Transition maps on the result inferred from those of self: + for chart1 in coord_def: + for chart2 in coord_def: + if chart2 != chart1 and (chart1, chart2) in self._coord_changes: + self._coord_changes[(chart1, chart2)].restrict(resu) + #!# update vector frames and change of frames + return resu + + def diff_map(self, codomain, coord_functions=None, chart1=None, + chart2=None, name=None, latex_name=None): + r""" + Define a differentiable map between the current differentiable manifold + and a differentiable manifold over the same topological field. + + See :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for a + complete documentation. + + INPUT: + + - ``codomain`` -- the map codomain (a differentiable manifold over the + same topological field as the current differentiable manifold) + - ``coord_functions`` -- (default: ``None``) if not ``None``, must be + either + + - (i) a dictionary of + the coordinate expressions (as lists (or tuples) of the + coordinates of the image expressed in terms of the coordinates of + the considered point) with the pairs of charts (chart1, chart2) + as keys (chart1 being a chart on the current manifold and chart2 a + chart on ``codomain``) + - (ii) a single coordinate expression in a given pair of charts, the + latter being provided by the arguments ``chart1`` and ``chart2`` + + In both cases, if the dimension of the arrival manifold is 1, + a single coordinate expression can be passed instead of a tuple with + a single element + - ``chart1`` -- (default: ``None``; used only in case (ii) above) chart + on the current manifold defining the start coordinates involved in + ``coord_functions`` for case (ii); if none is provided, the + coordinates are assumed to refer to the manifold's default chart + - ``chart2`` -- (default: ``None``; used only in case (ii) above) chart + on ``codomain`` defining the arrival coordinates involved in + ``coord_functions`` for case (ii); if none is provided, the + coordinates are assumed to refer to the default chart of ``codomain`` + - ``name`` -- (default: ``None``) name given to the differentiable + map + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + differentiable map; if none is provided, the LaTeX symbol is set to + ``name`` + + OUTPUT: + + - the differentiable map, as an instance of + :class:`~sage.manifolds.differentiable.diff_map.DiffMap` + + EXAMPLES: + + A differentiable map between an open subset of `S^2` covered by regular + spherical coordinates and `\RR^3`:: + + sage: M = Manifold(2, 'S^2') + sage: U = M.open_subset('U') + sage: c_spher.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: N = Manifold(3, 'R^3', r'\RR^3') + sage: c_cart.<x,y,z> = N.chart() # Cartesian coord. on R^3 + sage: Phi = U.diff_map(N, (sin(th)*cos(ph), sin(th)*sin(ph), cos(th)), + ....: name='Phi', latex_name=r'\Phi') + sage: Phi + Differentiable map Phi from the Open subset U of the 2-dimensional + differentiable manifold S^2 to the 3-dimensional differentiable + manifold R^3 + + The same definition, but with a dictionary with pairs of charts as + keys (case (i) above):: + + sage: Phi1 = U.diff_map(N, + ....: {(c_spher, c_cart): (sin(th)*cos(ph), sin(th)*sin(ph), + ....: cos(th))}, name='Phi', latex_name=r'\Phi') + sage: Phi1 == Phi + True + + The differentiable map acting on a point:: + + sage: p = U.point((pi/2, pi)) ; p + Point on the 2-dimensional differentiable manifold S^2 + sage: Phi(p) + Point on the 3-dimensional differentiable manifold R^3 + sage: Phi(p).coord(c_cart) + (-1, 0, 0) + sage: Phi1(p) == Phi(p) + True + + See the documentation of class + :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for more + examples. + + """ + homset = Hom(self, codomain) + if coord_functions is None: + coord_functions = {} + if not isinstance(coord_functions, dict): + # Turn coord_functions into a dictionary: + if chart1 is None: + chart1 = self._def_chart + elif chart1 not in self._atlas: + raise ValueError("{} is not a chart ".format(chart1) + + "defined on the {}".format(self)) + if chart2 is None: + chart2 = codomain._def_chart + elif chart2 not in codomain._atlas: + raise ValueError("{} is not a chart ".format(chart2) + + " defined on the {}".format(codomain)) + coord_functions = {(chart1, chart2): coord_functions} + return homset(coord_functions, name=name, latex_name=latex_name) + + def diff_mapping(self, codomain, coord_functions=None, chart1=None, + chart2=None, name=None, latex_name=None): + r""" + Deprecated. + + Use :meth:`diff_map` instead. + + EXAMPLE:: + + sage: M = Manifold(2, 'M'); X.<x,y> = M.chart() + sage: N = Manifold(2, 'N'); Y.<u,v> = N.chart() + sage: Phi = M.diff_mapping(N, {(X,Y): [x+y, x-y]}, name='Phi') + doctest:...: DeprecationWarning: Use diff_map() instead. + See http://trac.sagemath.org/18783 for details. + sage: Phi + Differentiable map Phi from the 2-dimensional differentiable + manifold M to the 2-dimensional differentiable manifold N + + """ + from sage.misc.superseded import deprecation + deprecation(18783, 'Use diff_map() instead.') + return self.diff_map(codomain, coord_functions=coord_functions, + chart1=chart1, chart2=chart2, name=name, + latex_name=latex_name) + + def diffeomorphism(self, codomain, coord_functions=None, chart1=None, + chart2=None, name=None, latex_name=None): + r""" + Define a diffeomorphism between the current manifold and another one. + + See :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for a + complete documentation. + + INPUT: + + - ``codomain`` -- codomain of the diffeomorphism (the arrival manifold + or some subset of it) + - ``coord_functions`` -- (default: ``None``) if not ``None``, must be + either + + - (i) a dictionary of + the coordinate expressions (as lists (or tuples) of the + coordinates of the image expressed in terms of the coordinates of + the considered point) with the pairs of charts (chart1, chart2) + as keys (chart1 being a chart on the current manifold and chart2 + a chart on ``codomain``) + - (ii) a single coordinate expression in a given pair of charts, the + latter being provided by the arguments ``chart1`` and ``chart2`` + + In both cases, if the dimension of the arrival manifold is 1, + a single coordinate expression can be passed instead of a tuple with + a single element + - ``chart1`` -- (default: ``None``; used only in case (ii) above) chart + on the current manifold defining the start coordinates involved in + ``coord_functions`` for case (ii); if none is provided, the + coordinates are assumed to refer to the manifold's default chart + - ``chart2`` -- (default: ``None``; used only in case (ii) above) chart + on ``codomain`` defining the arrival coordinates involved in + ``coord_functions`` for case (ii); if none is provided, the + coordinates are assumed to refer to the default chart of ``codomain`` + - ``name`` -- (default: ``None``) name given to the diffeomorphism + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + diffeomorphism; if none is provided, the LaTeX symbol is set to + ``name`` + + OUTPUT: + + - the diffeomorphism, as an instance of + :class:`~sage.manifolds.differentiable.diff_map.DiffMap` + + EXAMPLE: + + Diffeomorphism between the open unit disk in `\RR^2` and `\RR^2`:: + + sage: M = Manifold(2, 'M') # the open unit disk + sage: forget() # for doctests only + sage: c_xy.<x,y> = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord on M + sage: c_xy.add_restrictions(x^2+y^2<1) + sage: N = Manifold(2, 'N') # R^2 + sage: c_XY.<X,Y> = N.chart() # canonical coordinates on R^2 + sage: Phi = M.diffeomorphism(N, [x/sqrt(1-x^2-y^2), y/sqrt(1-x^2-y^2)], + ....: name='Phi', latex_name=r'\Phi') + sage: Phi + Diffeomorphism Phi from the 2-dimensional differentiable manifold M + to the 2-dimensional differentiable manifold N + sage: Phi.display() + Phi: M --> N + (x, y) |--> (X, Y) = (x/sqrt(-x^2 - y^2 + 1), y/sqrt(-x^2 - y^2 + 1)) + + The inverse diffeomorphism:: + + sage: Phi^(-1) + Diffeomorphism Phi^(-1) from the 2-dimensional differentiable + manifold N to the 2-dimensional differentiable manifold M + sage: (Phi^(-1)).display() + Phi^(-1): N --> M + (X, Y) |--> (x, y) = (X/sqrt(X^2 + Y^2 + 1), Y/sqrt(X^2 + Y^2 + 1)) + + See the documentation of class + :class:`~sage.manifolds.differentiable.diff_map.DiffMap` for more + examples. + + """ + homset = Hom(self, codomain) + if coord_functions is None: + coord_functions = {} + if not isinstance(coord_functions, dict): + # Turn coord_functions into a dictionary: + if chart1 is None: + chart1 = self._def_chart + elif chart1 not in self._atlas: + raise ValueError("{} is not a chart ".format(chart1) + + "defined on the {}".format(self)) + if chart2 is None: + chart2 = codomain._def_chart + elif chart2 not in codomain._atlas: + raise ValueError("{} is not a chart ".format(chart2) + + " defined on the {}".format(codomain)) + coord_functions = {(chart1, chart2): coord_functions} + return homset(coord_functions, name=name, latex_name=latex_name, + is_isomorphism=True) + + +############################################################################### + +class DifferentiableManifold(DifferentiableMixin, TopologicalManifold): + r""" + Differentiable manifold over a topological field `K`. + + Given a non-discrete topological field `K` (in most applications, + `K = \RR` or `K = \CC`; see however [4]_ for `K = \QQ_p` and [5]_ for + other fields), a *differentiable manifold over* `K` is a topological + manifold `M` over `K` equipped with an atlas whose transitions maps are of + class `C^k` (i.e. `k`-times continuously differentiable) for a fixed + positive integer `k` (possibly `k=\infty`). `M` is then called a + `C^k`-*manifold over* `K`. + + Note that + + - if the mention of `K` is omitted, then `K=\RR` is assumed; + - if `K=\CC`, any `C^k`-manifold with `k\geq 1` is actually a + `C^\infty`-manifold (even an analytic manifold); + - if `K=\RR`, any `C^k`-manifold with `k\geq 1` admits a compatible + `C^\infty`-structure (Whitney's smoothing theorem). + + INPUT: + + - ``n`` -- positive integer; dimension of the manifold + - ``name`` -- string; name (symbol) given to the manifold + - ``field`` -- field `K` on which the manifold is + defined; allowed values are + + - ``'real'`` or an object of type ``RealField`` (e.g., ``RR``) for + a manifold over `\RR` + - ``'complex'`` or an object of type ``ComplexField`` (e.g., ``CC``) + for a manifold over `\CC` + - an object in the category of topological fields (see + :class:`~sage.categories.fields.Fields` and + :class:`~sage.categories.topological_spaces.TopologicalSpaces`) + for other types of manifolds + + - ``structure`` -- manifold structure (see + :class:`~sage.manifolds.structure.DifferentialStructure` or + :class:`~sage.manifolds.structure.RealDifferentialStructure`) + - ``diff_degree`` -- (default: ``infinity``) degree `k` of + differentiability + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the manifold; if none is provided, it is set to ``name`` + - ``full_name`` -- (default: ``None``) string; short description of the + manifold; if none is provided, it is formed from the field, dimension + and structure + - ``start_index`` -- (default: 0) integer; lower value of the range of + indices used for "indexed objects" on the manifold, e.g. coordinates + in a chart + - ``category`` -- (default: ``None``) to specify the category; if ``None``, + ``Manifolds(field).Differentiable()`` (or ``Manifolds(field).Smooth()`` + if ``diff_degree`` = ``infinity``) is assumed (see the category + :class:`~sage.categories.manifolds.Manifolds`) + - ``unique_tag`` -- (default: ``None``) tag used to force the construction + of a new object when all the other arguments have been used previously + (without ``unique_tag``, the + :class:`~sage.structure.unique_representation.UniqueRepresentation` + behavior inherited from + :class:`~sage.manifolds.abstract.AbstractSet` + would return the previously constructed object corresponding to these + arguments). + + EXAMPLES: + + A 4-dimensional differentiable manifold (over `\RR`):: + + sage: M = Manifold(4, 'M', latex_name=r'\mathcal{M}'); M + 4-dimensional differentiable manifold M + sage: type(M) + <class 'sage.manifolds.differentiable.manifold.DifferentiableManifold_with_category'> + sage: latex(M) + \mathcal{M} + sage: dim(M) + 4 + + Since the base field has not been specified, `\RR` has been assumed:: + + sage: M.base_field() + Real Field with 53 bits of precision + + Since the degree of differentiability has not been specified, the default + value, `C^\infty`, has been assumed:: + + sage: M.diff_degree() + +Infinity + + The input parameter ``start_index`` defines the range of indices on the + manifold:: + + sage: M = Manifold(4, 'M') + sage: list(M.irange()) + [0, 1, 2, 3] + sage: M = Manifold(4, 'M', start_index=1) < |