summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Gourgoulhon <eric.gourgoulhon@obspm.fr>2016-05-06 15:19:22 +0200
committerEric Gourgoulhon <eric.gourgoulhon@obspm.fr>2016-05-06 15:19:22 +0200
commit7b3dab3d7bab19d91e825a4a91e4102a770e9e94 (patch)
tree76f5d9c400ef824a8ffe59aabf2ae4ed0dc3b157
parentUpdated SageMath version to 7.2.rc0 (diff)
parentFixing a (essentially trivial) doctest failure. (diff)
Merge branch 'public/manifolds/top_manif_scalar_fields' of git://trac.sagemath.org/sage into Sage 7.2.rc0
-rw-r--r--src/doc/en/reference/manifolds/chart.rst11
-rw-r--r--src/doc/en/reference/manifolds/index.rst4
-rw-r--r--src/doc/en/reference/manifolds/manifold.rst4
-rw-r--r--src/doc/en/reference/manifolds/scalarfield.rst9
-rw-r--r--src/sage/doctest/sources.py1
-rw-r--r--src/sage/manifolds/chart.py319
-rw-r--r--src/sage/manifolds/coord_func.py1451
-rw-r--r--src/sage/manifolds/coord_func_symb.py1762
-rw-r--r--src/sage/manifolds/manifold.py354
-rw-r--r--src/sage/manifolds/point.py4
-rw-r--r--src/sage/manifolds/scalarfield.py2773
-rw-r--r--src/sage/manifolds/scalarfield_algebra.py621
-rw-r--r--src/sage/manifolds/structure.py3
-rw-r--r--src/sage/manifolds/utilities.py837
14 files changed, 8116 insertions, 37 deletions
diff --git a/src/doc/en/reference/manifolds/chart.rst b/src/doc/en/reference/manifolds/chart.rst
new file mode 100644
index 00000000..0ba9dc6
--- /dev/null
+++ b/src/doc/en/reference/manifolds/chart.rst
@@ -0,0 +1,11 @@
+Coordinate Charts
+=================
+
+.. toctree::
+ :maxdepth: 2
+
+ sage/manifolds/chart
+
+ sage/manifolds/coord_func
+
+ sage/manifolds/coord_func_symb
diff --git a/src/doc/en/reference/manifolds/index.rst b/src/doc/en/reference/manifolds/index.rst
index f0d5932..13995d6 100644
--- a/src/doc/en/reference/manifolds/index.rst
+++ b/src/doc/en/reference/manifolds/index.rst
@@ -11,8 +11,10 @@ More documentation (in particular example worksheets) can be found
`here <http://sagemanifolds.obspm.fr/documentation.html>`_.
.. toctree::
- :maxdepth: 2
+ :maxdepth: 3
manifold
+ sage/manifolds/utilities
+
.. include:: ../footer.txt
diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst
index d9364a3..3ac4f04 100644
--- a/src/doc/en/reference/manifolds/manifold.rst
+++ b/src/doc/en/reference/manifolds/manifold.rst
@@ -12,4 +12,6 @@ Topological Manifolds
sage/manifolds/point
- sage/manifolds/chart
+ chart
+
+ scalarfield
diff --git a/src/doc/en/reference/manifolds/scalarfield.rst b/src/doc/en/reference/manifolds/scalarfield.rst
new file mode 100644
index 00000000..3ba7a2e
--- /dev/null
+++ b/src/doc/en/reference/manifolds/scalarfield.rst
@@ -0,0 +1,9 @@
+Scalar Fields
+=============
+
+.. toctree::
+ :maxdepth: 2
+
+ sage/manifolds/scalarfield_algebra
+
+ sage/manifolds/scalarfield
diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py
index afd2562..67d2c5f 100644
--- a/src/sage/doctest/sources.py
+++ b/src/sage/doctest/sources.py
@@ -717,6 +717,7 @@ class FileDocTestSource(DocTestSource):
There are 8 tests in sage/combinat/root_system/type_G.py that are not being run
There are 3 unexpected tests being run in sage/doctest/parsing.py
There are 1 unexpected tests being run in sage/doctest/reporting.py
+ There are 15 tests in sage/manifolds/manifold.py that are not being run
There are 3 tests in sage/rings/invariant_theory.py that are not being run
sage: os.chdir(cwd)
"""
diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py
index 6a6194b..7e1610b 100644
--- a/src/sage/manifolds/chart.py
+++ b/src/sage/manifolds/chart.py
@@ -16,10 +16,10 @@ AUTHORS:
REFERENCES:
-- Chap. 2 of [Lee11]_ J.M. Lee: *Introduction to Topological Manifolds*,
+- 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*,
+- Chap. 1 of [Lee13]_ \J.M. Lee : *Introduction to Smooth Manifolds*,
2nd ed., Springer (New York) (2013)
"""
@@ -40,6 +40,7 @@ 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
+from sage.manifolds.coord_func_symb import CoordFunctionSymb
class Chart(UniqueRepresentation, SageObject):
r"""
@@ -309,6 +310,17 @@ class Chart(UniqueRepresentation, SageObject):
self._dom_restrict = {} # dict. of the restrictions of self to
# subsets of self._domain, with the
# subsets as keys
+ # The null and one functions of the coordinates:
+ base_field_type = self._domain.base_field_type()
+ # Expression in self of the zero and one scalar fields of open sets
+ # containing the domain of self:
+ for dom in self._domain._supersets:
+ if hasattr(dom, '_zero_scalar_field'):
+ # dom is an open set
+ dom._zero_scalar_field._express[self] = self.function_ring().zero()
+ if hasattr(dom, '_one_scalar_field'):
+ # dom is an open set
+ dom._one_scalar_field._express[self] = self.function_ring().one()
def _init_coordinates(self, coord_list):
r"""
@@ -825,6 +837,252 @@ class Chart(UniqueRepresentation, SageObject):
transformations = [transformations]
return CoordChange(chart1, chart2, *transformations)
+ def function_ring(self):
+ """
+ Return the ring of coordinate functions on ``self``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.function_ring()
+ Ring of coordinate functions on Chart (M, (x, y))
+ """
+ from sage.manifolds.coord_func_symb import CoordFunctionSymbRing
+ return CoordFunctionSymbRing(self)
+
+ def function(self, expression):
+ r"""
+ Define a coordinate function to the base field.
+
+ If the current chart belongs to the atlas of a `n`-dimensional manifold
+ over a topological field `K`, a *coordinate function* is a map
+
+ .. MATH::
+
+ \begin{array}{cccc}
+ f:& V\subset K^n & \longrightarrow & K \\
+ & (x^1,\ldots, x^n) & \longmapsto & f(x^1,\ldots, x^n),
+ \end{array}
+
+ where `V` is the chart codomain and `(x^1,\ldots, x^n)` are the
+ chart coordinates.
+
+ The coordinate function can be either a symbolic one or a numerical
+ one, depending on the parameter ``expression`` (see below).
+
+ See :class:`~sage.manifolds.coord_func.CoordFunction`
+ and :class:`~sage.manifolds.coord_func_symb.CoordFunctionSymb`
+ for a complete documentation.
+
+ INPUT:
+
+ - ``expression`` -- material defining the coordinate function; it can
+ be either:
+
+ - a symbolic expression involving the chart coordinates, to represent
+ `f(x^1,\ldots, x^n)`
+ - a string representing the name of a file where the data
+ to construct a numerical coordinate function is stored
+
+ OUTPUT:
+
+ - instance of a subclass of the base class
+ :class:`~sage.manifolds.coord_func.CoordFunction`
+ representing the coordinate function `f`; this is
+ :class:`~sage.manifolds.coord_func_symb.CoordFunctionSymb` if
+ if ``expression`` is a symbolic expression.
+
+ EXAMPLES:
+
+ A symbolic coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(sin(x*y))
+ sage: f
+ sin(x*y)
+ sage: type(f)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: f.display()
+ (x, y) |--> sin(x*y)
+ sage: f(2,3)
+ sin(6)
+
+ """
+ if isinstance(expression, str):
+ raise NotImplementedError("numerical coordinate function not " +
+ "implemented yet")
+ else:
+ return self.function_ring()(expression)
+
+ def zero_function(self):
+ r"""
+ Return the zero function of the coordinates.
+
+ If the current chart belongs to the atlas of a `n`-dimensional manifold
+ over a topological field `K`, the zero coordinate function is the map
+
+ .. MATH::
+
+ \begin{array}{cccc}
+ f:& V\subset K^n & \longrightarrow & K \\
+ & (x^1,\ldots, x^n) & \longmapsto & 0,
+ \end{array}
+
+ where `V` is the chart codomain.
+
+ See class :class:`~sage.manifolds.coord_func_symb.CoorFunctionSymb`
+ for a complete documentation.
+ OUTPUT:
+
+ - instance of class
+ :class:`~sage.manifolds.coord_func_symb.CoorFunctionSymb`
+ representing the zero coordinate function `f`.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.zero_function()
+ 0
+ sage: X.zero_function().display()
+ (x, y) |--> 0
+ sage: type(X.zero_function())
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+
+ The result is cached::
+
+ sage: X.zero_function() is X.zero_function()
+ True
+
+ Zero function on a p-adic manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological', field=Qp(5)); M
+ 2-dimensional topological manifold M over the 5-adic Field with
+ capped relative precision 20
+ sage: X.<x,y> = M.chart()
+ sage: X.zero_function()
+ 0
+ sage: X.zero_function().display()
+ (x, y) |--> 0
+
+ """
+ return self.function_ring().zero()
+
+ def one_function(self):
+ r"""
+ Return the constant function of the coordinates equal to one.
+
+ If the current chart belongs to the atlas of a `n`-dimensional manifold
+ over a topological field `K`, the "one" coordinate function is the map
+
+ .. MATH::
+
+ \begin{array}{cccc}
+ f:& V\subset K^n & \longrightarrow & K \\
+ & (x^1,\ldots, x^n) & \longmapsto & 1,
+ \end{array}
+
+ where `V` is the chart codomain.
+
+ See class :class:`~sage.manifolds.coord_func_symb.CoorFunctionSymb`
+ for a complete documentation.
+ OUTPUT:
+
+ - instance of class
+ :class:`~sage.manifolds.coord_func_symb.CoorFunctionSymb`
+ representing the one coordinate function `f`.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: X.one_function()
+ 1
+ sage: X.one_function().display()
+ (x, y) |--> 1
+ sage: type(X.one_function())
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+
+ The result is cached::
+
+ sage: X.one_function() is X.one_function()
+ True
+
+ One function on a p-adic manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological', field=Qp(5)); M
+ 2-dimensional topological manifold M over the 5-adic Field with
+ capped relative precision 20
+ sage: X.<x,y> = M.chart()
+ sage: X.one_function()
+ 1 + O(5^20)
+ sage: X.one_function().display()
+ (x, y) |--> 1 + O(5^20)
+
+ """
+ return self.function_ring().one()
+
+
+ def multifunction(self, *expressions):
+ r"""
+ Define a coordinate function to some Cartesian power of the base field.
+
+ If `n` and `m` are two positive integers and `(U, \varphi)` is a
+ chart on a topological manifold `M` of dimension `n` over a
+ topological field `K`, a *multi-coordinate function* associated
+ to `(U,\varphi)` is a map
+
+ .. MATH::
+
+ \begin{array}{llcl}
+ f:& V \subset K^n & \longrightarrow & K^m \\
+ & (x^1, \ldots, x^n) & \longmapsto & (f_1(x^1, \ldots, x^n),
+ \ldots, f_m(x^1, \ldots, x^n)),
+ \end{array}
+
+ where `V` is the codomain of `\varphi`. In other words, `f` is a
+ `K^m`-valued function of the coordinates associated to the chart
+ `(U,\varphi)`.
+
+ See :class:`~sage.manifolds.coord_func.MultiCoordFunction` for a
+ complete documentation.
+
+ INPUT:
+
+ - ``expressions`` -- list (or tuple) of `m` elements to construct the
+ coordinate functions `f_i` (`1\leq i \leq m`); for
+ symbolic coordinate functions, this must be symbolic expressions
+ involving the chart coordinates, while for numerical coordinate
+ functions, this must be data file names
+
+ OUTPUT:
+
+ - a :class:`~sage.manifolds.coord_func.MultiCoordFunction`
+ representing `f`
+
+ EXAMPLES:
+
+ Function of two coordinates with values in `\RR^3`::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x+y, sin(x*y), x^2 + 3*y); f
+ Coordinate functions (x + y, sin(x*y), x^2 + 3*y) on the Chart (M, (x, y))
+ sage: f(2,3)
+ (5, sin(6), 13)
+
+ TESTS::
+
+ sage: type(f)
+ <class 'sage.manifolds.coord_func.MultiCoordFunction'>
+
+ """
+ from sage.manifolds.coord_func import MultiCoordFunction
+ return MultiCoordFunction(self, expressions)
+
+
#*****************************************************************************
class RealChart(Chart):
@@ -1412,6 +1670,7 @@ class RealChart(Chart):
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.
@@ -1599,7 +1858,6 @@ class RealChart(Chart):
# All tests have been passed:
return True
-
#*****************************************************************************
class CoordChange(SageObject):
@@ -1669,10 +1927,9 @@ class CoordChange(SageObject):
+ "must be provided")
self._chart1 = chart1
self._chart2 = chart2
- #*# when MultiCoordFunction will be implemented (trac #18640):
- # self._transf = chart1.multifunction(*transformations)
- #*# for now:
- self._transf = transformations
+ # The coordinate transformations are implemented via the class
+ # MultiCoordFunction:
+ self._transf = chart1.multifunction(*transformations)
self._inverse = None
# If the two charts are on the same open subset, the coordinate change
# is added to the subset (and supersets) dictionary:
@@ -1791,12 +2048,7 @@ class CoordChange(SageObject):
(3, -1)
"""
- #*# When MultiCoordFunction is implemented (trac #18640):
- # return self._transf(*coords)
- #*# for now:
- substitutions = {self._chart1._xx[j]: coords[j] for j in range(self._n1)}
- return tuple([self._transf[i].subs(substitutions).simplify_full()
- for i in range(self._n2)])
+ return self._transf(*coords)
def inverse(self):
r"""
@@ -1832,6 +2084,8 @@ class CoordChange(SageObject):
"""
from sage.symbolic.relation import solve
+ from sage.manifolds.utilities import simplify_chain_real, \
+ simplify_chain_generic
if self._inverse is not None:
return self._inverse
# The computation is necessary:
@@ -1857,10 +2111,7 @@ class CoordChange(SageObject):
coord_domain[i] = 'positive'
xp2 = [ SR.var('xxxx' + str(i), domain=coord_domain[i])
for i in range(n2) ]
- #*# when MultiCoordFunction will be implemented (trac #18640):
- # xx2 = self._transf.expr()
- #*# for now:
- xx2 = self._transf
+ xx2 = self._transf.expr()
equations = [xp2[i] == xx2[i] for i in range(n2)]
try:
solutions = solve(equations, *x1, solution_dict=True)
@@ -1871,6 +2122,14 @@ class CoordChange(SageObject):
if len(solutions) == 1:
x2_to_x1 = [solutions[0][x1[i]].subs(substitutions)
for i in range(n1)]
+ for transf in x2_to_x1:
+ try:
+ if self._domain.base_field_type() == 'real':
+ transf = simplify_chain_real(transf)
+ else:
+ transf = simplify_chain_generic(transf)
+ except AttributeError:
+ pass
else:
list_x2_to_x1 = []
for sol in solutions:
@@ -1879,6 +2138,14 @@ class CoordChange(SageObject):
"set_inverse() to set the inverse " +
"manually")
x2_to_x1 = [sol[x1[i]].subs(substitutions) for i in range(n1)]
+ for transf in x2_to_x1:
+ try:
+ if self._domain.base_field_type() == 'real':
+ transf = simplify_chain_real(transf)
+ else:
+ transf = simplify_chain_generic(transf)
+ except AttributeError:
+ pass
if self._chart1.valid_coordinates(*x2_to_x1):
list_x2_to_x1.append(x2_to_x1)
if len(list_x2_to_x1) == 0:
@@ -1938,7 +2205,7 @@ class CoordChange(SageObject):
sage: spher_to_cart.set_inverse(sqrt(x^3+y^2), atan2(y,x), verbose=True)
Check of the inverse coordinate transformation:
- r == sqrt(r^3*cos(ph)^3 + r^2*sin(ph)^2)
+ r == sqrt(r*cos(ph)^3 + sin(ph)^2)*r
ph == arctan2(r*sin(ph), r*cos(ph))
x == sqrt(x^3 + y^2)*x/sqrt(x^2 + y^2)
y == sqrt(x^3 + y^2)*y/sqrt(x^2 + y^2)
@@ -1992,10 +2259,7 @@ class CoordChange(SageObject):
raise ValueError("composition not possible: " +
"{} is different from {}".format(other._chart2,
other._chart1))
- #*# when MultiCoordFunction will be implemented (trac #18640):
- # transf = self._transf(*(other._transf.expr()))
- #*# for now:
- transf = self(*(other._transf))
+ transf = self._transf(*(other._transf.expr()))
return type(self)(other._chart1, self._chart2, *transf)
def restrict(self, dom1, dom2=None):
@@ -2038,12 +2302,8 @@ class CoordChange(SageObject):
ch2 = self._chart2.restrict(dom2)
if (ch1, ch2) in dom1.coord_changes():
return dom1.coord_changes()[(ch1,ch2)]
- #*# when MultiCoordFunction will be implemented (trac #18640):
- # return type(self)(self._chart1.restrict(dom1),
- # self._chart2.restrict(dom2), *(self._transf.expr()))
- #*# for now:
return type(self)(self._chart1.restrict(dom1),
- self._chart2.restrict(dom2), *(self._transf))
+ self._chart2.restrict(dom2), *(self._transf.expr()))
def display(self):
r"""
@@ -2079,10 +2339,7 @@ class CoordChange(SageObject):
from sage.tensor.modules.format_utilities import FormattedExpansion
coords2 = self._chart2[:]
n2 = len(coords2)
- #*# when MultiCoordFunction will be implemented (trac #18640):
- # expr = self._transf.expr()
- #*# for now:
- expr = self._transf
+ expr = self._transf.expr()
rtxt = ""
if n2 == 1:
rlatex = r"\begin{array}{lcl}"
diff --git a/src/sage/manifolds/coord_func.py b/src/sage/manifolds/coord_func.py
new file mode 100644
index 00000000..5bb4712
--- /dev/null
+++ b/src/sage/manifolds/coord_func.py
@@ -0,0 +1,1451 @@
+r"""
+Coordinate Functions
+
+In the context of a topological manifold `M` over a topological field `K`,
+a *coordinate function* is a function from a chart codomain
+to `K`. In other words, a coordinate function is a `K`-valued function of
+the coordinates associated to some chart.
+
+More precisely, let `(U, \varphi)` be a chart on `M`, i.e. `U` is an open
+subset of `M` and `\varphi: U \to V \subset K^n` is a homeomorphism
+from `U` to an open subset `V` of `K^n`. A *coordinate function associated
+to the chart* `(U, \varphi)` is a function
+
+.. MATH::
+
+ \begin{array}{cccc}
+ f:& V\subset K^n & \longrightarrow & K \\
+ & (x^1, \ldots, x^n) & \longmapsto & f(x^1, \ldots, x^n)
+ \end{array}
+
+Coordinate functions are implemented by derived classes of the abstract base
+class :class:`CoordFunction`.
+
+The class :class:`MultiCoordFunction` implements `K^m`-valued functions of
+the coordinates of a chart, with `m` a positive integer.
+
+AUTHORS:
+
+- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
+- Travis Scrimshaw (2016) : make :class:`CoordFunction` inheritate from
+ :class:`~sage.structure.element.AlgebraElement`
+
+"""
+#*****************************************************************************
+# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
+# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
+# Copyright (C) 2016 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.misc.abstract_method import abstract_method
+from sage.misc.cachefunc import cached_method
+from sage.structure.element import AlgebraElement
+from sage.structure.sage_object import SageObject
+
+class CoordFunction(AlgebraElement):
+ r"""
+ Abstract base class for coordinate functions.
+
+ If `(U, \varphi)` is a chart on a topological manifold `M` of
+ dimension `n` over a topological field `K`, a *coordinate function*
+ associated to `(U, \varphi)` is a map `f: V \subset K^n \to K`, where
+ `V` is the codomain of `\varphi`. In other words, `f` is a `K`-valued
+ function of the coordinates associated to the chart `(U, \varphi)`.
+
+ The class :class:`CoordFunction` is an abstract one. Specific
+ coordinate functions must be implemented by derived classes, like
+ :class:`~sage.manifolds.coord_func_symb.CoordFunctionSymb` for
+ symbolic coordinate functions.
+
+ INPUT:
+
+ - ``parent`` -- the algebra of coordinate functions on a given chart
+
+ """
+ def __init__(self, parent):
+ r"""
+ Initialize ``self``.
+
+ TEST::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+
+ """
+ AlgebraElement.__init__(self, parent)
+ self._nc = len(parent._chart[:]) # number of coordinates
+
+ # ----------------------------------------------------------------
+ # Methods that do not need to be re-implemented by derived classes
+ # ----------------------------------------------------------------
+
+ def chart(self):
+ r"""
+ Return the chart with respect to which ``self`` is defined.
+
+ OUTPUT:
+
+ - a :class:`~sage.manifolds.chart.Chart`
+
+ EXAMPLE::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(1+x+y^2)
+ sage: f.chart()
+ Chart (M, (x, y))
+ sage: f.chart() is X
+ True
+
+ """
+ return self.parent()._chart
+
+ def scalar_field(self, name=None, latex_name=None):
+ r"""
+ Construct the scalar field that has ``self`` as
+ coordinate expression.
+
+ The domain of the scalar field is the open subset covered by the
+ chart on which ``self`` is defined.
+
+ INPUT:
+
+ - ``name`` -- (default: ``None``) name given to the scalar field
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
+ scalar field; if ``None``, the LaTeX symbol is set to ``name``
+
+ OUTPUT:
+
+ - a :class:`~sage.manifolds.scalarfield.ScalarField`
+
+ EXAMPLES:
+
+ Construction of a scalar field on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart()
+ sage: fc = c_xy.function(x+2*y^3)
+ sage: f = fc.scalar_field() ; f
+ Scalar field on the 2-dimensional topological manifold M
+ sage: f.display()
+ M --> R
+ (x, y) |--> 2*y^3 + x
+ sage: f.coord_function(c_xy) is fc
+ True
+
+ """
+ alg = self.parent()._chart.domain().scalar_field_algebra()
+ return alg.element_class(alg, coord_expression={self.parent()._chart: self},
+ name=name, latex_name=latex_name)
+
+ # TODO: This should be abstract up to SageObject at some point - TCS
+ def __ne__(self, other):
+ r"""
+ Inequality operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - ``True`` if ``self`` is different from ``other``, or ``False``
+ otherwise
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y)
+ sage: g = X.function(x*y)
+ sage: f != g
+ True
+ sage: h = X.function(x+y)
+ sage: f != h
+ False
+
+ """
+ return not (self == other)
+
+ # --------------------------------------------
+ # Methods to be implemented by derived classes
+ # --------------------------------------------
+
+ @abstract_method
+ def _repr_(self):
+ r"""
+ String representation of the object.
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f._repr_()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method _repr_ at 0x...>
+ """
+
+ @abstract_method
+ def _latex_(self):
+ r"""
+ LaTeX representation of the object.
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f._latex_()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method _latex_ at 0x...>
+ """
+
+ @abstract_method
+ def display(self):
+ r"""
+ Display the function in arrow notation.
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.display()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method display at 0x...>
+ """
+
+ disp = display
+
+ @abstract_method
+ def expr(self):
+ r"""
+ Return some data that, along with the chart, is sufficient to
+ reconstruct the object.
+
+ For a symbolic coordinate function, this returns the symbol
+ expression representing the function (see
+ :meth:`sage.manifolds.coord_func_symb.CoordFunctionSymb.expr`)
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.expr()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method expr at 0x...>
+
+ """
+
+ @abstract_method
+ def __call__(self, *coords, **options):
+ r"""
+ Compute the value of the function at specified coordinates.
+
+ INPUT:
+
+ - ``*coords`` -- list of coordinates `(x^1, \ldots ,x^n)`,
+ where the function `f` is to be evaluated
+ - ``**options`` -- options to control the computation (e.g.
+ simplification options)
+
+ OUTPUT:
+
+ - the value `f(x^1, \ldots, x^n)`, where `f` is the current
+ coordinate function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.__call__(2,-3)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method __call__ at 0x...>
+
+ """
+
+ @abstract_method
+ def is_zero(self):
+ r"""
+ Return ``True`` if the function is zero and ``False`` otherwise.
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.is_zero()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method is_zero at 0x...>
+
+ """
+
+ @abstract_method
+ def copy(self):
+ r"""
+ Return an exact copy of the object.
+
+ OUTPUT:
+
+ - an instance of :class:`CoordFunction`
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.copy()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method copy at 0x...>
+
+ """
+
+ @abstract_method
+ def diff(self, coord):
+ r"""
+ Return the partial derivative of ``self`` with respect to a
+ coordinate.
+
+ INPUT:
+
+ - ``coord`` -- either the coordinate `x^i` with respect
+ to which the derivative of the coordinate function `f` is to be
+ taken, or the index `i` labelling this coordinate (with the
+ index convention defined on the chart domain via the parameter
+ ``start_index``)
+
+ OUTPUT:
+
+ - instance of :class:`CoordFunction` representing the partial
+ derivative `\frac{\partial f}{\partial x^i}`
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.diff(x)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method diff at 0x...>
+
+ """
+
+ @abstract_method
+ def __invert__(self):
+ r"""
+ Inverse operator.
+
+ If `f` denotes the current coordinate function and `K` the topological
+ field over which the manifold is defined, the *inverse* of `f` is the
+ coordinate function `1 / f`, where `1` of the multiplicative identity
+ of `K`.
+
+ OUTPUT:
+
+ - the inverse of ``self``
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.__invert__()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method __invert__ at 0x...>
+
+ """
+
+ @abstract_method
+ def _add_(self, other):
+ r"""
+ Addition operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the addition of ``self`` and
+ ``other``
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f._add_(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method _add_ at 0x...>
+
+ """
+
+ @abstract_method
+ def _sub_(self, other):
+ r"""
+ Subtraction operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the subtraction of ``other`` from
+ ``self``
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f._sub_(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method _sub_ at 0x...>
+
+ """
+
+ @abstract_method
+ def _mul_(self, other):
+ r"""
+ Multiplication operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the multiplication of ``self`` by
+ ``other``
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f._mul_(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method _mul_ at 0x...>
+
+ """
+
+ @abstract_method
+ def _div_(self, other):
+ r"""
+ Division operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the division of ``self`` by
+ ``other``
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f._div_(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method _div_ at 0x...>
+
+ """
+
+ @abstract_method
+ def exp(self):
+ r"""
+ Exponential of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\exp(f)`, where `f` is the current coordinate
+ function.
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.exp()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method exp at 0x...>
+
+ """
+
+ @abstract_method
+ def log(self, base=None):
+ r"""
+ Logarithm of ``self``.
+
+ INPUT:
+
+ - ``base`` -- (default: ``None``) base of the logarithm; if None, the
+ natural logarithm (i.e. logarithm to base e) is returned
+
+ OUTPUT:
+
+ - coordinate function `\log_a(f)`, where `f` is the current coordinate
+ function and `a` is the base
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.log()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method log at 0x...>
+
+ """
+
+ @abstract_method
+ def __pow__(self, exponent):
+ r"""
+ Power of ``self``.
+
+ INPUT:
+
+ - ``exponent`` -- the exponent
+
+ OUTPUT:
+
+ - coordinate function `f^a`, where `f` is the current coordinate
+ function and `a` is the exponent
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.__pow__(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method __pow__ at 0x...>
+
+ """
+
+ @abstract_method
+ def sqrt(self):
+ r"""
+ Square root of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\sqrt{f}`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.sqrt()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method sqrt at 0x...>
+
+ """
+
+ @abstract_method
+ def cos(self):
+ r"""
+ Cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\cos(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.cos()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method cos at 0x...>
+
+ """
+
+ @abstract_method
+ def sin(self):
+ r"""
+ Sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\sin(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.sin()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method sin at 0x...>
+
+ """
+
+ @abstract_method
+ def tan(self):
+ r"""
+ Tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\tan(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.tan()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method tan at 0x...>
+
+ """
+
+ @abstract_method
+ def arccos(self):
+ r"""
+ Arc cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\arccos(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.arccos()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method arccos at 0x...>
+
+ """
+
+ @abstract_method
+ def arcsin(self):
+ r"""
+ Arc sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\arcsin(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.arcsin()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method arcsin at 0x...>
+
+ """
+
+ @abstract_method
+ def arctan(self):
+ r"""
+ Arc tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\arctan(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.arctan()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method arctan at 0x...>
+
+ """
+
+ @abstract_method
+ def cosh(self):
+ r"""
+ Hyperbolic cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\cosh(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.cosh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method cosh at 0x...>
+
+ """
+
+ @abstract_method
+ def sinh(self):
+ r"""
+ Hyperbolic sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\sinh(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.sinh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method sinh at 0x...>
+
+ """
+
+ @abstract_method
+ def tanh(self):
+ r"""
+ Hyperbolic tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\tanh(f)`, where `f` is the current coordinate
+ function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.tanh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method tanh at 0x...>
+
+ """
+
+ @abstract_method
+ def arccosh(self):
+ r"""
+ Inverse hyperbolic cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{arcosh}(f)`, where `f` is the current
+ coordinate function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.arccosh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method arccosh at 0x...>
+
+ """
+
+ @abstract_method
+ def arcsinh(self):
+ r"""
+ Inverse hyperbolic sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{arsinh}(f)`, where `f` is the current
+ coordinate function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.arcsinh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method arcsinh at 0x...>
+
+ """
+
+ @abstract_method
+ def arctanh(self):
+ r"""
+ Inverse hyperbolic tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{artanh}(f)`, where `f` is the current
+ coordinate function
+
+ TESTS:
+
+ This method must be implemented by derived classes; it is not
+ implemented here::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: from sage.manifolds.coord_func import CoordFunction
+ sage: f = CoordFunction(X.function_ring())
+ sage: f.arctanh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: <abstract method arctanh at 0x...>
+
+ """
+
+
+#*****************************************************************************
+
+# TODO: Make this and CoordFunction have a common ABC
+class MultiCoordFunction(SageObject):
+ r"""
+ Coordinate function to some Cartesian power of the base field.
+
+ If `n` and `m` are two positive integers and `(U, \varphi)` is a chart on
+ a topological manifold `M` of dimension `n` over a topological field `K`,
+ a *multi-coordinate function* associated to `(U, \varphi)` is a map
+
+ .. MATH::
+
+ \begin{array}{llcl}
+ f:& V \subset K^n & \longrightarrow & K^m \\
+ & (x^1, \ldots, x^n) & \longmapsto & (f_1(x^1, \ldots, x^n),
+ \ldots, f_m(x^1, \ldots, x^n)),
+ \end{array}
+
+ where `V` is the codomain of `\varphi`. In other words, `f` is a
+ `K^m`-valued function of the coordinates associated to the chart
+ `(U, \varphi)`. Each component `f_i` (`1 \leq i \leq m`) is a coordinate
+ function and is therefore stored as a
+ :class:`~sage.manifolds.coord_func.CoordFunction`.
+
+ INPUT:
+
+ - ``chart`` -- the chart `(U, \varphi)`
+ - ``expressions`` -- list (or tuple) of length `m` of elements to
+ construct the coordinate functions `f_i` (`1 \leq i \leq m`); for
+ symbolic coordinate functions, this must be symbolic expressions
+ involving the chart coordinates, while for numerical coordinate
+ functions, this must be data file names
+
+ EXAMPLES:
+
+ A function `f: V \subset \RR^2 \longrightarrow \RR^3`::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x)*exp(y)); f
+ Coordinate functions (x - y, x*y, cos(x)*e^y) on the Chart (M, (x, y))
+ sage: type(f)
+ <class 'sage.manifolds.coord_func.MultiCoordFunction'>
+ sage: f(x,y)
+ (x - y, x*y, cos(x)*e^y)
+ sage: latex(f)
+ \left(x - y, x y, \cos\left(x\right) e^{y}\right)
+
+ Each real-valued function `f_i` (`1 \leq i \leq m`) composing `f` can
+ be accessed via the square-bracket operator, by providing `i-1` as an
+ argument::
+
+ sage: f[0]
+ x - y
+ sage: f[1]
+ x*y
+ sage: f[2]
+ cos(x)*e^y
+
+ We can give a more verbose explanation of each function::
+
+ sage: f[0].display()
+ (x, y) |--> x - y
+
+ Each ``f[i-1]`` is an instance of
+ :class:`~sage.manifolds.coord_func.CoordFunction`::
+
+ sage: isinstance(f[0], sage.manifolds.coord_func.CoordFunction)
+ True
+
+ In the present case, ``f[i-1]`` is an instance of the subclass
+ :class:`~sage.manifolds.coord_func_symb.CoordFunctionSymb`::
+
+ sage: type(f[0])
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+
+ A class :class:`MultiCoordFunction` can represent a
+ real-valued function (case `m = 1`), although one should
+ rather employ the class :class:`~sage.manifolds.coord_func.CoordFunction`
+ for this purpose::
+
+ sage: g = X.multifunction(x*y^2)
+ sage: g(x,y)
+ (x*y^2,)
+
+ Evaluating the functions at specified coordinates::
+
+ sage: f(1,2)
+ (-1, 2, cos(1)*e^2)
+ sage: var('a b')
+ (a, b)
+ sage: f(a,b)
+ (a - b, a*b, cos(a)*e^b)
+ sage: g(1,2)
+ (4,)
+
+ """
+ def __init__(self, chart, expressions):
+ r"""
+ Initialize ``self``.
+
+ TESTS::
+
+ sage: M = Manifold(3, 'M', structure='topological')
+ sage: X.<x,y,z> = M.chart()
+ sage: f = X.multifunction(x+y+z, x*y*z); f
+ Coordinate functions (x + y + z, x*y*z) on the Chart (M, (x, y, z))
+ sage: type(f)
+ <class 'sage.manifolds.coord_func.MultiCoordFunction'>
+ sage: TestSuite(f).run()
+
+ """
+ self._chart = chart
+ self._nc = len(self._chart._xx) # number of coordinates
+ self._nf = len(expressions) # number of functions
+ self._functions = tuple(chart.function(express)
+ for express in expressions)
+
+ def _repr_(self):
+ r"""
+ String representation of ``self``.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x)*exp(y))
+ sage: f._repr_()
+ 'Coordinate functions (x - y, x*y, cos(x)*e^y) on the Chart (M, (x, y))'
+ sage: f
+ Coordinate functions (x - y, x*y, cos(x)*e^y) on the Chart (M, (x, y))
+
+ """
+ return "Coordinate functions {} on the {}".format(self._functions,
+ self._chart)
+
+ def _latex_(self):
+ r"""
+ LaTeX representation of the object.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x)*exp(y))
+ sage: f._latex_()
+ \left(x - y, x y, \cos\left(x\right) e^{y}\right)
+ sage: latex(f)
+ \left(x - y, x y, \cos\left(x\right) e^{y}\right)
+
+ """
+ from sage.misc.latex import latex
+ return latex(self._functions)
+
+ def expr(self):
+ r"""
+ Return a tuple of data, the item no.`i` begin sufficient to
+ reconstruct the coordinate function no. `i`.
+
+ In other words, if ``f`` is a multi-coordinate function, then
+ ``f.chart().multifunction(*(f.expr()))`` results in a
+ multi-coordinate function identical to ``f``.
+
+ For a symbolic multi-coordinate function, :meth:`expr` returns the
+ tuple of the symbolic expressions of the coordinate functions
+ composing the object.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x)*exp(y))
+ sage: f.expr()
+ (x - y, x*y, cos(x)*e^y)
+ sage: type(f.expr()[0])
+ <type 'sage.symbolic.expression.Expression'>
+
+ One shall always have::
+
+ sage: f.chart().multifunction(*(f.expr())) == f
+ True
+
+ """
+ return tuple(func.expr() for func in self._functions)
+
+ def chart(self):
+ r"""
+ Return the chart with respect to which ``self`` is defined.
+
+ OUTPUT:
+
+ - a :class:`~sage.manifolds.chart.Chart`
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x)*exp(y))
+ sage: f.chart()
+ Chart (M, (x, y))
+ sage: f.chart() is X
+ True
+
+ """
+ return self._chart
+
+ def __eq__(self, other):
+ r"""
+ Comparison (equality) operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`MultiCoordFunction`
+
+ OUTPUT:
+
+ - ``True`` if ``self`` is equal to ``other``, ``False`` otherwise
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x*y))
+ sage: f == X.multifunction(x-y, x*y)
+ False
+ sage: f == X.multifunction(x-y, x*y, 2)
+ False
+ sage: f == X.multifunction(x-y, x*y, cos(y*x))
+ True
+ sage: Y.<u,v> = M.chart()
+ sage: f == Y.multifunction(u-v, u*v, cos(u*v))
+ False
+
+ """
+ if other is self:
+ return True
+ if not isinstance(other, MultiCoordFunction):
+ return False
+ if other._chart != self._chart:
+ return False
+ if other._nf != self._nf:
+ return False
+ for i in range(self._nf):
+ if other._functions[i] != self._functions[i]:
+ return False
+ return True
+
+ def __ne__(self, other):
+ r"""
+ Inequality operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`MultiCoordFunction`
+
+ OUTPUT:
+
+ - ``True`` if ``self`` is different from ``other``, ``False``
+ otherwise
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x*y))
+ sage: f != X.multifunction(x-y, x*y)
+ True
+ sage: f != X.multifunction(x, y, 2)
+ True
+ sage: f != X.multifunction(x-y, x*y, cos(x*y))
+ False
+
+ """
+ return not (self == other)
+
+ def __getitem__(self, index):
+ r"""
+ Return a specified function of the set represented by ``self``.
+
+ INPUT:
+
+ - ``index`` -- index `i` of the function (`0 \leq i \leq m-1`)
+
+ OUTPUT
+
+ -- a :class:`CoordFunction` representing the function
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x*y))
+ sage: f.__getitem__(0)
+ x - y
+ sage: f.__getitem__(1)
+ x*y
+ sage: f.__getitem__(2)
+ cos(x*y)
+ sage: f[0], f[1], f[2]
+ (x - y, x*y, cos(x*y))
+
+ """
+ return self._functions[index]
+
+ def __call__(self, *coords, **options):
+ r"""
+ Compute the values of the functions at specified coordinates.
+
+ INPUT:
+
+ - ``*coords`` -- list of coordinates where the functions are
+ to be evaluated
+ - ``**options`` -- allows to pass some options, e.g.,
+ ``simplify=False`` to disable simplification for symbolic
+ coordinate functions
+
+ OUTPUT:
+
+ - tuple containing the values of the `m` functions
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, cos(x*y))
+ sage: f.__call__(2,3)
+ (-1, 6, cos(6))
+ sage: f(2,3)
+ (-1, 6, cos(6))
+ sage: f.__call__(x,y)
+ (x - y, x*y, cos(x*y))
+
+ """
+ return tuple(func(*coords, **options) for func in self._functions)
+
+ @cached_method
+ def jacobian(self):
+ r"""
+ Return the Jacobian matrix of the system of coordinate functions.
+
+ ``jacobian()`` is a 2-dimensional array of size `m \times n`,
+ where `m` is the number of functions and `n` the number of
+ coordinates, the generic element being
+ `J_{ij} = \frac{\partial f_i}{\partial x^j}` with `1 \leq i \leq m`
+ (row index) and `1 \leq j \leq n` (column index).
+
+ OUTPUT:
+
+ - Jacobian matrix as a 2-dimensional array ``J`` of
+ coordinate functions with ``J[i-1][j-1]`` being
+ `J_{ij} = \frac{\partial f_i}{\partial x^j}`
+ for `1 \leq i \leq m` and `1 \leq j \leq n`
+
+ EXAMPLES:
+
+ Jacobian of a set of 3 functions of 2 coordinates::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, y^3*cos(x))
+ sage: f.jacobian()
+ [ 1 -1]
+ [ y x]
+ [ -y^3*sin(x) 3*y^2*cos(x)]
+
+ Each element of the result is a
+ :class:`coordinate function <CoordFunction>`::
+
+ sage: type(f.jacobian()[2,0])
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: f.jacobian()[2,0].display()
+ (x, y) |--> -y^3*sin(x)
+
+ Test of the computation::
+
+ sage: [[f.jacobian()[i,j] == f[i].diff(j) for j in range(2)] for i in range(3)]
+ [[True, True], [True, True], [True, True]]
+
+ Test with ``start_index = 1``::
+
+ sage: M = Manifold(2, 'M', structure='topological', start_index=1)
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y, y^3*cos(x))
+ sage: f.jacobian()
+ [ 1 -1]
+ [ y x]
+ [ -y^3*sin(x) 3*y^2*cos(x)]
+ sage: [[f.jacobian()[i,j] == f[i].diff(j+1) for j in range(2)] # note the j+1
+ ....: for i in range(3)]
+ [[True, True], [True, True], [True, True]]
+ """
+ from sage.matrix.constructor import matrix
+ mat = matrix([[func.diff(coord) for coord in self._chart[:]]
+ for func in self._functions])
+ mat.set_immutable()
+ return mat
+
+ @cached_method
+ def jacobian_det(self):
+ r"""
+ Return the Jacobian determinant of the system of functions.
+
+ The number `m` of coordinate functions must equal the number `n`
+ of coordinates.
+
+ OUTPUT:
+
+ - a :class:`CoordFunction` representing the determinant
+
+ EXAMPLES:
+
+ Jacobian determinant of a set of 2 functions of 2 coordinates::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.multifunction(x-y, x*y)
+ sage: f.jacobian_det()
+ x + y
+
+ The output of :meth:`jacobian_det` is an instance of
+ :class:`CoordFunction` and can therefore be called on specific
+ values of the coordinates, e.g. `(x,y) = (1,2)`::
+
+ sage: type(f.jacobian_det())
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: f.jacobian_det().display()
+ (x, y) |--> x + y
+ sage: f.jacobian_det()(1,2)
+ 3
+
+ The result is cached::
+
+ sage: f.jacobian_det() is f.jacobian_det()
+ True
+
+ We verify the determinant of the Jacobian::
+
+ sage: f.jacobian_det() == det(matrix([[f[i].diff(j).expr() for j in range(2)]
+ ....: for i in range(2)]))
+ True
+
+ Jacobian determinant of a set of 3 functions of 3 coordinates::
+
+ sage: M = Manifold(3, 'M', structure='topological')
+ sage: X.<x,y,z> = M.chart()
+ sage: f = X.multifunction(x*y+z^2, z^2*x+y^2*z, (x*y*z)^3)
+ sage: f.jacobian_det().display()
+ (x, y, z) |--> 6*x^3*y^5*z^3 - 3*x^4*y^3*z^4 - 12*x^2*y^4*z^5 + 6*x^3*y^2*z^6
+
+ We verify the determinant of the Jacobian::
+
+ sage: f.jacobian_det() == det(matrix([[f[i].diff(j).expr() for j in range(3)]
+ ....: for i in range(3)]))
+ True
+
+ """
+ def simple_determinant(aa):
+ r"""
+ Compute the determinant of a square matrix represented as an array.
+
+ This function is based on Laplace's cofactor expansion.
+ """
+ n = len(aa)
+ if n == 1:
+ return aa[0][0]
+ res = 0
+ sign = True
+ for i in range(n):
+ b = []
+ for k in range(i):
+ r = []
+ for l in range(1,n):
+ r.append(aa[k][l])
+ b.append(r)
+ for k in range(i+1,n):
+ r = []
+ for l in range(1,n):
+ r.append(aa[k][l])
+ b.append(r)
+ if sign:
+ res += aa[i][0] * simple_determinant(b)
+ else:
+ res -= aa[i][0] * simple_determinant(b)
+ sign = not sign
+ return res
+
+ if self._nf != self._nc:
+ raise ValueError("the Jacobian matrix is not a square matrix")
+ J = self.jacobian()
+ J = [[J[i,j] for i in range(self._nc)] for j in range(self._nc)]
+ return simple_determinant(J)
+
diff --git a/src/sage/manifolds/coord_func_symb.py b/src/sage/manifolds/coord_func_symb.py
new file mode 100644
index 00000000..551dda8
--- /dev/null
+++ b/src/sage/manifolds/coord_func_symb.py
@@ -0,0 +1,1762 @@
+r"""
+Symbolic Coordinate Functions
+
+In the context of a topological manifold `M` over a topological field `K`,
+a *coordinate function* is a function from a chart codomain
+to `K`. In other words, a coordinate function is a `K`-valued function of
+the coordinates associated to some chart.
+
+More precisely, let `(U, \varphi)` be a chart on `M`, i.e. `U` is an open
+subset of `M` and `\varphi: U \to V \subset K^n` is a homeomorphism
+from `U` to an open subset `V` of `K^n`. A *coordinate function associated
+to the chart* `(U, \varphi)` is a function
+
+.. MATH::
+
+ \begin{array}{cccc}
+ f:& V \subset K^n & \longrightarrow & K \\
+ & (x^1, \ldots, x^n) & \longmapsto & f(x^1, \ldots, x^n)
+ \end{array}
+
+This module implements symbolic coordinate functions via the class
+:class:`CoordFunctionSymb`.
+
+AUTHORS:
+
+- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
+- Travis Scrimshaw (2016) : make coordinate functions elements of
+ :class:`CoordFunctionSymbRing`.
+
+"""
+#*****************************************************************************
+# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
+# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
+# Copyright (C) 2016 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.misc.cachefunc import cached_method
+from sage.symbolic.ring import SR
+from sage.structure.element import RingElement
+from sage.structure.parent import Parent
+from sage.structure.unique_representation import UniqueRepresentation
+from sage.categories.commutative_algebras import CommutativeAlgebras
+from sage.manifolds.coord_func import CoordFunction, MultiCoordFunction
+from sage.manifolds.utilities import (ExpressionNice, simplify_chain_real,
+ simplify_chain_generic)
+
+class CoordFunctionSymb(CoordFunction):
+ r"""
+ Coordinate function with symbolic representation.
+
+ If `(U, \varphi)` is a chart on a topological manifold `M` of
+ dimension `n` over a topological field `K`, a *coordinate function*
+ associated to `(U, \varphi)` is a map
+
+ .. MATH::
+
+ \begin{array}{llcl}
+ f:& V \subset K^n & \longrightarrow & K \\
+ & (x^1, \ldots, x^n) & \longmapsto & f(x^1, \ldots, x^n),
+ \end{array}
+
+ where `V` is the codomain of `\varphi`. In other words, `f` is a
+ `K`-valued function of the
+ coordinates associated to the chart `(U, \varphi)`.
+
+ INPUT:
+
+ - ``parent`` -- the algebra of coordinate functions on the chart
+ `(U, \varphi)`
+ - ``expression`` -- a symbolic expression representing
+ `f(x^1, \ldots, x^n)`, where `(x^1, \ldots, x^n)` are the
+ coordinates of the chart `(U, \varphi)`
+
+ EXAMPLES:
+
+ A symbolic coordinate function associated with a 2-dimensional chart::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x^2+3*y+1)
+ sage: type(f)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: f.display()
+ (x, y) |--> x^2 + 3*y + 1
+ sage: f(x,y)
+ x^2 + 3*y + 1
+
+ The symbolic expression is returned when asking the direct display of
+ the function::
+
+ sage: f
+ x^2 + 3*y + 1
+ sage: latex(f)
+ x^{2} + 3 \, y + 1
+
+ A similar output is obtained by means of the method :meth:`expr`::
+
+ sage: f.expr()
+ x^2 + 3*y + 1
+
+ The value of the function at specified coordinates is obtained by means
+ of the standard parentheses notation::
+
+ sage: f(2,-1)
+ 2
+ sage: var('a b')
+ (a, b)
+ sage: f(a,b)
+ a^2 + 3*b + 1
+
+ An unspecified coordinate function::
+
+ sage: g = X.function(function('G')(x, y))
+ sage: g
+ G(x, y)
+ sage: g.display()
+ (x, y) |--> G(x, y)
+ sage: g.expr()
+ G(x, y)
+ sage: g(2,3)
+ G(2, 3)
+
+ Coordinate functions can be compared to other values::
+
+ sage: f = X.function(x^2+3*y+1)
+ sage: f == 2
+ False
+ sage: f == x^2 + 3*y + 1
+ True
+ sage: g = X.function(x*y)
+ sage: f == g
+ False
+ sage: h = X.function(x^2+3*y+1)
+ sage: f == h
+ True
+
+ .. RUBRIC:: Differences between ``CoordFunctionSymb`` and callable
+ symbolic expressions
+
+ Callable symbolic expressions are defined directly from symbolic
+ expressions of the coordinates::
+
+ sage: f0(x,y) = x^2 + 3*y + 1
+ sage: type(f0)
+ <type 'sage.symbolic.expression.Expression'>
+ sage: f0
+ (x, y) |--> x^2 + 3*y + 1
+ sage: f0(x,y)
+ x^2 + 3*y + 1
+
+ To get an output similar to that of ``f0`` for the coordinate function
+ ``f``, we must use the method :meth:`display`::
+
+ sage: f
+ x^2 + 3*y + 1
+ sage: f.display()
+ (x, y) |--> x^2 + 3*y + 1
+ sage: f(x,y)
+ x^2 + 3*y + 1
+
+ More importantly, instances of :class:`CoordFunctionSymb` differ from
+ callable symbolic expression by the automatic simplifications in all
+ operations. For instance, adding the two callable symbolic expressions::
+
+ sage: f0(x,y,z) = cos(x)^2 ; g0(x,y,z) = sin(x)^2
+
+ results in::
+
+ sage: f0 + g0
+ (x, y, z) |--> cos(x)^2 + sin(x)^2
+
+ To get `1`, one has to call
+ :meth:`~sage.symbolic.expression.Expression.simplify_trig`::
+
+ sage: (f0 + g0).simplify_trig()
+ (x, y, z) |--> 1
+
+ On the contrary, the sum of the corresponding :class:`CoordFunctionSymb`
+ instances is automatically simplified (see
+ :func:`~sage.manifolds.utilities.simplify_chain_real` and
+ :func:`~sage.manifolds.utilities.simplify_chain_generic` for details)::
+
+ sage: f = X.function(cos(x)^2) ; g = X.function(sin(x)^2)
+ sage: f + g
+ 1
+
+ Another difference regards the display of partial derivatives:
+ for callable symbolic functions, it relies on Pynac notation
+ ``D[0]``, ``D[1]``, etc.::
+
+ sage: g = function('g')(x, y)
+ sage: f0(x,y) = diff(g, x) + diff(g, y)
+ sage: f0
+ (x, y) |--> D[0](g)(x, y) + D[1](g)(x, y)
+
+ while for coordinate functions, the display is more "textbook" like::
+
+ sage: f = X.function(diff(g, x) + diff(g, y))
+ sage: f
+ d(g)/dx + d(g)/dy
+
+ The difference is even more dramatic on LaTeX outputs::
+
+ sage: latex(f0)
+ \left( x, y \right) \ {\mapsto} \ D[0]\left(g\right)\left(x, y\right)
+ + D[1]\left(g\right)\left(x, y\right)
+ sage: latex(f)
+ \frac{\partial\,g}{\partial x} + \frac{\partial\,g}{\partial y}
+
+ Note that this regards only the display of coordinate functions:
+ internally, the Pynac notation is still used, as we can check by asking
+ for the symbolic expression stored in ``f``::
+
+ sage: f.expr()
+ D[0](g)(x, y) + D[1](g)(x, y)
+
+ One can switch to Pynac notation by changing the global options::
+
+ sage: Manifold.global_options(textbook_output=False)
+ sage: latex(f)
+ D[0]\left(g\right)\left(x, y\right) + D[1]\left(g\right)\left(x, y\right)
+ sage: Manifold.global_options.reset()
+ sage: latex(f)
+ \frac{\partial\,g}{\partial x} + \frac{\partial\,g}{\partial y}
+
+ Another difference between :class:`CoordFunctionSymb` and
+ callable symbolic expression is the possibility to switch off the display
+ of the arguments of unspecified functions. Consider for instance::
+
+ sage: f = X.function(function('u')(x, y) * function('v')(x, y))
+ sage: f
+ u(x, y)*v(x, y)
+ sage: f0(x,y) = function('u')(x, y) * function('v')(x, y)
+ sage: f0
+ (x, y) |--> u(x, y)*v(x, y)
+
+ If there is a clear understanding that `u` and `v` are functions of
+ `(x,y)`, the explicit mention of the latter can be cumbersome in lengthy
+ tensor expressions. We can switch it off by::
+
+ sage: Manifold.global_options(omit_function_arguments=True)
+ sage: f
+ u*v
+
+ Note that neither the callable symbolic expression ``f0`` nor the internal
+ expression of ``f`` is affected by the above command::
+
+ sage: f0
+ (x, y) |--> u(x, y)*v(x, y)
+ sage: f.expr()
+ u(x, y)*v(x, y)
+
+ We revert to the default behavior by::
+
+ sage: Manifold.global_options.reset()
+ sage: f
+ u(x, y)*v(x, y)
+
+ """
+ def __init__(self, parent, expression):
+ r"""
+ Initialize ``self``.
+
+ TESTS:
+
+ Coordinate function on a real manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(1+x*y); f
+ x*y + 1
+ sage: type(f)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: TestSuite(f).run()
+
+ Coordinate function on a complex manifold::
+
+ sage: N = Manifold(2, 'N', structure='topological', field='complex')
+ sage: Y.<z,w> = N.chart()
+ sage: g = Y.function(i*z + 2*w); g
+ 2*w + I*z
+ sage: TestSuite(g).run()
+
+ """
+ CoordFunction.__init__(self, parent)
+ self._express = SR(expression) # symbolic expression enforced
+ # Definition of the simplification chain to be applied in
+ # symbolic calculus:
+ if self.parent()._chart.manifold().base_field_type() == 'real':
+ self._simplify = simplify_chain_real
+ else:
+ self._simplify = simplify_chain_generic
+ # Derived quantities:
+ self._der = None # list of partial derivatives (to be set by diff()
+ # and unset by del_derived())
+
+ # -------------------------------------------------------------
+ # Methods to be implemented by derived classes of CoordFunction
+ # -------------------------------------------------------------
+
+ def _repr_(self):
+ r"""
+ String representation of ``self``.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(1+x*y)
+ sage: f._repr_()
+ 'x*y + 1'
+ sage: repr(f) # indirect doctest
+ 'x*y + 1'
+ sage: f # indirect doctest
+ x*y + 1
+
+ """
+ if self.parent()._chart.manifold().global_options('textbook_output'):
+ return str(ExpressionNice(self._express))
+ else:
+ return str(self._express)
+
+ def _latex_(self):
+ r"""
+ LaTeX representation of ``self``.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(cos(x*y/2))
+ sage: f._latex_()
+ \cos\left(\frac{1}{2} \, x y\right)
+ sage: latex(f) # indirect doctest
+ \cos\left(\frac{1}{2} \, x y\right)
+
+ """
+ from sage.misc.latex import latex
+ if self.parent()._chart.manifold().global_options('textbook_output'):
+ return latex(ExpressionNice(self._express))
+ else:
+ return latex(self._express)
+
+ def display(self):
+ r"""
+ Display ``self`` in arrow notation.
+
+ The output is either text-formatted (console mode) or
+ LaTeX-formatted (notebook mode).
+
+ EXAMPLES:
+
+ Coordinate function on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(cos(x*y/2))
+ sage: f.display()
+ (x, y) |--> cos(1/2*x*y)
+ sage: latex(f.display())
+ \left(x, y\right) \mapsto \cos\left(\frac{1}{2} \, x y\right)
+
+ A shortcut is ``disp()``::
+
+ sage: f.disp()
+ (x, y) |--> cos(1/2*x*y)
+
+ Display of the zero function::</