summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Gourgoulhon <eric.gourgoulhon@obspm.fr>2016-01-03 14:06:53 +0100
committerEric Gourgoulhon <eric.gourgoulhon@obspm.fr>2016-01-03 14:06:53 +0100
commit069baf4c9bddffee6a31b58884dc383f27869248 (patch)
treedddfa48f4d77cf9a1a168acc276c9724d78cac49
parentUpdated Sage version to 7.0.beta2 (diff)
parentSuppress the (unused) argument category in OpenTopologicalSubmanifold; minor ... (diff)
Merge branch 'public/manifolds/top_manif_basics' into Sage 7.0.beta2.
-rw-r--r--src/doc/en/reference/index.rst1
l---------src/doc/en/reference/manifolds/conf.py1
-rw-r--r--src/doc/en/reference/manifolds/index.rst18
-rw-r--r--src/doc/en/reference/manifolds/manifold.rst17
-rw-r--r--src/sage/all.py2
-rw-r--r--src/sage/manifolds/__init__.py1
-rw-r--r--src/sage/manifolds/abstract.py679
-rw-r--r--src/sage/manifolds/all.py2
-rw-r--r--src/sage/manifolds/chart.py2036
-rw-r--r--src/sage/manifolds/manifold.py1698
-rw-r--r--src/sage/manifolds/point.py654
-rw-r--r--src/sage/manifolds/structure.py69
-rw-r--r--src/sage/manifolds/subset.py669
13 files changed, 5847 insertions, 0 deletions
diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst
index ed28a7a..104cf4e 100644
--- a/src/doc/en/reference/index.rst
+++ b/src/doc/en/reference/index.rst
@@ -86,6 +86,7 @@ Geometry and Topology
* :doc:`Combinatorial Geometry <geometry/index>`
* :doc:`Cell Complexes and their Homology <homology/index>`
* :doc:`Differential Forms <tensor/index>`
+* :doc:`Manifolds <manifolds/index>`
* :doc:`Parametrized Surfaces <riemannian_geometry/index>`
Number Theory, Algebraic Geometry
diff --git a/src/doc/en/reference/manifolds/conf.py b/src/doc/en/reference/manifolds/conf.py
new file mode 120000
index 00000000..2bdf7e6
--- /dev/null
+++ b/src/doc/en/reference/manifolds/conf.py
@@ -0,0 +1 @@
+../conf_sub.py \ No newline at end of file
diff --git a/src/doc/en/reference/manifolds/index.rst b/src/doc/en/reference/manifolds/index.rst
new file mode 100644
index 00000000..f0d5932
--- /dev/null
+++ b/src/doc/en/reference/manifolds/index.rst
@@ -0,0 +1,18 @@
+Manifolds
+=========
+
+This is the Sage implementation of manifolds resulting from the
+`SageManifolds project <http://sagemanifolds.obspm.fr/>`_.
+This section describes only the "manifold" part of SageManifolds;
+the pure algebraic part is described in the section
+:ref:`tensors-on-free-modules`.
+
+More documentation (in particular example worksheets) can be found
+`here <http://sagemanifolds.obspm.fr/documentation.html>`_.
+
+.. toctree::
+ :maxdepth: 2
+
+ manifold
+
+.. include:: ../footer.txt
diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst
new file mode 100644
index 00000000..d7a0d18
--- /dev/null
+++ b/src/doc/en/reference/manifolds/manifold.rst
@@ -0,0 +1,17 @@
+Topological manifolds
+=====================
+
+.. toctree::
+ :maxdepth: 2
+
+ sage/manifolds/manifold
+
+ sage/manifolds/abstract
+
+ sage/manifolds/subset
+
+ sage/manifolds/structure
+
+ sage/manifolds/point
+
+ sage/manifolds/chart
diff --git a/src/sage/all.py b/src/sage/all.py
index 3ddfe08..664bc0b 100644
--- a/src/sage/all.py
+++ b/src/sage/all.py
@@ -173,6 +173,8 @@ from sage.matroids.all import *
from sage.game_theory.all import *
+from sage.manifolds.all import *
+
# Lazily import notebook functions and interacts (#15335)
lazy_import('sagenb.notebook.notebook_object', 'notebook')
lazy_import('sagenb.notebook.notebook_object', 'inotebook')
diff --git a/src/sage/manifolds/__init__.py b/src/sage/manifolds/__init__.py
new file mode 100644
index 00000000..932b798
--- /dev/null
+++ b/src/sage/manifolds/__init__.py
@@ -0,0 +1 @@
+# Empty file
diff --git a/src/sage/manifolds/abstract.py b/src/sage/manifolds/abstract.py
new file mode 100644
index 00000000..7bab86e
--- /dev/null
+++ b/src/sage/manifolds/abstract.py
@@ -0,0 +1,679 @@
+r"""
+Abstract objects and sets
+
+These are base classes for the manifolds.
+
+AUTHORS:
+
+- Travis Scrimshaw (2015-11-25): initial version
+- Eric Gourgoulhon (2015): some methods from previous TopologicalManifold class
+
+"""
+
+#*****************************************************************************
+# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
+# Copyright (C) 2015 Travis Scrimshaw <tscrimsh@umn.edu>
+#
+# 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.structure.parent import Parent
+from sage.structure.unique_representation import UniqueRepresentation
+from sage.categories.fields import Fields
+from sage.categories.manifolds import Manifolds
+from sage.categories.sets_cat import Sets
+from sage.rings.integer import Integer
+from sage.manifolds.point import ManifoldPoint
+
+class AbstractNamedObject(object):
+ r"""
+ An abstract named object.
+
+ This abstract class handles the outputs (text and LaTeX) of named objects
+ (e.g. parent objects in manifold classes).
+
+ INPUT:
+
+ - ``name`` -- (short) string; name (symbol) given to the object
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to
+ denote the object; if ``None``, the LaTeX symbol is set to ``name``
+ - ``full_name`` -- (default: ``None``) string; short description of the
+ object; if ``None``, the description is set to ``name``.
+
+ EXAMPLES::
+
+ sage: from sage.manifolds.abstract import AbstractNamedObject
+ sage: a = AbstractNamedObject('A', latex_name=r'\mathcal{A}',
+ ....: full_name='Object A')
+ sage: a._repr_()
+ 'Object A'
+ sage: a._latex_()
+ '\\mathcal{A}'
+ sage: latex(a)
+ \mathcal{A}
+
+ Using default values::
+
+ sage: a = AbstractNamedObject('A')
+ sage: a._repr_()
+ 'A'
+ sage: a._latex_()
+ 'A'
+ sage: latex(a)
+ A
+
+ """
+ def __init__(self, name, latex_name=None, full_name=None):
+ """
+ Initialize ``self``.
+
+ TEST::
+
+ sage: from sage.manifolds.abstract import AbstractNamedObject
+ sage: a = AbstractNamedObject('a')
+ sage: type(a)
+ <class 'sage.manifolds.abstract.AbstractNamedObject'>
+
+ """
+ if not isinstance(name, str):
+ raise TypeError("{} is not a string".format(name))
+ self._name = name
+ if latex_name is None:
+ self._latex_name = self._name
+ else:
+ if not isinstance(latex_name, str):
+ raise TypeError("{} is not a string".format(latex_name))
+ self._latex_name = latex_name
+ if full_name is None:
+ self.__custom_name = self._name
+ else:
+ if not isinstance(full_name, str):
+ raise TypeError("{} is not a string".format(full_name))
+ self.__custom_name = full_name
+
+ def _repr_(self):
+ """
+ Return a string representation of ``self``.
+
+ TEST::
+
+ sage: from sage.manifolds.abstract import AbstractNamedObject
+ sage: a = AbstractNamedObject('a')
+ sage: a._repr_()
+ 'a'
+
+ """
+ return self.__custom_name
+
+ def _latex_(self):
+ r"""
+ LaTeX representation of ``self``.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: A = M.subset('A')
+ sage: A._latex_()
+ 'A'
+ sage: B = A.subset('B', latex_name=r'\mathcal{B}')
+ sage: B._latex_()
+ '\\mathcal{B}'
+ sage: latex(B) # indirect doctest
+ \mathcal{B}
+
+ sage: M = Manifold(3, 'M', structure='topological')
+ sage: M._latex_()
+ 'M'
+ sage: latex(M)
+ M
+ sage: M = Manifold(3, 'M', latex_name=r'\mathcal{M}',
+ ....: structure='topological')
+ sage: M._latex_()
+ '\\mathcal{M}'
+ sage: latex(M) # indirect doctest
+ \mathcal{M}
+ """
+ return self._latex_name
+
+class AbstractSet(AbstractNamedObject, UniqueRepresentation, Parent):
+ """
+ An abstract set on a topological manifold.
+
+ An abstract set is an :class:`AbstractNamedObject` along with its known
+ subsets, supersets, intersections, unions, and open covers.
+
+ INPUT:
+
+ - ``name`` -- (short) string; name (symbol) given to the object
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to
+ denote the object; if ``None``, the LaTeX symbol is set to ``name``
+ - ``full_name`` -- (default: ``None``) string; short description of the
+ object; if ``None``, the description is set to ``name``.
+ - ``base`` -- (default: ``None``) base for the
+ :class:`~sage.structure.parent.Parent` constructor
+ - ``category`` -- (default: ``None``) category of the object
+
+ """
+ def __init__(self, name, latex_name=None, full_name=None, base=None,
+ category=None):
+ r"""
+ Initialize ``self``
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: A = M.subset('A'); A
+ Subset A of the 2-dimensional topological manifold M
+ """
+ AbstractNamedObject.__init__(self, name, latex_name=latex_name,
+ full_name=full_name)
+
+ category = Sets().or_subcategory(category)
+ Parent.__init__(self, base=base, category=category)
+
+ self._supersets = set([self]) # subsets containing self
+ self._subsets = set([self]) # subsets of self
+ self._top_subsets = set([self]) # subsets contained in self but not
+ # in another strict subset of self
+ self._intersections = {} # dict. of intersections with other subsets
+ # (key: subset name)
+ self._unions = {} # dict. of unions with other subsets (key: subset
+ # name)
+ self._open_covers = [] # list of open covers of self
+
+ #### Methods required for any Parent in the category of sets:
+
+ def _element_constructor_(self, coords=None, chart=None, name=None,
+ latex_name=None, check_coords=True):
+ r"""
+ Construct a point in the subset from its coordinates in some chart.
+
+ INPUT:
+
+ - ``coords`` -- (default: ``None``) either (i) the point coordinates
+ (as a tuple or a list) in the chart ``chart`` or (ii) another point
+ in the subset
+ - ``chart`` -- (default: ``None``) chart in which the coordinates are
+ given; if none is provided, the coordinates are assumed to refer to
+ the subset's default chart
+ - ``name`` -- (default: ``None``) name given to the point
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
+ point; if none is provided, the LaTeX symbol is set to ``name``
+ - ``check_coords`` -- (default: ``True``) determines whether ``coords``
+ are valid coordinates for the chart ``chart``; for symbolic
+ coordinates, it is recommended to set ``check_coords`` to ``False``.
+
+ OUTPUT:
+
+ - an instance of :class:`~sage.manifolds.point.ManifoldPoint`
+ representing a point in the current subset.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: p = M((-2,3)); p # coord in the default chart
+ Point on the 2-dimensional topological manifold M
+ sage: X(p)
+ (-2, 3)
+
+ A generic subset has no default chart, so the chart must be explicited::
+
+ sage: A = M.subset('A')
+ sage: p = A((-2,3), chart=X); p
+ Point on the 2-dimensional topological manifold M
+ sage: X(p)
+ (-2, 3)
+ sage: p.parent()
+ Subset A of the 2-dimensional topological manifold M
+ sage: p in A
+ True
+
+ Coordinates in a chart with some coordinate restrictions::
+
+ sage: Y.<u,v> = M.chart('u:(-1,1) v:(-1,1)')
+ sage: p = A((0,1/2), chart=Y); p
+ Point on the 2-dimensional topological manifold M
+ sage: Y(p)
+ (0, 1/2)
+ sage: p = A((0,1/2), chart=Y, check_coords=False); p
+ Point on the 2-dimensional topological manifold M
+ sage: Y(p)
+ (0, 1/2)
+ sage: p = A((3,1/2), chart=Y)
+ Traceback (most recent call last):
+ ...
+ ValueError: the coordinates (3, 1/2) are not valid on the Chart (M, (u, v))
+
+ Specifying the name of the point::
+
+ sage: p = A((-2,3), chart=X, name='p'); p
+ Point p on the 2-dimensional topological manifold M
+
+ A point as entry::
+
+ sage: q = A(p); q
+ Point p on the 2-dimensional topological manifold M
+ sage: X(q)
+ (-2, 3)
+
+ """
+ if isinstance(coords, ManifoldPoint):
+ point = coords # for readability
+ # This should actually never happen by the coercion framework...
+ if point.parent() is self:
+ return point
+ if point in self:
+ resu = self.element_class(self, name=point._name,
+ latex_name=point._latex_name)
+ for chart, coords in point._coordinates.iteritems():
+ resu._coordinates[chart] = coords
+ return resu
+ else:
+ raise ValueError("the {}".format(point) +
+ " is not in {}".format(self))
+ return self.element_class(self, coords=coords, chart=chart, name=name,
+ latex_name=latex_name, check_coords=check_coords)
+
+ def _an_element_(self):
+ r"""
+ Construct some point in the subset.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: A = M.subset('A')
+ sage: p = A._an_element_(); p
+ Point on the 2-dimensional topological manifold M
+ sage: p in A
+ True
+
+ """
+ #!# should be improved...
+ return self.element_class(self)
+
+ #### End of methods required for any Parent in the category of sets
+
+ def open_covers(self):
+ r"""
+ Return the list of open covers of the current subset.
+
+ If the current subset, `A` say, is a subset of the manifold `M`, an
+ *open cover* of `A` is list (indexed set) `(U_i)_{i\in I}` of
+ open subsets of `M` such that
+
+ .. MATH::
+
+ A \subset \bigcup_{i \in I} U_i
+
+ If `A` is open, we ask that the above inclusion is actually an
+ identity:
+
+ .. MATH::
+
+ A = \bigcup_{i \in I} U_i
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: M.open_covers()
+ [[2-dimensional topological manifold M]]
+ sage: U = M.open_subset('U')
+ sage: U.open_covers()
+ [[Open subset U of the 2-dimensional topological manifold M]]
+ sage: A = U.open_subset('A')
+ sage: B = U.open_subset('B')
+ sage: U.declare_union(A,B)
+ sage: U.open_covers()
+ [[Open subset U of the 2-dimensional topological manifold M],
+ [Open subset A of the 2-dimensional topological manifold M,
+ Open subset B of the 2-dimensional topological manifold M]]
+ sage: V = M.open_subset('V')
+ sage: M.declare_union(U,V)
+ sage: M.open_covers()
+ [[2-dimensional topological manifold M],
+ [Open subset U of the 2-dimensional topological manifold M,
+ Open subset V of the 2-dimensional topological manifold M],
+ [Open subset A of the 2-dimensional topological manifold M,
+ Open subset B of the 2-dimensional topological manifold M,
+ Open subset V of the 2-dimensional topological manifold M]]
+
+ """
+ return self._open_covers
+
+ def subsets(self):
+ r"""
+ Return the set of subsets that have been defined on the current subset.
+
+ OUTPUT:
+
+ - A Python set containing all the subsets that have been defined on
+ the current subset.
+
+ .. NOTE::
+
+ To get the subsets as a list, used the method
+ :meth:`list_of_subsets` instead.
+
+ EXAMPLE:
+
+ Subsets of a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: U = M.open_subset('U')
+ sage: V = M.subset('V')
+ sage: M.subsets() # random (set output)
+ {Subset V of the 2-dimensional topological manifold M,
+ 2-dimensional topological manifold M,
+ Open subset U of the 2-dimensional topological manifold M}
+ sage: type(M.subsets())
+ <type 'frozenset'>
+ sage: U in M.subsets()
+ True
+
+ The method :meth:`list_of_subsets` returns a list (sorted
+ alphabetically by the subset names) instead of a set::
+
+ sage: M.list_of_subsets()
+ [2-dimensional topological manifold M,
+ Open subset U of the 2-dimensional topological manifold M,
+ Subset V of the 2-dimensional topological manifold M]
+
+ """
+ return frozenset(self._subsets)
+
+ def list_of_subsets(self):
+ r"""
+ Return the list of subsets that have been defined on the current
+ subset.
+
+ The list is sorted by the alphabetical names of the subsets.
+
+ OUTPUT:
+
+ - A list containing all the subsets that have been defined on
+ the current subset.
+
+ .. NOTE::
+
+ To get the subsets as a Python set, used the method
+ :meth:`subsets` instead.
+
+ EXAMPLE:
+
+ Subsets of a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: U = M.open_subset('U')
+ sage: V = M.subset('V')
+ sage: M.list_of_subsets()
+ [2-dimensional topological manifold M,
+ Open subset U of the 2-dimensional topological manifold M,
+ Subset V of the 2-dimensional topological manifold M]
+
+ The method :meth:`subsets` returns a set instead of a list::
+
+ sage: M.subsets() # random (set output)
+ {Subset V of the 2-dimensional topological manifold M,
+ 2-dimensional topological manifold M,
+ Open subset U of the 2-dimensional topological manifold M}
+
+ """
+ return sorted(self._subsets, key=lambda x: x._name)
+
+ def subset(self, name, latex_name=None, is_open=False):
+ r"""
+ Create a subset of ``self``.
+
+ INPUT:
+
+ - ``name`` -- name given to the subset
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
+ subset; if none is provided, it is set to ``name``
+ - ``is_open`` -- (default: ``False``) if ``True``, the created subset
+ is assumed to be open with respect to the manifold's topology
+
+ OUTPUT:
+
+ - the subset, as an instance of :class:`~sage.manifolds.subset.ManifoldSubset`, or
+ of the derived class
+ :class:`~sage.manifolds.subset.OpenTopologicalSubmanifold` if ``is_open``
+ is ``True``.
+
+ EXAMPLES:
+
+ Creating a subset of a manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: a = M.subset('A'); a
+ Subset A of the 2-dimensional topological manifold M
+
+ Creating a subset of A::
+
+ sage: b = a.subset('B', latex_name=r'\mathcal{B}'); b
+ Subset B of the 2-dimensional topological manifold M
+ sage: latex(b)
+ \mathcal{B}
+
+ We have then::
+
+ sage: b.is_subset(a)
+ True
+ sage: b in a.subsets()
+ True
+ """
+ if is_open:
+ return self.open_subset(name, latex_name=latex_name)
+ from sage.manifolds.subset import ManifoldSubset
+ res = ManifoldSubset(self.manifold(), name, latex_name=latex_name)
+ res._supersets.update(self._supersets)
+ for sd in self._supersets:
+ sd._subsets.add(res)
+ self._top_subsets.add(res)
+ return res
+
+ def get_subset(self, name):
+ r"""
+ Get a subset by its name.
+
+ The subset must have been previously created by the method
+ :meth:`subset` (or
+ :meth:`~sage.manifolds.manifold.TopologicalManifold.open_subset`)
+
+ INPUT:
+
+ - ``name`` -- (string) name of the subset
+
+ OUTPUT:
+
+ - instance of :class:`~sage.manifolds.subset.ManifoldSubset` (or
+ of the derived class
+ :class:`~sage.manifolds.subset.OpenTopologicalSubmanifold` for an open
+ subset) representing the subset whose name is ``name``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(4, 'M', structure='topological')
+ sage: A = M.subset('A')
+ sage: B = A.subset('B')
+ sage: U = M.open_subset('U')
+ sage: M.list_of_subsets()
+ [Subset A of the 4-dimensional topological manifold M,
+ Subset B of the 4-dimensional topological manifold M,
+ 4-dimensional topological manifold M,
+ Open subset U of the 4-dimensional topological manifold M]
+ sage: M.get_subset('A')
+ Subset A of the 4-dimensional topological manifold M
+ sage: M.get_subset('A') is A
+ True
+ sage: M.get_subset('B') is B
+ True
+ sage: A.get_subset('B') is B
+ True
+ sage: M.get_subset('U')
+ Open subset U of the 4-dimensional topological manifold M
+ sage: M.get_subset('U') is U
+ True
+
+ """
+ for ss in self._subsets:
+ if ss._name == name:
+ return ss
+ raise ValueError("no subset of name '{}' found".format(name))
+
+ def is_subset(self, other):
+ r"""
+ Return ``True`` iff ``self`` is included in ``other``.
+
+ EXAMPLES:
+
+ Subsets on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: a = M.subset('A')
+ sage: b = a.subset('B')
+ sage: c = M.subset('C')
+ sage: a.is_subset(M)
+ True
+ sage: b.is_subset(a)
+ True
+ sage: b.is_subset(M)
+ True
+ sage: a.is_subset(b)
+ False
+ sage: c.is_subset(a)
+ False
+ """
+ return self in other._subsets
+
+ def __contains__(self, point):
+ r"""
+ Check whether ``point`` is contained in ``self``.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: A = M.subset('A')
+ sage: p = A((-2,3), chart=X); p
+ Point on the 2-dimensional topological manifold M
+ sage: A.__contains__(p)
+ True
+ sage: p in A # indirect doctest
+ True
+ sage: A.__contains__(A.an_element())
+ True
+ sage: q = M((0,0), chart=X); q
+ Point on the 2-dimensional topological manifold M
+ sage: A.__contains__(q)
+ False
+ """
+ # for efficiency, a quick test first:
+ if point.parent() is self:
+ return True
+ if point.parent().is_subset(self):
+ return True
+ #!# should be improved once coordinate definition have been introduced
+ # in ManifoldSubset
+ return False
+
+ def declare_union(self, dom1, dom2):
+ r"""
+ Declare that the current subset is the union of two subsets,
+ i.e. that
+
+ .. MATH::
+
+ U = U_1 \cup U_2,
+
+ where `U` is the current subset, `U_1 \subset U` and `U_2 \subset U`.
+
+ INPUT:
+
+ - ``dom1`` -- subset `U_1`
+ - ``dom2`` -- subset `U_2`
+
+ EXAMPLE::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: A = M.subset('A')
+ sage: B = M.subset('B')
+ sage: M.declare_union(A, B)
+ sage: A.union(B)
+ 2-dimensional topological manifold M
+
+ """
+ if dom1 == dom2:
+ if dom1 != self:
+ raise ValueError("the union of two identical sets must be " +
+ "this set")
+ return
+ if not dom1.is_subset(self):
+ raise TypeError("the {} is not a subset of ".format(dom1) +
+ "the {}".format(self))
+ if not dom2.is_subset(self):
+ raise TypeError("the {} is not a subset of ".format(dom2) +
+ "the {}".format(self))
+ dom1._unions[dom2._name] = self
+ dom2._unions[dom1._name] = self
+ for oc1 in dom1._open_covers:
+ for oc2 in dom2._open_covers:
+ oc = oc1[:]
+ for s in oc2:
+ if s not in oc:
+ oc.append(s)
+ self._open_covers.append(oc)
+
+ def point(self, coords=None, chart=None, name=None, latex_name=None):
+ r"""
+ Define a point in ``self``.
+
+ See :class:`~sage.manifolds.point.ManifoldPoint` for a
+ complete documentation.
+
+ INPUT:
+
+ - ``coords`` -- the point coordinates (as a tuple or a list) in the
+ chart specified by ``chart``
+ - ``chart`` -- (default: ``None``) chart in which the point coordinates
+ are given; if ``None``, the coordinates are assumed to refer to
+ the default chart of the current subset
+ - ``name`` -- (default: ``None``) name given to the point
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
+ point; if ``None``, the LaTeX symbol is set to ``name``
+
+ OUTPUT:
+
+ - the declared point, as an instance of
+ :class:`~sage.manifolds.point.ManifoldPoint`.
+
+ EXAMPLES:
+
+ Points on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart()
+ sage: p = M.point((1,2), name='p'); p
+ Point p on the 2-dimensional topological manifold M
+ sage: p in M
+ True
+ sage: a = M.open_subset('A')
+ sage: c_uv.<u,v> = a.chart()
+ sage: q = a.point((-1,0), name='q'); q
+ Point q on the 2-dimensional topological manifold M
+ sage: q in a
+ True
+ sage: p._coordinates
+ {Chart (M, (x, y)): (1, 2)}
+ sage: q._coordinates
+ {Chart (A, (u, v)): (-1, 0)}
+ """
+ return self.element_class(self, coords=coords, chart=chart,
+ name=name, latex_name=latex_name)
+
+ Element = ManifoldPoint
diff --git a/src/sage/manifolds/all.py b/src/sage/manifolds/all.py
new file mode 100644
index 00000000..c7defde
--- /dev/null
+++ b/src/sage/manifolds/all.py
@@ -0,0 +1,2 @@
+from sage.misc.lazy_import import lazy_import
+lazy_import('sage.manifolds.manifold', 'Manifold')
diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py
new file mode 100644
index 00000000..993f046
--- /dev/null
+++ b/src/sage/manifolds/chart.py
@@ -0,0 +1,2036 @@
+r"""
+Coordinate charts
+
+The class :class:`Chart` implements coordinate charts on a topological manifold
+over a topological field `K`. The subclass :class:`RealChart` is devoted
+to the case `K=\RR`, for which the concept of coordinate range is meaningful.
+
+Transition maps between charts are implemented via the class
+:class:`CoordChange`.
+
+AUTHORS:
+
+- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
+- Travis Scrimshaw (2015): review tweaks
+
+REFERENCES:
+
+- Chap. 2 of [Lee11]_ J.M. Lee: *Introduction to Topological Manifolds*,
+ 2nd ed., Springer (New York) (2011)
+
+- Chap. 1 of [Lee13]_ 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>
+# Copyright (C) 2015 Travis Scrimshaw <tscrimsh@umn.edu>
+#
+# 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.structure.sage_object import SageObject
+from sage.structure.unique_representation import UniqueRepresentation
+from sage.symbolic.ring import SR
+from sage.rings.infinity import Infinity
+from sage.misc.latex import latex
+
+class Chart(UniqueRepresentation, SageObject):
+ r"""
+ Chart on a manifold.
+
+ Given a (topological) manifold `M` of dimension `n` over a topological
+ field `K`, a *chart* on `M` is a pair `(U,\varphi)`, where `U` is 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 (must be
+ an instance of :class:`~sage.manifolds.manifold.TopologicalManifold`)
+ - ``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 topological manifold::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ 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.chart.Chart'>
+
+ 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', structure='topological')
+ 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
+
+ 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', structure='topological')
+ 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', structure='topological')
+ sage: X.<x,y> = M.chart()
+
+ In the above example, the chart X covers entirely the manifold ``M``::
+
+ sage: X.domain()
+ Complex 2-dimensional topological 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 Complex 2-dimensional topological 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', structure='topological',
+ ....: 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 slice
+ operator ``[:]``::
+
+ sage: Y[:]
+ (z1, z2)
+
+ Some partial sets of coordinates::
+
+ sage: Y[:1]
+ (z1,)
+ sage: Y[1:]
+ (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 chart map `\varphi` acting on a point is obtained by passing
+ it as an input to the map::
+
+ sage: p = M.point((1+i, 2), chart=X); p
+ Point on the Complex 2-dimensional topological manifold M
+ sage: X(p)
+ (I + 1, 2)
+ sage: X(p) == p.coord(X)
+ True
+
+ .. SEEALSO::
+
+ :class:`sage.manifolds.chart.RealChart` for charts on topological
+ manifolds over `\RR`.
+
+ """
+ def __init__(self, domain, coordinates='', names=None):
+ r"""
+ Construct a chart.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X
+ Chart (M, (x, y))
+ sage: type(X)
+ <class 'sage.manifolds.chart.Chart'>
+ sage: assumptions() # no assumptions on x,y set by X._init_coordinates
+ []
+ sage: TestSuite(X).run()
+
+ """
+ from sage.manifolds.manifold import TopologicalManifold
+ if not isinstance(domain, TopologicalManifold):
+ raise TypeError("the first argument must be an open subset of " +
+ "a topological manifold")
+ if coordinates == '':
+ for x in names:
+ coordinates += x + ' '
+ coordinates = coordinates[:-1]
+ self._manifold = domain.manifold()
+ self._domain = domain
+ # Treatment of the coordinates:
+ if ' ' in coordinates:
+ coord_list = coordinates.split()
+ else:
+ coord_list = [coordinates]
+ if len(coord_list) != self._manifold.dim():
+ raise ValueError("the list of coordinates must contain " +
+ "{} elements".format(self._manifold.dim()))
+ # The treatment of coordinates is performed by a seperate method,
+ # _init_coordinates, which sets self._xx and
+ # which may be redefined for subclasses (for instance RealChart).
+ self._init_coordinates(coord_list)
+ coord_string = ' '.join(str(x) for x in self._xx)
+ if coord_string in self._domain._charts_by_coord:
+ raise ValueError("the chart with coordinates " + coord_string +
+ " has already been declared on " +
+ "the {}".format(self._domain))
+ self._domain._charts_by_coord[coord_string] = self
+ #
+ # Additional restrictions on the coordinates
+ self._restrictions = [] # to be set with method add_restrictions()
+ #
+ # The chart is added to the domain's atlas, as well as to all the
+ # atlases of the domain's supersets; moreover the fist defined chart
+ # is considered as the default chart
+ for sd in self._domain._supersets:
+ # the chart is added in the top charts only if its coordinates have
+ # not been used:
+ for chart in sd._atlas:
+ if self._xx == chart._xx:
+ break
+ else:
+ sd._top_charts.append(self)
+ sd._atlas.append(self)
+ if sd._def_chart is None:
+ sd._def_chart = self
+ # The chart is added to the list of the domain's covering charts:
+ self._domain._covering_charts.append(self)
+ # Initialization of the set of charts that are restrictions of the
+ # current chart to subsets of the chart domain:
+ self._subcharts = set([self])
+ # Initialization of the set of charts which the current chart is a
+ # restriction of:
+ self._supercharts = set([self])
+ #
+ self._dom_restrict = {} # dict. of the restrictions of self to
+ # subsets of self._domain, with the
+ # subsets as keys
+
+ def _init_coordinates(self, coord_list):
+ r"""
+ Initialization of the coordinates as symbolic variables.
+
+ This method must be redefined by derived classes in order to take
+ into account specificities (e.g. enforcing real coordinates).
+
+ INPUT:
+
+ - ``coord_list`` -- list of coordinate fields, which items in each
+ field separated by ":"; there are at most 2 items per field:
+ the coordinate name and the coordinate LaTeX symbol
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<z1, z2> = M.chart()
+ sage: X._init_coordinates(['z1', 'z2'])
+ sage: X
+ Chart (M, (z1, z2))
+ sage: X._init_coordinates([r'z1:\zeta_1', r'z2:\zeta_2'])
+ sage: X
+ Chart (M, (z1, z2))
+ sage: latex(X)
+ \left(M,({\zeta_1}, {\zeta_2})\right)
+
+ """
+ xx_list = [] # will contain the coordinates as Sage symbolic variables
+ for coord_field in coord_list:
+ coord_properties = coord_field.split(':')
+ coord_symb = coord_properties[0].strip() # the coordinate symbol
+ # LaTeX symbol:
+ coord_latex = None
+ for prop in coord_properties[1:]:
+ coord_latex = prop.strip()
+ # Construction of the coordinate as some Sage's symbolic variable:
+ coord_var = SR.var(coord_symb, latex_name=coord_latex)
+ xx_list.append(coord_var)
+ self._xx = tuple(xx_list)
+
+ def _repr_(self):
+ r"""
+ String representation of the object.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X
+ Chart (M, (x, y))
+
+ """
+ return 'Chart ({}, {})'.format(self._domain._name, self._xx)
+
+ def _latex_(self):
+ r"""
+ LaTeX representation of the object.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X._latex_()
+ '\\left(M,(x, y)\\right)'
+ sage: Y.<z1, z2> = M.chart(r'z1:\zeta_1 z2:\zeta2')
+ sage: Y._latex_()
+ '\\left(M,({\\zeta_1}, {\\zeta2})\\right)'
+ sage: latex(Y)
+ \left(M,({\zeta_1}, {\zeta2})\right)
+
+ """
+ description = r'\left(' + latex(self._domain).strip() + ',('
+ n = len(self._xx)
+ for i in range(n-1):
+ description += latex(self._xx[i]).strip() + ', '
+ description += latex(self._xx[n-1]).strip() + r')\right)'
+ return description
+
+ def _first_ngens(self, n):
+ r"""
+ Return the list of coordinates.
+
+ This is useful only for the use of 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)"
+
+ """
+ return self[:]
+
+ def __getitem__(self, i):
+ r"""
+ Access to the coordinates.
+
+ INPUT:
+
+ - ``i`` -- index of the coordinate; if the slice ``[:]``, then all
+ the coordinates are returned
+
+ OUTPUT:
+
+ - the coordinate of index ``i`` or all the coordinates (as a tuple) if
+ ``i`` is the slice ``[:]``
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X[0]
+ x
+ sage: X[1]
+ y
+ sage: X[:]
+ (x, y)
+
+ The index range is controlled by the parameter ``start_index``::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological',
+ ....: start_index=1)
+ sage: X.<x,y> = M.chart()
+ sage: X[1]
+ x
+ sage: X[2]
+ y
+ sage: X[:]
+ (x, y)
+
+ We check that slices are properly shifted as well::
+
+ sage: X[2:]
+ (y,)
+ sage: X[:2]
+ (x,)
+ """
+ if isinstance(i, slice):
+ start,stop = i.start,i.stop
+ if start is not None:
+ start -= self._manifold._sindex
+ if stop is not None:
+ stop -= self._manifold._sindex
+ return self._xx[start:stop:i.step]
+ else:
+ return self._xx[i-self._manifold._sindex]
+
+ def __call__(self, point):
+ r"""
+ Return the coordinates of a given point.
+
+ INPUT:
+
+ - ``point`` -- point in the domain of the chart
+
+ OUTPUT:
+
+ - tuple of the coordinates of the point
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: p = M.point((1+i, 2-i), chart=X)
+ sage: X(p)
+ (I + 1, -I + 2)
+ sage: X(M.an_element())
+ (0, 0)
+
+ """
+ return point.coord(self)
+
+ def domain(self):
+ r"""
+ Return the open subset on which the chart is defined.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.domain()
+ 2-dimensional topological manifold M
+ sage: U = M.open_subset('U')
+ sage: Y.<u,v> = U.chart()
+ sage: Y.domain()
+ Open subset U of the 2-dimensional topological manifold M
+
+ """
+ return self._domain
+
+ def manifold(self):
+ r"""
+ Return the manifold on which the chart is defined.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: U = M.open_subset('U')
+ sage: X.<x,y> = U.chart()
+ sage: X.manifold()
+ 2-dimensional topological manifold M
+ sage: X.domain()
+ Open subset U of the 2-dimensional topological manifold M
+
+ """
+ return self._manifold
+
+ def add_restrictions(self, restrictions):
+ r"""
+ Add some restrictions on the coordinates.
+
+ INPUT:
+
+ - ``restrictions`` -- list of restrictions on the
+ coordinates, in addition to the ranges declared by the intervals
+ specified in the chart constructor.
+ A restriction can be any symbolic equality or inequality involving the
+ coordinates, such as x>y or x^2+y^2 != 0. The items of the list
+ ``restrictions`` 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
+ ``restrictions``. For example, ``restrictions`` = [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
+ ``restrictions`` contains only one item, this item can be passed as
+ such, i.e. writing x>y instead of the single element list [x>y].
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.add_restrictions(abs(x) > 1)
+ sage: X.valid_coordinates(2+i, 1)
+ True
+ sage: X.valid_coordinates(i, 1)
+ False
+
+ """
+ if not isinstance(restrictions, list):
+ # case of a single condition or conditions to be combined by "or"
+ restrictions = [restrictions]
+ self._restrictions.extend(restrictions)
+
+ def restrict(self, subset, restrictions=None):
+ r"""
+ Return the restriction of the chart to some open subset of its domain.
+
+ If the current chart is `(U,\varphi)`, a *restriction* (or *subchart*)
+ is a chart `(V,\psi)` such that `V\subset U` and `\psi = \varphi |_V`.
+
+ If such subchart has not been defined yet, it is constructed here.
+
+ The coordinates of the subchart bare the same names as the coordinates
+ of the current chart.
+
+ INPUT:
+
+ - ``subset`` -- open subset `V` of the chart domain `U` (must be an
+ instance of :class:`~sage.manifolds.manifold.TopologicalManifold`)
+ - ``restrictions`` -- (default: ``None``) list of coordinate
+ restrictions defining the subset `V`.
+ A restriction can be any symbolic equality or
+ inequality involving the coordinates, such as x>y or x^2+y^2 != 0.
+ The items of the list ``restrictions`` 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 ``restrictions``. For example, ``restrictions``
+ being [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 ``restrictions`` contains only one item,
+ this item can be passed as such, i.e. writing x>y instead of the
+ single element list [x>y]. Note that the argument ``restrictions``
+ can be omitted if the subchart has been already initialized by a
+ previous call.
+
+ OUTPUT:
+
+ - chart `(V,\psi)`, as an instance of :class:`Chart`.
+
+ EXAMPLES:
+
+ Coordinates on the unit open ball of `\CC^2` as a subchart
+ of the global coordinates of `\CC^2`::
+
+ sage: M = Manifold(2, 'C^2', field='complex', structure='topological')
+ sage: X.<z1, z2> = M.chart()
+ sage: B = M.open_subset('B')
+ sage: X_B = X.restrict(B, abs(z1)^2 + abs(z2)^2 < 1); X_B
+ Chart (B, (z1, z2))
+
+ """
+ if subset == self._domain:
+ return self
+ if subset not in self._dom_restrict:
+ if not subset.is_subset(self._domain):
+ raise ValueError("the specified subset is not a subset " +
+ "of the domain of definition of the chart")
+ coordinates = ""
+ for coord in self._xx:
+ coordinates += repr(coord) + ' '
+ res = type(self)(subset, coordinates)
+ res._restrictions.extend(self._restrictions)
+ # The coordinate restrictions are added to the result chart and
+ # possibly transformed into coordinate bounds:
+ if restrictions is not None:
+ res.add_restrictions(restrictions)
+ # Update of supercharts and subcharts:
+ res._supercharts.update(self._supercharts)
+ for schart in self._supercharts:
+ schart._subcharts.add(res)
+ schart._dom_restrict[subset] = res
+ # Update of domain restrictions:
+ self._dom_restrict[subset] = res
+ return self._dom_restrict[subset]
+
+ def valid_coordinates(self, *coordinates, **kwds):
+ r"""
+ Check whether a tuple of coordinates can be the coordinates of a
+ point in the chart domain.
+
+ INPUT:
+
+ - ``*coordinates`` -- coordinate values
+ - ``**kwds`` -- options:
+
+ - ``parameters=None``, dictionary to set numerical values to
+ some parameters (see example below)
+
+ OUTPUT:
+
+ - ``True`` if the coordinate values are admissible in the chart image,
+ ``False`` otherwise.
+
+ EXAMPLE::
+
+ sage: M = Manifold(2, 'M', field='complex', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.add_restrictions([abs(x)<1, y!=0])
+ sage: X.valid_coordinates(0, i)
+ True
+ sage: X.valid_coordinates(i, 1)
+ False
+ sage: X.valid_coordinates(i/2, 1)
+ True
+ sage: X.valid_coordinates(i/2, 0)
+ False
+ sage: X.valid_coordinates(2, 0)
+ False
+
+ Example of use with the keyword ``parameters`` to set a specific value
+ to a parameter appearing in the coordinate restrictions::
+
+ sage: var('a') # the parameter is a symbolic variable
+ a
+ sage: Y.<u,v> = M.chart()
+ sage: Y.add_restrictions(abs(v)<a)
+ sage: Y.valid_coordinates(1, i, parameters={a: 2}) # setting a=2
+ True
+ sage: Y.valid_coordinates(1, 2*i, parameters={a: 2})
+ False
+
+ """
+ if len(coordinates) != self._domain._dim:
+ return False
+ if 'parameters' in kwds:
+ parameters = kwds['parameters']
+ else:
+ parameters = None
+ # Check of restrictions:
+ if self._restrictions:
+ substitutions = dict(zip(self._xx, coordinates))
+ if parameters:
+ substitutions.update(parameters)
+ for restrict in self._restrictions:
+ if isinstance(restrict, tuple): # case of or conditions
+ combine = False
+ for expr in restrict:
+ combine = combine or bool(expr.subs(substitutions))
+ if not combine:
+ return False
+ else:
+ if not bool(restrict.subs(substitutions)):
+ return False
+ # All tests have been passed:
+ return True
+
+ 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`.
+
+ INPUT:
+
+ - ``other`` -- the chart `(V, \psi)`
+ - ``transformations`` -- tuple (or list) `(Y_1, \ldots, Y_n)`, 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:`CoordChange`
+
+ EXAMPLES:
+
+ Transition map between two stereographic charts on the circle `S^1`::
+
+ sage: M = Manifold(1, 'S^1', structure='topological')
+ 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 topological manifold S^1,
+ Open subset U of the 1-dimensional topological manifold S^1,
+ Open subset V of the 1-dimensional topological manifold S^1,
+ Open subset W of the 1-dimensional topological 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 spherical chart and the Cartesian
+ one on `\RR^2`::
+
+ sage: M = Manifold(2, 'R^2', structure='topological')
+ 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 topological manifold R^2,
+ Open subset U of the 2-dimensional topological 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 CoordChange(chart1, chart2, *transformations)
+
+#*****************************************************************************
+
+class RealChart(Chart):
+ r"""
+ Chart on a topological manifold over `\RR`.
+
+ Given a topological manifold `M` of dimension `n` over `\RR`, a *chart*
+ on `M` is a pair `(U,\varphi)`, where `U` is 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', structure='topological',
+ ....: 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.chart.RealChart'>
+
+ 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', structure='topological',
+ ....: 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
+
+ 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', structure='topological', 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', structure='topological', 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 slice ``[:]``::
+
+ 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 chart map `\varphi` acting 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 topological 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 \neq 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
+
+ """
+ def __init__(self, domain, coordinates='', names=None):
+ r"""
+ Construct a chart on a real topological manifold.
+
+ TESTS::
+
+ sage: forget() # for doctests only
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X
+ Chart (M, (x, y))
+ sage: type(X)
+ <class 'sage.manifolds.chart.RealChart'>
+ sage: assumptions() # assumptions set in X._init_coordinates
+ [x is real, y is real]
+ sage: TestSuite(X).run()
+
+ """
+ Chart.__init__(self, domain, coordinates=coordinates, names=names)
+
+ def _init_coordinates(self, coord_list):
+ r"""
+ Initialization of the coordinates as symbolic variables.
+
+ This method must be redefined by derived classes in order to take
+ into account specificities (e.g. enforcing real coordinates).
+
+ INPUT:
+
+ - ``coord_list`` -- list of coordinate fields, which items in each
+ field separated by ":"; there are at most 3 items per field:
+ the coordinate name, the coordinate LaTeX symbol and the coordinate
+ range
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X._init_coordinates(['x', 'y'])
+ sage: X
+ Chart (M, (x, y))
+ sage: latex(X)
+ \left(M,(x, y)\right)
+ sage: X.coord_range()
+ x: (-oo, +oo); y: (-oo, +oo)
+ sage: X._init_coordinates([r'x1:\xi:(0,1)', r'y1:\eta'])
+ sage: X
+ Chart (M, (x1, y1))
+ sage: latex(X)
+ \left(M,({\xi}, {\eta})\right)
+ sage: X.coord_range()
+ x1: (0, 1); y1: (-oo, +oo)
+
+ """
+ from sage.symbolic.assumptions import assume
+ xx_list = [] # will contain the coordinates as Sage symbolic variables
+ bounds_list = [] # will contain the coordinate bounds
+ for coord_field in coord_list:
+ coord_properties = coord_field.split(':')
+ coord_symb = coord_properties[0].strip() # the coordinate symbol
+ # default values, possibly redefined below:
+ coord_latex = None
+ xmin = -Infinity; xmin_included = False
+ xmax = +Infinity; xmax_included = False
+ # scan of the properties other than the symbol:
+ for prop in coord_properties[1:]:
+ prop1 = prop.strip()
+ delim_min = prop1[0]
+ if delim_min in ['[', ']', '(']:
+ # prop1 is the coordinate's range
+ xmin_str, xmax_str = prop1[1:len(prop1)-1].split(',')
+ if xmin_str not in ['-inf', '-Inf', '-infinity',
+ '-Infinity', '-oo']:
+ xmin = SR(xmin_str)
+ xmin_included = ( delim_min == '[' )
+ if xmax_str not in ['inf', '+inf', 'Inf', '+Inf',
+ 'infinity', '+infinity', 'Infinity',
+ '+Infinity', 'oo', '+oo']:
+ xmax = SR(xmax_str)
+ xmax_included = ( prop1[-1] == ']' )
+ else:
+ # prop1 is the coordinate's LaTeX symbol
+ coord_latex = prop1
+ # Construction of the coordinate as some Sage's symbolic variable:
+ coord_var = SR.var(coord_symb, domain='real',
+ latex_name=coord_latex)
+ assume(coord_var, 'real')
+ if xmin != -Infinity:
+ if xmin_included:
+ assume(coord_var >= xmin)
+ else:
+ assume(coord_var > xmin)
+ if xmax != Infinity:
+ if xmax_included:
+ assume(coord_var <= xmax)
+ else:
+ assume(coord_var < xmax)
+ xx_list.append(coord_var)
+ bounds_list.append(((xmin, xmin_included), (xmax, xmax_included)))
+ self._xx = tuple(xx_list)
+ self._bounds = tuple(bounds_list)
+
+ def coord_bounds(self, i=None):
+ r"""
+ Return the lower and upper bounds of the range of a coordinate.
+
+ For a nicely formatted output, use :meth:`coord_range` instead.
+
+ INPUT:
+
+ - ``i`` -- (default: ``None``) index of the coordinate; if ``None``,
+ the bounds of all the coordinates are returned
+
+ OUTPUT:
+
+ - the coordinate bounds as the tuple
+ ``((xmin, min_included), (xmax, max_included))`` where
+
+ - ``xmin`` is the coordinate lower bound
+ - ``min_included`` is a boolean, indicating whether the coordinate
+ can take the value ``xmin``, i.e. ``xmin`` is a strict lower
+ bound iff ``min_included`` is ``False``
+ - ``xmin`` is the coordinate upper bound
+ - ``max_included`` is a boolean, indicating whether the coordinate
+ can take the value ``xmax``, i.e. ``xmax`` is a strict upper
+ bound iff ``max_included`` is ``False``
+
+ EXAMPLES:
+
+ Some coordinate bounds on a 2-dimensional manifold::
+
+ sage: forget() # for doctests only
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart('x y:[0,1)')
+ sage: c_xy.coord_bounds(0) # x in (-oo,+oo) (the default)
+ ((-Infinity, False), (+Infinity, False))
+ sage: c_xy.coord_bounds(1) # y in [0,1)
+ ((0, True), (1, False))
+ sage: c_xy.coord_bounds()
+ (((-Infinity, False), (+Infinity, False)), ((0, True), (1, False)))
+ sage: c_xy.coord_bounds() == (c_xy.coord_bounds(0), c_xy.coord_bounds(1))
+ True
+
+ The coordinate bounds can also be recovered via the method
+ :meth:`coord_range`::
+
+ sage: c_xy.coord_range()
+ x: (-oo, +oo); y: [0, 1)
+ sage: c_xy.coord_range(y)
+ y: [0, 1)
+
+ or via Sage's function
+ :func:`sage.symbolic.assumptions.assumptions`::
+
+ sage: assumptions(x)
+ [x is real]
+ sage: assumptions(y)
+ [y is real, y >= 0, y < 1]
+
+ """
+ if i is None:
+ return self._bounds
+ else:
+ return self._bounds[i-self._manifold._sindex]
+
+ def coord_range(self, xx=None):
+ r"""
+ Display the range of a coordinate (or all coordinates), as an
+ interval.
+
+ INPUT:
+
+ - ``xx`` -- (default: ``None``) symbolic expression corresponding
+ to a coordinate of the current chart; if ``None``, the ranges of
+ all coordinates are displayed
+
+ EXAMPLES:
+
+ Ranges of coordinates on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.coord_range()
+ x: (-oo, +oo); y: (-oo, +oo)
+ sage: X.coord_range(x)
+ x: (-oo, +oo)
+ sage: U = M.open_subset('U', coord_def={X: [x>1, y<pi]})
+ sage: XU = X.restrict(U) # restriction of chart X to U
+ sage: XU.coord_range()
+ x: (1, +oo); y: (-oo, pi)
+ sage: XU.coord_range(x)
+ x: (1, +oo)
+ sage: XU.coord_range(y)
+ y: (-oo, pi)
+
+ The output is LaTeX-formatted for the notebook::
+
+ sage: latex(XU.coord_range(y))
+ y :\ \left( -\infty, \pi \right)
+
+ """
+ from sage.tensor.modules.format_utilities import FormattedExpansion
+ def _display_coord_range(self, xx, rtxt, rlatex):
+ ind = self._xx.index(xx)
+ bounds = self._bounds[ind]
+ rtxt += "{}: ".format(xx)
+ rlatex += latex(xx) + r":\ "
+ if bounds[0][1]:
+ rtxt += "["
+ rlatex += r"\left["
+ else:
+ rtxt += "("
+ rlatex += r"\left("
+ xmin = bounds[0][0]
+ if xmin == -Infinity:
+ rtxt += "-oo, "
+ rlatex += r"-\infty,"
+ else:
+ rtxt += "{}, ".format(xmin)
+ rlatex += latex(xmin) + ","
+ xmax = bounds[1][0]
+ if xmax == Infinity:
+ rtxt += "+oo"
+ rlatex += r"+\infty"
+ else:
+ rtxt += "{}".format(xmax)
+ rlatex += latex(xmax)
+ if bounds[1][1]:
+ rtxt += "]"
+ rlatex += r"\right]"
+ else:
+ rtxt += ")"
+ rlatex += r"\right)"
+ return rtxt, rlatex
+ resu_txt = ""
+ resu_latex = ""
+ if xx is None:
+ for x in self._xx:
+ if resu_txt != "":
+ resu_txt += "; "
+ resu_latex += r";\quad "
+ resu_txt, resu_latex = _display_coord_range(self, x, resu_txt,
+ resu_latex)
+ else:
+ resu_txt, resu_latex = _display_coord_range(self, xx, resu_txt,
+ resu_latex)
+ return FormattedExpansion(resu_txt, resu_latex)
+
+
+ def add_restrictions(self, restrictions):
+ r"""
+ Add some restrictions on the coordinates.
+
+ INPUT:
+
+ - ``restrictions`` -- list of restrictions on the
+ coordinates, in addition to the ranges declared by the intervals
+ specified in the chart constructor.
+ A restriction can be any symbolic equality or inequality involving the
+ coordinates, such as x>y or x^2+y^2 != 0. The items of the list
+ ``restrictions`` 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
+ ``restrictions``. For example, ``restrictions`` = [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
+ ``restrictions`` contains only one item, this item can be passed as
+ such, i.e. writing x>y instead of the single element list [x>y].
+
+ EXAMPLES:
+
+ Cartesian coordinates on the open unit disc in $\RR^2$::
+
+ sage: M = Manifold(2, 'M', structure='topological') # the open unit disc
+ sage: X.<x,y> = M.chart()
+ sage: X.add_restrictions(x^2+y^2<1)
+ sage: X.valid_coordinates(0,2)
+ False
+ sage: X.valid_coordinates(0,1/3)
+ True
+
+ The restrictions are transmitted to subcharts::
+
+ sage: A = M.open_subset('A') # annulus 1/2 < r < 1
+ sage: X_A = X.restrict(A, x^2+y^2 > 1/4)
+ sage: X_A._restrictions
+ [x^2 + y^2 < 1, x^2 + y^2 > (1/4)]
+ sage: X_A.valid_coordinates(0,1/3)
+ False
+ sage: X_A.valid_coordinates(2/3,1/3)
+ True
+
+ If appropriate, the restrictions are transformed into bounds on
+ the coordinate ranges::
+
+ sage: U = M.open_subset('U')
+ sage: X_U = X.restrict(U)
+ sage: X_U.coord_range()
+ x: (-oo, +oo); y: (-oo, +oo)
+ sage: X_U.add_restrictions([x<0, y>1/2])
+ sage: X_U.coord_range()
+ x: (-oo, 0); y: (1/2, +oo)
+
+ """
+ import operator
+ if not isinstance(restrictions, list):
+ # case of a single condition or conditions to be combined by "or"
+ restrictions = [restrictions]
+ self._restrictions.extend(restrictions)
+ # Update of the coordinate bounds from the restrictions:
+ bounds = list(self._bounds) # convert to a list for modifications
+ new_restrictions = []
+ for restrict in self._restrictions:
+ restrict_used = False # determines whether restrict is used
+ # to set some coordinate bound
+ if not isinstance(restrict, tuple): # case of 'or' conditions
+ # excluded
+ operands = restrict.operands()
+ left = operands[0]
+ right = operands[1]
+ right_var = right.variables()
+ if left in self._xx:
+ # the l.h.s. of the restriction is a single
+ # coordinate
+ right_coord = [coord for coord in self._xx
+ if coord in right_var]
+ if not right_coord:
+ # there is no other coordinate in the r.h.s.
+ ind = self._xx.index(left)
+ left_bounds = list(bounds[ind])
+ oper = restrict.operator()
+ oinf = left_bounds[0][0] # old coord inf
+ osup = left_bounds[1][0] # old coord sup
+ if oper == operator.lt:
+ if osup == Infinity or right <= osup:
+ left_bounds[1] = (right, False)
+ restrict_used = True
+ elif oper == operator.le:
+ if osup == Infinity or right < osup:
+ left_bounds[1] = (right, True)
+ restrict_used = True
+ elif oper == operator.gt:
+ if oinf == -Infinity or right >= oinf:
+ left_bounds[0] = (right, False)
+ restrict_used = True
+ elif oper == operator.ge:
+ if oinf == -Infinity or right > oinf:
+ left_bounds[0] = (right, True)
+ restrict_used = True
+ bounds[ind] = tuple(left_bounds)
+ if not restrict_used:
+ # if restrict has not been used to set a coordinate bound
+ # it is maintained in the list of restrictions:
+ new_restrictions.append(restrict)
+ self._bounds = tuple(bounds)
+ self._restrictions = new_restrictions
+
+ def restrict(self, subset, restrictions=None):
+ r"""
+ Return the restriction of the chart to some open subset of its domain.
+
+ If the current chart is `(U,\varphi)`, a *restriction* (or *subchart*)
+ is a chart `(V,\psi)` such that `V\subset U` and `\psi = \varphi |_V`.
+
+ If such subchart has not been defined yet, it is constructed here.
+
+ The coordinates of the subchart bare the same names as the coordinates
+ of the current chart.
+
+ INPUT:
+
+ - ``subset`` -- open subset `V` of the chart domain `U` (must be an
+ instance of :class:`~sage.manifolds.manifold.TopologicalManifold`)
+ - ``restrictions`` -- (default: ``None``) list of coordinate
+ restrictions defining the subset `V`.
+ A restriction can be any symbolic equality or
+ inequality involving the coordinates, such as x>y or x^2+y^2 != 0.
+ The items of the list ``restrictions`` 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 ``restrictions``. For example, ``restrictions``
+ being [x>y, (x!=0, y!=0), z^2<x] means (x>