summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRelease Manager <release@sagemath.org>2016-05-16 11:10:03 +0200
committerVolker Braun <vbraun.name@gmail.com>2016-05-16 11:10:03 +0200
commita2777c9887415c0772be9288c1ce0333b54a11f5 (patch)
tree5c40ef846ad5eae68c9320320c0e61f6c0708d17
parentTrac #18265: Axioms for semigroups: L,R,J,H-trivial, aperiodic (diff)
parentPython 3 format for print in manifolds (diff)
Trac #18640: Topological manifolds: scalar fields
This ticket implements scalar fields on topological manifolds. This is a follow up of ticket #18529 within the [http://sagemanifolds.obspm.fr/ SageManifolds project]. See the metaticket #18528 for an overview. By ''scalar field'', it is meant a continuous map f: M --> K, where K is a topological field and M a topological manifold over K. This ticket implements the following Python classes: - `CoordFunction`: abstract base class for coordinate functions, i.e. functions V\subset K^n^ --> K, where V is some chart codomain and n=dim(M) - `CoordFunctionSymb`: symbolic coordinate functions - `MultiCoordFunction`: functions V\subset K^n^ --> K^m^, where V is some chart codomain and m some positive integer - `ScalarFieldAlgebra`: set C^0^(M) of scalar fields M --> K as a commutative algebra over K (Parent class) - `ScalarField`: scalar field M --> K (Element class) - `ExpressionNice`: a subclass of `sage.symbolic.expression.Expression` with enhanced display of callable symbolic expressions Internally, `ScalarField`'s are described by their coordinate representations in various charts, which are implemented as a dictionary of `CoordFunction`'s, with the charts as keys. At the moment, there is only one concrete class for coordinate functions: `CoordFunctionSymb` (functions described by symbolic expressions of the coordinates), but in the future there should be numerical coordinate functions (hence the abstract base class `CoordFunction`). '''Documentation''': The reference manual is produced by `sage -docbuild reference/manifolds html` It can also be accessed online at http://sagemanifolds.obspm.fr/doc/18640/reference/manifolds/ More documentation (e.g. example worksheets) can be found [http://sagemanifolds.obspm.fr/documentation.html here]. URL: http://trac.sagemath.org/18640 Reported by: egourgoulhon Ticket author(s): Eric Gourgoulhon, Michal Bejger, Travis Scrimshaw Reviewer(s): Travis Scrimshaw, Eric Gourgoulhon
-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.py327
-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.py398
-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, 8139 insertions, 66 deletions
diff --git a/src/doc/en/reference/manifolds/chart.rst b/src/doc/en/reference/manifolds/chart.rst
new file mode 100644
index 0000000..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 0000000..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..63b583a 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):
@@ -951,8 +1209,8 @@ class RealChart(Chart):
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]
sage: (x1, x2, x3) == (r, th, ph)
True
@@ -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,14 +2138,22 @@ 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:
raise ValueError("no solution found; use set_inverse() to " +
"set the inverse manually")
if len(list_x2_to_x1) > 1:
- print "Multiple solutions found: "
- print list_x2_to_x1
+ print("Multiple solutions found: ")
+ print(list_x2_to_x1)
raise ValueError(
"non-unique solution to the inverse coordinate " +
"transformation; use set_inverse() to set the inverse " +
@@ -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 0000000..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 0000000..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::
+
+ sage: X.zero_function().display()
+ (x, y) |--> 0
+
+ """
+ from sage.tensor.modules.format_utilities import FormattedExpansion
+ from sage.misc.latex import latex
+ resu_txt = str(self.parent()._chart[:]) + ' |--> ' + \
+ str(ExpressionNice(self._express))
+ resu_latex = latex(self.parent()._chart[:]) + r' \mapsto' + \
+ latex(ExpressionNice(self._express))
+ return FormattedExpansion(resu_txt, resu_latex)
+
+ disp = display
+
+ def expr(self):
+ r"""
+ Return the symbolic expression representing the image of ``self``.
+
+ OUTPUT:
+
+ - :class:`symbolic expression <sage.symbolic.expression.Expression>`
+ involving the chart coordinates
+
+ EXAMPLES:
+
+ Coordinate function of a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x^2+3*y+1)
+ sage: f.expr()
+ x^2 + 3*y + 1
+ sage: type(f.expr())
+ <type 'sage.symbolic.expression.Expression'>
+
+ For a symbolic coordinate function, one shall always have::
+
+ sage: bool( f.expr() == f(*(f.chart()[:])) )
+ True
+
+ The method :meth:`expr` is useful for accessing to all the
+ symbolic expression functionalities in Sage; for instance::
+
+ sage: var('a')
+ a
+ sage: f = X.function(a*x*y); f.display()
+ (x, y) |--> a*x*y
+ sage: f.expr()
+ a*x*y
+ sage: f.expr().subs(a=2)
+ 2*x*y
+
+ Note that for substituting the value of a coordinate, the function
+ call can be used as well::
+
+ sage: f(x,3)
+ 3*a*x
+ sage: bool( f(x,3) == f.expr().subs(y=3) )
+ True
+
+ """
+ return self._express
+
+ 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`` -- allows to pass ``simplify=False`` to disable the
+ call of the simplification chain on the result
+
+ OUTPUT:
+
+ - the value `f(x^1, \ldots, x^n)`, where `f` is the current
+ coordinate function
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(sin(x*y))
+ sage: f.__call__(-2, 3)
+ -sin(6)
+ sage: f(-2, 3)
+ -sin(6)
+ sage: var('a b')
+ (a, b)
+ sage: f.__call__(a, b)
+ sin(a*b)
+ sage: f(a,b)
+ sin(a*b)
+ sage: f.__call__(pi, 1)
+ 0
+ sage: f.__call__(pi, 1/2)
+ 1
+
+ """
+ if len(coords) != self._nc:
+ raise ValueError("bad number of coordinates")
+ substitutions = dict(zip(self.parent()._chart._xx, coords))
+ resu = self._express.subs(substitutions)
+ if 'simplify' in options:
+ if options['simplify']:
+ return self._simplify(resu)
+ else:
+ return resu
+ else:
+ return self._simplify(resu)
+
+ def is_zero(self):
+ r"""
+ Return ``True`` if the function is zero and ``False`` otherwise.
+
+ EXAMPLES:
+
+ Coordinate functions associated to 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: f.is_zero()
+ False
+ sage: f == 0
+ False
+ sage: g = X.function(0)
+ sage: g.is_zero()
+ True
+ sage: g == 0
+ True
+ sage: X.zero_function().is_zero()
+ True
+ sage: X.zero_function() == 0
+ True
+
+ """
+ return self._express.is_zero()
+
+ def copy(self):
+ r"""
+ Return an exact copy of the object.
+
+ OUTPUT:
+
+ - a :class:`CoordFunctionSymb`
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y^2)
+ sage: g = f.copy(); g
+ y^2 + x
+
+ By construction, ``g`` is identical to ``f``::
+
+ sage: type(g) == type(f)
+ True
+ sage: g == f
+ True
+
+ but it is not the same object::
+
+ sage: g is f
+ False
+
+ """
+ return type(self)(self.parent(), self._express)
+
+ def diff(self, coord):
+ r"""
+ Partial derivative 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:
+
+ - a :class:`CoordFunctionSymb` representing the partial
+ derivative `\frac{\partial f}{\partial x^i}`
+
+ EXAMPLES:
+
+ Partial derivatives of a 2-dimensional coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x^2+3*y+1); f
+ x^2 + 3*y + 1
+ sage: f.diff(x)
+ 2*x
+ sage: f.diff(y)
+ 3
+
+ Each partial derivatives is itself a coordinate function::
+
+ sage: type(f.diff(x))
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+
+ An index can be used instead of the coordinate symbol::
+
+ sage: f.diff(0)
+ 2*x
+ sage: f.diff(1)
+ 3
+
+ The index range depends on the convention used on the chart's domain::
+
+ sage: M = Manifold(2, 'M', structure='topological', start_index=1)
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x^2+3*y+1)
+ sage: f.diff(0)
+ Traceback (most recent call last):
+ ...
+ ValueError: coordinate index out of range
+ sage: f.diff(1)
+ 2*x
+ sage: f.diff(2)
+ 3
+
+ """
+ from sage.calculus.functional import diff
+ from sage.rings.integer import Integer
+ if self._der is None:
+ # the list of partial derivatives has to be updated
+ self._der = [type(self)(self.parent(),
+ self._simplify(diff(self._express, xx)))
+ for xx in self.parent()._chart[:]]
+ if isinstance(coord, (int, Integer)):
+ # NB: for efficiency, we access directly to the "private" attributes
+ # of other classes. A more conventional OOP writing would be
+ # coordsi = coord - self.parent()._chart.domain().start_index()
+ coordsi = coord - self.parent()._chart._domain._sindex
+ if coordsi < 0 or coordsi >= self._nc:
+ raise ValueError("coordinate index out of range")
+ return self._der[coordsi]
+ else:
+ return self._der[self.parent()._chart[:].index(coord)]
+
+ def __eq__(self, other):
+ r"""
+ Comparison (equality) operator.
+
+ INPUT:
+
+ - ``other`` -- a :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - ``True`` if ``self`` is equal to ``other``, or ``False`` otherwise
+
+ TESTS:
+
+ Coordinate functions associated to a 2-dimensional chart::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y^2)
+ sage: g = X.function(x+y^2)
+ sage: f == g
+ True
+ sage: f == 1
+ False
+ sage: h = X.function(1)
+ sage: h == 1
+ True
+ sage: h == f
+ False
+ sage: h == 0
+ False
+ sage: X.function(0) == 0
+ True
+ sage: X.zero_function() == 0
+ True
+
+ """
+ if other is self:
+ return True
+ if isinstance(other, CoordFunctionSymb):
+ if other.parent() != self.parent():
+ return False
+ else:
+ return bool(other._express == self._express)
+ else:
+ return bool(self._express == other)
+
+ def __neg__(self):
+ r"""
+ Unary minus operator.
+
+ OUTPUT:
+
+ - the opposite of ``self``
+
+ TESTS:
+
+ Coordinate functions associated to a 2-dimensional chart::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y^2)
+ sage: g = -f; g
+ -y^2 - x
+ sage: type(g)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: -g == f
+ True
+
+ """
+ return type(self)(self.parent(), self._simplify(-self._express))
+
+ 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:
+
+ Coordinate functions associated to a 2-dimensional chart::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(1+x^2+y^2)
+ sage: g = f.__invert__(); g
+ 1/(x^2 + y^2 + 1)
+ sage: type(g)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: g == ~f
+ True
+ sage: g.__invert__() == f
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(SR.one() / self._express))
+ # NB: self._express.__invert__() would return 1/self._express
+ # (cf. the code of __invert__ in src/sage/symbolic/expression.pyx)
+ # Here we prefer SR(1)/self._express
+
+ 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::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y^2)
+ sage: g = X.function(x+1)
+ sage: s = f + g; s.display()
+ (x, y) |--> y^2 + 2*x + 1
+ sage: type(s)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: (f + 0).display()
+ (x, y) |--> y^2 + x
+ sage: (f + X.zero_function()).display()
+ (x, y) |--> y^2 + x
+ sage: (f + 1).display()
+ (x, y) |--> y^2 + x + 1
+ sage: (f + pi).display()
+ (x, y) |--> pi + y^2 + x
+ sage: (f + x).display()
+ (x, y) |--> y^2 + 2*x
+ sage: (f + -f).display()
+ (x, y) |--> 0
+
+ """
+ res = self._simplify(self._express + other._express)
+ if res == 0:
+ return self.parent().zero()
+ else:
+ return type(self)(self.parent(), res)
+
+ 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::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y^2)
+ sage: g = X.function(x+1)
+ sage: s = f - g; s.display()
+ (x, y) |--> y^2 - 1
+ sage: type(s)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: (f - 0).display()
+ (x, y) |--> y^2 + x
+ sage: (f - X.zero_function()).display()
+ (x, y) |--> y^2 + x
+ sage: (f - 1).display()
+ (x, y) |--> y^2 + x - 1
+ sage: (f - x).display()
+ (x, y) |--> y^2
+ sage: (f - pi).display()
+ (x, y) |--> -pi + y^2 + x
+ sage: (f - f).display()
+ (x, y) |--> 0
+ sage: (f - g) == -(g - f)
+ True
+ """
+ res = self._simplify(self._express - other._express)
+ if res == 0:
+ return self.parent().zero()
+ else:
+ return type(self)(self.parent(), res)
+
+ 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::
+
+ 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: s = f._mul_(g); s.display()
+ (x, y) |--> x^2 - y^2
+ sage: type(s)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: (f * 0).display()
+ (x, y) |--> 0
+ sage: (f * X.zero_function()).display()
+ (x, y) |--> 0
+ sage: (f * (1/f)).display()
+ (x, y) |--> 1
+
+ """
+ res = self._simplify(self._express * other._express)
+ if res == 0:
+ return self.parent().zero()
+ else:
+ return type(self)(self.parent(), res)
+
+ def _rmul_(self, other):
+ """
+ Return ``other * self``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: one = X.function_ring().one()
+ sage: 2 * one
+ 2
+ sage: f = X.function(x+y)
+ sage: (f * pi).display()
+ (x, y) |--> pi*(x + y)
+ sage: (x * f).display()
+ (x, y) |--> (x + y)*x
+ """
+ try:
+ other = SR(other)
+ except (TypeError, ValueError):
+ return
+ return type(self)(self.parent(), other * self._express)
+
+ def _lmul_(self, other):
+ """
+ Return ``self * other``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: one = X.function_ring().one()
+ sage: one * 2
+ 2
+ sage: f = X.function(x+y)
+ sage: (f * 2).display()
+ (x, y) |--> 2*x + 2*y
+ sage: (f * pi).display()
+ (x, y) |--> pi*(x + y)
+ """
+ try:
+ other = SR(other)
+ except (TypeError, ValueError):
+ return
+ return type(self)(self.parent(), self._express * other)
+
+ 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::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y)
+ sage: g = X.function(1+x^2+y^2)
+ sage: s = f._div_(g); s.display()
+ (x, y) |--> (x + y)/(x^2 + y^2 + 1)
+ sage: type(s)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: f / X.zero_function()
+ Traceback (most recent call last):
+ ...
+ ZeroDivisionError: division of a coordinate function by zero
+ sage: (f / 1).display()
+ (x, y) |--> x + y
+ sage: (f / 2).display()
+ (x, y) |--> 1/2*x + 1/2*y
+ sage: (f / pi).display()
+ (x, y) |--> (x + y)/pi
+ sage: (f / (1+x^2)).display()
+ (x, y) |--> (x + y)/(x^2 + 1)
+ sage: (f / (1+x^2)).display()
+ (x, y) |--> (x + y)/(x^2 + 1)
+ sage: (f / g) == ~(g / f)
+ True
+
+ """
+ if other._express.is_zero():
+ raise ZeroDivisionError("division of a coordinate function by zero")
+ res = self._simplify(self._express / SR(other))
+ if res == 0:
+ return self.parent().zero()
+ else:
+ return type(self)(self.parent(), res)
+
+ def exp(self):
+ r"""
+ Exponential of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\exp(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y)
+ sage: f.exp()
+ e^(x + y)
+ sage: exp(f) # equivalent to f.exp()
+ e^(x + y)
+ sage: exp(f).display()
+ (x, y) |--> e^(x + y)
+ sage: exp(X.zero_function())
+ 1
+
+ """
+ return type(self)(self.parent(), self._simplify(self._express.exp()))
+
+ 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
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y)
+ sage: f.log()
+ log(x + y)
+ sage: log(f) # equivalent to f.log()
+ log(x + y)
+ sage: log(f).display()
+ (x, y) |--> log(x + y)
+ sage: f.log(2)
+ log(x + y)/log(2)
+ sage: log(f, 2)
+ log(x + y)/log(2)
+
+ """
+ return type(self)(self.parent(), self._simplify(self._express.log(base)))
+
+ 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
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y)
+ sage: f.__pow__(3)
+ x^3 + 3*x^2*y + 3*x*y^2 + y^3
+ sage: f^3 # equivalent to f.__pow__(3)
+ x^3 + 3*x^2*y + 3*x*y^2 + y^3
+ sage: f.__pow__(3).display()
+ (x, y) |--> x^3 + 3*x^2*y + 3*x*y^2 + y^3
+ sage: pow(f,3).display()
+ (x, y) |--> x^3 + 3*x^2*y + 3*x*y^2 + y^3
+ sage: (f^3).display()
+ (x, y) |--> x^3 + 3*x^2*y + 3*x*y^2 + y^3
+ sage: pow(X.zero_function(), 3).display()
+ (x, y) |--> 0
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(pow(self._express, exponent)))
+
+ def sqrt(self):
+ r"""
+ Square root of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\sqrt{f}`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x+y)
+ sage: f.sqrt()
+ sqrt(x + y)
+ sage: sqrt(f) # equivalent to f.sqrt()
+ sqrt(x + y)
+ sage: sqrt(f).display()
+ (x, y) |--> sqrt(x + y)
+ sage: sqrt(X.zero_function()).display()
+ (x, y) |--> 0
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.sqrt()))
+
+ def cos(self):
+ r"""
+ Cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\cos(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.cos()
+ cos(x*y)
+ sage: cos(f) # equivalent to f.cos()
+ cos(x*y)
+ sage: cos(f).display()
+ (x, y) |--> cos(x*y)
+ sage: cos(X.zero_function()).display()
+ (x, y) |--> 1
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.cos()))
+
+ def sin(self):
+ r"""
+ Sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\sin(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.sin()
+ sin(x*y)
+ sage: sin(f) # equivalent to f.sin()
+ sin(x*y)
+ sage: sin(f).display()
+ (x, y) |--> sin(x*y)
+ sage: sin(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.sin()))
+
+ def tan(self):
+ r"""
+ Tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\tan(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.tan()
+ sin(x*y)/cos(x*y)
+ sage: tan(f) # equivalent to f.tan()
+ sin(x*y)/cos(x*y)
+ sage: tan(f).display()
+ (x, y) |--> sin(x*y)/cos(x*y)
+ sage: tan(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.tan()))
+
+ def arccos(self):
+ r"""
+ Arc cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\arccos(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.arccos()
+ arccos(x*y)
+ sage: arccos(f) # equivalent to f.arccos()
+ arccos(x*y)
+ sage: acos(f) # equivalent to f.arccos()
+ arccos(x*y)
+ sage: arccos(f).display()
+ (x, y) |--> arccos(x*y)
+ sage: arccos(X.zero_function()).display()
+ (x, y) |--> 1/2*pi
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.arccos()))
+
+ def arcsin(self):
+ r"""
+ Arc sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\arcsin(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.arcsin()
+ arcsin(x*y)
+ sage: arcsin(f) # equivalent to f.arcsin()
+ arcsin(x*y)
+ sage: asin(f) # equivalent to f.arcsin()
+ arcsin(x*y)
+ sage: arcsin(f).display()
+ (x, y) |--> arcsin(x*y)
+ sage: arcsin(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.arcsin()))
+
+ def arctan(self):
+ r"""
+ Arc tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\arctan(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.arctan()
+ arctan(x*y)
+ sage: arctan(f) # equivalent to f.arctan()
+ arctan(x*y)
+ sage: atan(f) # equivalent to f.arctan()
+ arctan(x*y)
+ sage: arctan(f).display()
+ (x, y) |--> arctan(x*y)
+ sage: arctan(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.arctan()))
+
+ def cosh(self):
+ r"""
+ Hyperbolic cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\cosh(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.cosh()
+ cosh(x*y)
+ sage: cosh(f) # equivalent to f.cosh()
+ cosh(x*y)
+ sage: cosh(f).display()
+ (x, y) |--> cosh(x*y)
+ sage: cosh(X.zero_function()).display()
+ (x, y) |--> 1
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.cosh()))
+
+ def sinh(self):
+ r"""
+ Hyperbolic sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\sinh(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.sinh()
+ sinh(x*y)
+ sage: sinh(f) # equivalent to f.sinh()
+ sinh(x*y)
+ sage: sinh(f).display()
+ (x, y) |--> sinh(x*y)
+ sage: sinh(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.sinh()))
+
+ def tanh(self):
+ r"""
+ Hyperbolic tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\tanh(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.tanh()
+ sinh(x*y)/cosh(x*y)
+ sage: tanh(f) # equivalent to f.tanh()
+ sinh(x*y)/cosh(x*y)
+ sage: tanh(f).display()
+ (x, y) |--> sinh(x*y)/cosh(x*y)
+ sage: tanh(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.tanh()))
+
+ def arccosh(self):
+ r"""
+ Inverse hyperbolic cosine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{arcosh}(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.arccosh()
+ arccosh(x*y)
+ sage: arccosh(f) # equivalent to f.arccosh()
+ arccosh(x*y)
+ sage: acosh(f) # equivalent to f.arccosh()
+ arccosh(x*y)
+ sage: arccosh(f).display()
+ (x, y) |--> arccosh(x*y)
+ sage: arccosh(X.function(1)) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.arccosh()))
+
+ def arcsinh(self):
+ r"""
+ Inverse hyperbolic sine of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{arsinh}(f)`, where `f` is the current
+ coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.arcsinh()
+ arcsinh(x*y)
+ sage: arcsinh(f) # equivalent to f.arcsinh()
+ arcsinh(x*y)
+ sage: asinh(f) # equivalent to f.arcsinh()
+ arcsinh(x*y)
+ sage: arcsinh(f).display()
+ (x, y) |--> arcsinh(x*y)
+ sage: arcsinh(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.arcsinh()))
+
+ def arctanh(self):
+ r"""
+ Inverse hyperbolic tangent of ``self``.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{artanh}(f)`, where `f` is the
+ current coordinate function
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x*y)
+ sage: f.arctanh()
+ arctanh(x*y)
+ sage: arctanh(f) # equivalent to f.arctanh()
+ arctanh(x*y)
+ sage: atanh(f) # equivalent to f.arctanh()
+ arctanh(x*y)
+ sage: arctanh(f).display()
+ (x, y) |--> arctanh(x*y)
+ sage: arctanh(X.zero_function()) == X.zero_function()
+ True
+
+ """
+ return type(self)(self.parent(),
+ self._simplify(self._express.arctanh()))
+
+ # -------------------------------------
+ # Methods specific to CoordFunctionSymb
+ # -------------------------------------
+
+ def _del_derived(self):
+ r"""
+ Delete the derived quantities.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(cos(x*y))
+ sage: f._der
+ sage: f.diff(x)
+ -y*sin(x*y)
+ sage: f._der
+ [-y*sin(x*y), -x*sin(x*y)]
+ sage: f._del_derived()
+ sage: f._der
+
+ """
+ self._der = None # reset of the partial derivatives
+
+ def simplify(self):
+ r"""
+ Simplify the coordinate expression of ``self``.
+
+ For details about the employed chain of simplifications, see
+ :func:`~sage.manifolds.utilities.simplify_chain_real` for coordinate
+ functions on real manifolds and
+ :func:`~sage.manifolds.utilities.simplify_chain_generic` for the
+ generic case.
+
+ OUTPUT:
+
+ - ``self`` with its expression simplified
+
+ EXAMPLES:
+
+ Simplification of a 2-dimension coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(cos(x)^2+sin(x)^2 + sqrt(x^2))
+ sage: f.display()
+ (x, y) |--> cos(x)^2 + sin(x)^2 + sqrt(x^2)
+ sage: f.simplify()
+ abs(x) + 1
+
+ The method ``simplify()`` has changed the expression of ``f``::
+
+ sage: f.display()
+ (x, y) |--> abs(x) + 1
+
+ Another example::
+
+ sage: f = X.function((x^2-1)/(x+1))
+ sage: f
+ (x^2 - 1)/(x + 1)
+ sage: f.simplify()
+ x - 1
+
+ Examples taking into account the declared range of a coordinate::
+
+ sage: M = Manifold(2, 'M_1', structure='topological')
+ sage: X.<x,y> = M.chart('x:(0,+oo) y')
+ sage: f = X.function(sqrt(x^2))
+ sage: f
+ x
+ sage: f.simplify()
+ x
+
+ ::
+
+ sage: forget() # to clear the previous assumption on x
+ sage: M = Manifold(2, 'M_2', structure='topological')
+ sage: X.<x,y> = M.chart('x:(-oo,0) y')
+ sage: f = X.function(sqrt(x^2))
+ sage: f
+ sqrt(x^2)
+ sage: f.simplify()
+ -x
+
+ """
+ self._express = self._simplify(self._express)
+ self._del_derived()
+ return self
+
+ def factor(self):
+ r"""
+ Factorize the coordinate expression of ``self``.
+
+ OUTPUT:
+
+ - ``self`` with its expression factorized
+
+ EXAMPLES:
+
+ Factorization of a 2-dimensional coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x^2 + 2*x*y + y^2)
+ sage: f.display()
+ (x, y) |--> x^2 + 2*x*y + y^2
+ sage: f.factor()
+ (x + y)^2
+
+ The method ``factor()`` has changed the expression of ``f``::
+
+ sage: f.display()
+ (x, y) |--> (x + y)^2
+
+ """
+ self._express = self._express.factor()
+ self._del_derived()
+ return self
+
+ def expand(self):
+ r"""
+ Expand the coordinate expression of ``self``.
+
+ OUTPUT:
+
+ - ``self`` with its expression expanded
+
+ EXAMPLES:
+
+ Expanding a 2-dimensional coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function((x - y)^2)
+ sage: f.display()
+ (x, y) |--> (x - y)^2
+ sage: f.expand()
+ x^2 - 2*x*y + y^2
+
+ The method ``expand()`` has changed the expression of ``f``::
+
+ sage: f.display()
+ (x, y) |--> x^2 - 2*x*y + y^2
+
+ """
+ self._express = self._express.expand()
+ self._del_derived()
+ return self
+
+ def collect(self, s):
+ r"""
+ Collect the coefficients of `s` in the expression of ``self``
+ into a group.
+
+ INPUT:
+
+ - ``s`` -- the symbol whose coefficients will be collected
+
+ OUTPUT:
+
+ - ``self`` with the coefficients of ``s`` grouped in
+ its expression
+
+ EXAMPLES:
+
+ Action on a 2-dimensional coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x^2*y + x*y + (x*y)^2)
+ sage: f.display()
+ (x, y) |--> x^2*y^2 + x^2*y + x*y
+ sage: f.collect(y)
+ x^2*y^2 + (x^2 + x)*y
+
+ The method ``collect()`` has changed the expression of ``f``::
+
+ sage: f.display()
+ (x, y) |--> x^2*y^2 + (x^2 + x)*y
+
+ """
+ self._express = self._express.collect(s)
+ self._del_derived()
+ return self
+
+ def collect_common_factors(self):
+ r"""
+ Collect common factors in the expression of ``self``.
+
+ This method does not perform a full factorization but only looks
+ for factors which are already explicitly present.
+
+ OUTPUT:
+
+ - ``self`` with the common factors collected in
+ its expression
+
+ EXAMPLES:
+
+ Action on a 2-dimensional coordinate function::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = X.function(x/(x^2*y + x*y))
+ sage: f.display()
+ (x, y) |--> x/(x^2*y + x*y)
+ sage: f.collect_common_factors()
+ 1/((x + 1)*y)
+
+
+ The method ``collect_common_factors()`` has changed the expression
+ of ``f``::
+
+ sage: f.display()
+ (x, y) |--> 1/((x + 1)*y)
+
+ """
+ self._express = self._express.collect_common_factors()
+ self._del_derived()
+ return self
+
+
+class CoordFunctionSymbRing(Parent, UniqueRepresentation):
+ """
+ Ring of all symbolic coordinate functions on a chart.
+
+ INPUT:
+
+ - ``chart`` -- a coordinate chart, as an instance of class
+ :class:`~sage.manifolds.chart.Chart`
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: FR = X.function_ring(); FR
+ Ring of coordinate functions on Chart (M, (x, y))
+ sage: type(FR)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category'>
+ sage: FR.category()
+ Category of commutative algebras over Symbolic Ring
+
+ """
+ def __init__(self, chart):
+ """
+ Initialize ``self``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: FR = X.function_ring()
+ sage: TestSuite(FR).run()
+ """
+ self._chart = chart
+ Parent.__init__(self, base=SR, category=CommutativeAlgebras(SR))
+
+ def _repr_(self):
+ """
+ Return a string representation of ``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))
+ """
+ return "Ring of coordinate functions on {}".format(self._chart)
+
+ @cached_method
+ def zero(self):
+ """
+ Return the constant function `0` in ``self``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: FR = X.function_ring()
+ sage: FR.zero()
+ 0
+
+ sage: M = Manifold(2, 'M', structure='topological', field=Qp(5))
+ sage: X.<x,y> = M.chart()
+ sage: X.function_ring().zero()
+ 0
+ """
+ if self._chart.manifold().base_field_type() in ['real', 'complex']:
+ elt = SR.zero()
+ else:
+ elt = self._chart.manifold().base_field().zero()
+ return self.element_class(self, elt)
+
+ @cached_method
+ def one(self):
+ """
+ Return the constant function `1` in ``self``.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: FR = X.function_ring()
+ sage: FR.one()
+ 1
+
+ sage: M = Manifold(2, 'M', structure='topological', field=Qp(5))
+ sage: X.<x,y> = M.chart()
+ sage: X.function_ring().one()
+ 1 + O(5^20)
+ """
+ if self._chart.manifold().base_field_type() in ['real', 'complex']:
+ elt = SR.one()
+ else:
+ elt = self._chart.manifold().base_field().one()
+ return self.element_class(self, elt)
+
+ def from_base_ring(self, r):
+ """
+ Return the canonical embedding of ``r`` into ``self``.
+
+ INPUT:
+
+ - ``r`` -- an element of ``self.base_ring()``
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: FR = X.function_ring()
+ sage: f = FR.from_base_ring(x*y)
+ sage: f.display()
+ (x, y) |--> x*y
+
+ """
+ return self.element_class(self, r)
+
+ def is_integral_domain(self):
+ """
+ Return ``False`` as ``self`` is not an integral domain.
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: FR = X.function_ring()
+ sage: FR.is_integral_domain()
+ False
+ sage: FR.is_field()
+ False
+ """
+ return False
+
+ is_field = is_integral_domain
+
+ Element = CoordFunctionSymb
+
diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py
index 60c17e6..c2b33fc 100644
--- a/src/sage/manifolds/manifold.py
+++ b/src/sage/manifolds/manifold.py
@@ -169,6 +169,27 @@ Similarly::
sage: stereoN(S)
(0, 0)
+A continuous map `S^2 \to \RR` (scalar field)::
+
+ sage: f = M.scalar_field({stereoN: atan(x^2+y^2), stereoS: pi/2-atan(u^2+v^2)},
+ ....: name='f')
+ sage: f
+ Scalar field f on the 2-dimensional topological manifold S^2
+ sage: f.display()
+ f: S^2 --> R
+ on U: (x, y) |--> arctan(x^2 + y^2)
+ on V: (u, v) |--> 1/2*pi - arctan(u^2 + v^2)
+ sage: f(p)
+ arctan(5)
+ sage: f(N)
+ 1/2*pi
+ sage: f(S)
+ 0
+ sage: f.parent()
+ Algebra of scalar fields on the 2-dimensional topological manifold S^2
+ sage: f.parent().category()
+ Category of commutative algebras over Symbolic Ring
+
.. RUBRIC:: Example 2: the Riemann sphere as a topological manifold of
dimension 1 over `\CC`
@@ -256,6 +277,25 @@ The following subsets and charts have been defined::
sage: M.atlas()
[Chart (U, (z,)), Chart (V, (w,)), Chart (A, (z,)), Chart (A, (w,))]
+A constant map `\CC^* \rightarrow \CC`::
+
+ sage: f = M.constant_scalar_field(3+2*I, name='f'); f
+ Scalar field f on the Complex 1-dimensional topological manifold C*
+ sage: f.display()
+ f: C* --> C
+ on U: z |--> 2*I + 3
+ on V: w |--> 2*I + 3
+ sage: f(O)
+ 2*I + 3
+ sage: f(i)
+ 2*I + 3
+ sage: f(inf)
+ 2*I + 3
+ sage: f.parent()
+ Algebra of scalar fields on the Complex 1-dimensional topological
+ manifold C*
+ sage: f.parent().category()
+ Category of commutative algebras over Symbolic Ring
AUTHORS:
@@ -295,9 +335,70 @@ from sage.rings.real_mpfr import RR, RealField_class
from sage.rings.complex_field import ComplexField_class
from sage.misc.prandom import getrandbits
from sage.rings.integer import Integer
+from sage.structure.global_options import GlobalOptions
from sage.manifolds.subset import ManifoldSubset
-from sage.manifolds.structure import TopologicalStructure, \
- RealTopologicalStructure
+from sage.manifolds.structure import (TopologicalStructure,
+ RealTopologicalStructure)
+
+#############################################################################
+## Global options
+
+ManifoldOptions=GlobalOptions(name='manifolds',
+ doc=r"""
+ Sets and displays the global options for manifolds. If no parameters
+ are set, then the function returns a copy of the options dictionary.
+
+ The ``options`` to manifolds can be accessed as the method
+ :obj:`Manifold.global_options`.
+ """,
+ end_doc=r"""
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: g = function('g')(x, y)
+
+ 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
+
+ sage: latex(f)
+ \frac{\partial\,g}{\partial x} + \frac{\partial\,g}{\partial y}
+
+ One can switch to Pynac notation by changing ``textbook_output``
+ to ``False``::
+
+ sage: Manifold.global_options(textbook_output=False)
+ sage: f
+ D[0](g)(x, y) + D[1](g)(x, y)
+ 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()
+
+ 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::
+
+ sage: f = X.function(function('u')(x, y) * function('v')(x, y))
+ sage: f
+ u(x, y)*v(x, y)
+
+ We can switch it off by::
+
+ sage: M.global_options(omit_function_arguments=True)
+ sage: f
+ u*v
+ sage: M.global_options.reset()
+ """,
+ textbook_output=dict(default=True,
+ description='textbook-like output instead of the Pynac output for derivatives',
+ checker=lambda x: isinstance(x, bool)),
+ omit_function_arguments=dict(default=False,
+ description='Determine if the arguments of symbolic functions are printed',
+ checker=lambda x: isinstance(x, bool)),
+)
#############################################################################
## Class
@@ -536,6 +637,12 @@ class TopologicalManifold(ManifoldSubset):
# List of charts that individually cover self, i.e. whose
# domains are self (if non-empty, self is a coordinate domain):
self._covering_charts = []
+ # Algebra of scalar fields defined on self:
+ self._scalar_field_algebra = self._structure.scalar_field_algebra(self)
+ # The zero scalar field:
+ self._zero_scalar_field = self._scalar_field_algebra.zero()
+ # The unit scalar field:
+ self._one_scalar_field = self._scalar_field_algebra.one()
def _repr_(self):
r"""
@@ -1003,28 +1110,20 @@ class TopologicalManifold(ManifoldSubset):
Index range on a 4-dimensional manifold::
sage: M = Manifold(4, 'M', structure='topological')
- sage: for i in M.irange():
- ....: print i,
- ....:
- 0 1 2 3
- sage: for i in M.irange(2):
- ....: print i,
- ....:
- 2 3
+ sage: [i for i in M.irange()]
+ [0, 1, 2, 3]
+ sage: [i for i in M.irange(2)]
+ [2, 3]
sage: list(M.irange())
[0, 1, 2, 3]
Index range on a 4-dimensional manifold with starting index=1::
sage: M = Manifold(4, 'M', structure='topological', start_index=1)
- sage: for i in M.irange():
- ....: print i,
- ....:
- 1 2 3 4
- sage: for i in M.irange(2):
- ....: print i,
- ....:
- 2 3 4
+ sage: [i for i in M.irange()]
+ [1, 2, 3, 4]
+ sage: [i for i in M.irange(2)]
+ [2, 3, 4]
In general, one has always::
@@ -1061,7 +1160,7 @@ class TopologicalManifold(ManifoldSubset):
sage: M = Manifold(2, 'M', structure='topological', start_index=1)
sage: for ind in M.index_generator(2):
- ....: print ind
+ ....: print(ind)
....:
(1, 1)
(1, 2)
@@ -1071,15 +1170,17 @@ class TopologicalManifold(ManifoldSubset):
Loops can be nested::
sage: for ind1 in M.index_generator(2):
- ....: print ind1, " : ",
- ....: for ind2 in M.index_generator(2):
- ....: print ind2,
- ....: print ""
+ ....: print("{} : ".format(ind1))
+ ....: [ind2 for ind2 in M.index_generator(2)]
....:
- (1, 1) : (1, 1) (1, 2) (2, 1) (2, 2)
- (1, 2) : (1, 1) (1, 2) (2, 1) (2, 2)
- (2, 1) : (1, 1) (1, 2) (2, 1) (2, 2)
- (2, 2) : (1, 1) (1, 2) (2, 1) (2, 2)
+ (1, 1) :
+ [(1, 1), (1, 2), (2, 1), (2, 2)]
+ (1, 2) :
+ [(1, 1), (1, 2), (2, 1), (2, 2)]
+ (2, 1) :
+ [(1, 1), (1, 2), (2, 1), (2, 2)]
+ (2, 2) :
+ [(1, 1), (1, 2), (2, 1), (2, 2)]
"""
si = self._sindex
@@ -1488,6 +1589,247 @@ class TopologicalManifold(ManifoldSubset):
"""
return True
+ def scalar_field_algebra(self):
+ r"""
+ Return the algebra of scalar fields defined the manifold.
+
+ See :class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`
+ for a complete documentation.
+
+ OUTPUT:
+
+ - instance of
+ :class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`
+ representing the algebra `C^0(U)` of all scalar fields defined
+ on `U` = ``self``
+
+ EXAMPLES:
+
+ Scalar algebra of a 3-dimensional open subset::
+
+ sage: M = Manifold(3, 'M', structure='topological')
+ sage: U = M.open_subset('U')
+ sage: CU = U.scalar_field_algebra() ; CU
+ Algebra of scalar fields on the Open subset U of the 3-dimensional topological manifold M
+ sage: CU.category()
+ Category of commutative algebras over Symbolic Ring
+ sage: CU.zero()
+ Scalar field zero on the Open subset U of the 3-dimensional topological manifold M
+
+ The output is cached::
+
+ sage: U.scalar_field_algebra() is CU
+ True
+
+ """
+ return self._scalar_field_algebra
+
+ def scalar_field(self, coord_expression=None, chart=None, name=None,
+ latex_name=None):
+ r"""
+ Define a scalar field on the manifold.
+
+ See :class:`~sage.manifolds.scalarfield.ScalarField` for a complete
+ documentation.
+
+ INPUT:
+
+ - ``coord_expression`` -- (default: ``None``) coordinate expression(s)
+ of the scalar field; this can be either
+
+ * a single coordinate expression; if the argument ``chart`` is
+ ``'all'``, this expression is set to all the charts defined
+ on the open set; otherwise, the expression is set in the
+ specific chart provided by the argument ``chart``
+ * a dictionary of coordinate expressions, with the charts as keys
+
+ - ``chart`` -- (default: ``None``) chart defining the coordinates
+ used in ``coord_expression`` when the latter is a single
+ coordinate expression; if ``None``, the default chart of the
+ open set is assumed; if ``chart=='all'``, ``coord_expression`` is
+ assumed to be independent of the chart (constant scalar field)
+
+ - ``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``
+
+ If ``coord_expression`` is ``None`` or does not fully specified the
+ scalar field, other coordinate expressions can be added subsequently
+ by means of the methods
+ :meth:`~sage.manifolds.scalarfield.ScalarField.add_expr`,
+ :meth:`~sage.manifolds.scalarfield.ScalarField.add_expr_by_continuation`,
+ or :meth:`~sage.manifolds.scalarfield.ScalarField.set_expr`
+
+ OUTPUT:
+
+ - instance of :class:`~sage.manifolds.scalarfield.ScalarField`
+ representing the defined scalar field
+
+ EXAMPLES:
+
+ A scalar field defined by its coordinate expression in the open
+ set's default chart::
+
+ sage: M = Manifold(3, 'M', structure='topological')
+ sage: U = M.open_subset('U')
+ sage: c_xyz.<x,y,z> = U.chart()
+ sage: f = U.scalar_field(sin(x)*cos(y) + z, name='F'); f
+ Scalar field F on the Open subset U of the 3-dimensional topological manifold M
+ sage: f.display()
+ F: U --> R
+ (x, y, z) |--> cos(y)*sin(x) + z
+ sage: f.parent()
+ Algebra of scalar fields on the Open subset U of the 3-dimensional topological manifold M
+ sage: f in U.scalar_field_algebra()
+ True
+
+ Equivalent definition with the chart specified::
+
+ sage: f = U.scalar_field(sin(x)*cos(y) + z, chart=c_xyz, name='F')
+ sage: f.display()
+ F: U --> R
+ (x, y, z) |--> cos(y)*sin(x) + z
+
+ Equivalent definition with a dictionary of coordinate expression(s)::
+
+ sage: f = U.scalar_field({c_xyz: sin(x)*cos(y) + z}, name='F')
+ sage: f.display()
+ F: U --> R
+ (x, y, z) |--> cos(y)*sin(x) + z
+
+ See the documentation of class
+ :class:`~sage.manifolds.scalarfield.ScalarField` for more
+ examples.
+
+ .. SEEALSO::
+
+ :meth:`constant_scalar_field`, :meth:`zero_scalar_field`,
+ :meth:`one_scalar_field`
+
+ """
+ if isinstance(coord_expression, dict):
+ # check validity of entry
+ for chart in coord_expression:
+ if not chart._domain.is_subset(self):
+ raise ValueError("the {} is not defined ".format(chart) +
+ "on some subset of the " + str(self))
+ alg = self.scalar_field_algebra()
+ return alg.element_class(alg, coord_expression=coord_expression,
+ name=name, latex_name=latex_name, chart=chart)
+
+ def constant_scalar_field(self, value, name=None, latex_name=None):
+ r"""
+ Define a constant scalar field on the manifold.
+
+ INPUT:
+
+ - ``value`` -- constant value of the scalar field, either a numerical
+ value or a symbolic expression not involving any chart coordinates
+ - ``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:
+
+ - instance of :class:`~sage.manifolds.scalarfield.ScalarField`
+ representing the scalar field whose constant value is ``value``
+
+ EXAMPLES:
+
+ A constant scalar field on the 2-sphere::
+
+ sage: M = Manifold(2, 'M', structure='topological') # the 2-dimensional sphere S^2
+ sage: U = M.open_subset('U') # complement of the North pole
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
+ sage: V = M.open_subset('V') # complement of the South pole
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
+ ....: intersection_name='W',
+ ....: restrictions1= x^2+y^2!=0,
+ ....: restrictions2= u^2+v^2!=0)
+ sage: uv_to_xy = xy_to_uv.inverse()
+ sage: f = M.constant_scalar_field(-1) ; f
+ Scalar field on the 2-dimensional topological manifold M
+ sage: f.display()
+ M --> R
+ on U: (x, y) |--> -1
+ on V: (u, v) |--> -1
+
+ We have::
+
+ sage: f.restrict(U) == U.constant_scalar_field(-1)
+ True
+ sage: M.constant_scalar_field(0) is M.zero_scalar_field()
+ True
+
+ .. SEEALSO::
+
+ :meth:`zero_scalar_field`, :meth:`one_scalar_field`
+ """
+ if value == 0:
+ return self.zero_scalar_field()
+ alg = self.scalar_field_algebra()
+ return alg.element_class(alg, coord_expression=value, name=name,
+ latex_name=latex_name, chart='all')
+
+ def zero_scalar_field(self):
+ r"""
+ Return the zero scalar field defined on the manifold.
+
+ OUTPUT:
+
+ - instance of :class:`~sage.manifolds.scalarfield.ScalarField`
+ representing the constant scalar field with value 0
+
+ EXAMPLE::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.zero_scalar_field() ; f
+ Scalar field zero on the 2-dimensional topological manifold M
+ sage: f.display()
+ zero: M --> R
+ (x, y) |--> 0
+ sage: f.parent()
+ Algebra of scalar fields on the 2-dimensional topological manifold M
+ sage: f is M.scalar_field_algebra().zero()
+ True
+
+ """
+ return self._zero_scalar_field
+
+ def one_scalar_field(self):
+ r"""
+ Return the constant scalar field with value the unit element of the
+ manifold's base field.
+
+ OUTPUT:
+
+ - instance of :class:`~sage.manifolds.scalarfield.ScalarField`
+ representing the constant scalar field with value the unit element
+ of the manifold's base field
+
+ EXAMPLE::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.one_scalar_field(); f
+ Scalar field 1 on the 2-dimensional topological manifold M
+ sage: f.display()
+ 1: M --> R
+ (x, y) |--> 1
+ sage: f.parent()
+ Algebra of scalar fields on the 2-dimensional topological manifold M
+ sage: f is M.scalar_field_algebra().one()
+ True
+
+ """
+ return self._one_scalar_field
+
+ global_options = ManifoldOptions
+
##############################################################################
## Constructor function
@@ -1657,3 +1999,5 @@ def Manifold(dim, name, latex_name=None, field='real', structure='smooth',
latex_name=latex_name, start_index=start_index,
unique_tag=getrandbits(128)*time())
+Manifold.global_options = ManifoldOptions
+
diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py
index 8fdb6d4..2959942 100644
--- a/src/sage/manifolds/point.py
+++ b/src/sage/manifolds/point.py
@@ -14,9 +14,9 @@ AUTHORS:
REFERENCES:
-- [Lee11]_ J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed.,
+- [Lee11]_ \J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed.,
Springer (New York) (2011)
-- [Lee13]_ J.M. Lee : *Introduction to Smooth Manifolds*, 2nd ed.,
+- [Lee13]_ \J.M. Lee : *Introduction to Smooth Manifolds*, 2nd ed.,
Springer (New York, 2013)
EXAMPLES:
diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py
new file mode 100644
index 0000000..cb67373
--- /dev/null
+++ b/src/sage/manifolds/scalarfield.py
@@ -0,0 +1,2773 @@
+r"""
+Scalar Fields
+
+Given a topological manifold `M` over a topological field `K` (in most
+applications, `K = \RR` or `K = \CC`), a *scalar field* on `M` is a
+continuous map
+
+.. MATH::
+
+ f: M \longrightarrow K
+
+Scalar fields are implemented by the class :class:`ScalarField`.
+
+AUTHORS:
+
+- Eric Gourgoulhon, Michal Bejger (2013-2015): initial version
+- Travis Scrimshaw (2016): review tweaks
+
+REFERENCES:
+
+- [Lee11]_ \J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed.,
+ Springer (New York) (2011)
+- [KN63]_ \S. Kobayashi & K. Nomizu : *Foundations of Differential Geometry*,
+ vol. 1, Interscience Publishers (New York) (1963)
+
+"""
+
+#******************************************************************************
+# 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.structure.element import CommutativeAlgebraElement
+from sage.rings.integer import Integer
+from sage.symbolic.expression import Expression
+from sage.manifolds.coord_func import CoordFunction
+
+class ScalarField(CommutativeAlgebraElement):
+ r"""
+ Scalar field on a topological manifold.
+
+ Given a topological manifold `M` over a topological field `K` (in most
+ applications, `K = \RR` or `K = \CC`), a *scalar field on* `M` is a
+ continuous map
+
+ .. MATH::
+
+ f: M \longrightarrow K.
+
+ A scalar field on `M` is an element of the commutative algebra
+ `C^0(M)` (see
+ :class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`).
+
+ INPUT:
+
+ - ``parent`` -- the algebra of scalar fields containing the scalar field
+ (must be an instance of class
+ :class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`)
+
+ - ``coord_expression`` -- (default: ``None``) coordinate expression(s) of
+ the scalar field; this can be either
+
+ * a dictionary of coordinate expressions in various charts on
+ the domain, with the charts as keys;
+ * a single coordinate expression; if the argument ``chart`` is
+ ``'all'``, this expression is set to all the charts defined
+ on the open set; otherwise, the expression is set in the
+ specific chart provided by the argument ``chart``
+
+ - ``chart`` -- (default: ``None``) chart defining the coordinates used
+ in ``coord_expression`` when the latter is a single coordinate
+ expression; if none is provided (default), the default chart of the
+ open set is assumed. If ``chart=='all'``, ``coord_expression`` is
+ assumed to be independent of the chart (constant scalar field).
+
+ - ``name`` -- (default: ``None``) string; name (symbol) given to the
+ scalar field
+
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
+ scalar field; if none is provided, the LaTeX symbol is set to ``name``
+
+ If ``coord_expression`` is ``None`` or incomplete, coordinate
+ expressions can be added after the creation of the object, by means of
+ the methods :meth:`add_expr`, :meth:`add_expr_by_continuation` and
+ :meth:`set_expr`.
+
+ EXAMPLES:
+
+ A scalar field on the 2-sphere::
+
+ sage: M = Manifold(2, 'M', structure='topological') # the 2-dimensional sphere S^2
+ sage: U = M.open_subset('U') # complement of the North pole
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
+ sage: V = M.open_subset('V') # complement of the South pole
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
+ ....: intersection_name='W',
+ ....: restrictions1= x^2+y^2!=0,
+ ....: restrictions2= u^2+v^2!=0)
+ sage: uv_to_xy = xy_to_uv.inverse()
+ sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2), c_uv: (u^2+v^2)/(1+u^2+v^2)},
+ ....: name='f') ; f
+ Scalar field f on the 2-dimensional topological manifold M
+ sage: f.display()
+ f: M --> R
+ on U: (x, y) |--> 1/(x^2 + y^2 + 1)
+ on V: (u, v) |--> (u^2 + v^2)/(u^2 + v^2 + 1)
+
+ For scalar fields defined by a single coordinate expression, the latter
+ can be passed instead of the dictionary over the charts::
+
+ sage: g = U.scalar_field(x*y, chart=c_xy, name='g') ; g
+ Scalar field g on the Open subset U of the 2-dimensional topological
+ manifold M
+
+ The above is indeed equivalent to::
+
+ sage: g = U.scalar_field({c_xy: x*y}, name='g') ; g
+ Scalar field g on the Open subset U of the 2-dimensional topological
+ manifold M
+
+ Since ``c_xy`` is the default chart of ``U``, the argument ``chart`` can
+ be skipped::
+
+ sage: g = U.scalar_field(x*y, name='g') ; g
+ Scalar field g on the Open subset U of the 2-dimensional topological
+ manifold M
+
+ The scalar field `g` is defined on `U` and has an expression in terms of
+ the coordinates `(u,v)` on `W=U\cap V`::
+
+ sage: g.display()
+ g: U --> R
+ (x, y) |--> x*y
+ on W: (u, v) |--> u*v/(u^4 + 2*u^2*v^2 + v^4)
+
+ Scalar fields on `M` can also be declared with a single chart::
+
+ sage: f = M.scalar_field(1/(1+x^2+y^2), chart=c_xy, name='f') ; f
+ Scalar field f on the 2-dimensional topological manifold M
+
+ Their definition must then be completed by providing the expressions on
+ other charts, via the method :meth:`add_expr`, to get a global cover of
+ the manifold::
+
+ sage: f.add_expr((u^2+v^2)/(1+u^2+v^2), chart=c_uv)
+ sage: f.display()
+ f: M --> R
+ on U: (x, y) |--> 1/(x^2 + y^2 + 1)
+ on V: (u, v) |--> (u^2 + v^2)/(u^2 + v^2 + 1)
+
+ We can even first declare the scalar field without any coordinate
+ expression and provide them subsequently::
+
+ sage: f = M.scalar_field(name='f')
+ sage: f.add_expr(1/(1+x^2+y^2), chart=c_xy)
+ sage: f.add_expr((u^2+v^2)/(1+u^2+v^2), chart=c_uv)
+ sage: f.display()
+ f: M --> R
+ on U: (x, y) |--> 1/(x^2 + y^2 + 1)
+ on V: (u, v) |--> (u^2 + v^2)/(u^2 + v^2 + 1)
+
+ We may also use the method :meth:`add_expr_by_continuation` to complete
+ the coordinate definition using the analytic continuation from domains in
+ which charts overlap::
+
+ sage: f = M.scalar_field(1/(1+x^2+y^2), chart=c_xy, name='f') ; f
+ Scalar field f on the 2-dimensional topological manifold M
+ sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
+ sage: f.display()
+ f: M --> R
+ on U: (x, y) |--> 1/(x^2 + y^2 + 1)
+ on V: (u, v) |--> (u^2 + v^2)/(u^2 + v^2 + 1)
+
+ A scalar field can also be defined by some unspecified function of the
+ coordinates::
+
+ sage: h = U.scalar_field(function('H')(x, y), name='h') ; h
+ Scalar field h on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: h.display()
+ h: U --> R
+ (x, y) |--> H(x, y)
+ on W: (u, v) |--> H(u/(u^2 + v^2), v/(u^2 + v^2))
+
+ We may use the argument ``latex_name`` to specify the LaTeX symbol denoting
+ the scalar field if the latter is different from ``name``::
+
+ sage: latex(f)
+ f
+ sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2), c_uv: (u^2+v^2)/(1+u^2+v^2)},
+ ....: name='f', latex_name=r'\mathcal{F}')
+ sage: latex(f)
+ \mathcal{F}
+
+ The coordinate expression in a given chart is obtained via the method
+ :meth:`expr`, which returns a symbolic expression::
+
+ sage: f.expr(c_uv)
+ (u^2 + v^2)/(u^2 + v^2 + 1)
+ sage: type(f.expr(c_uv))
+ <type 'sage.symbolic.expression.Expression'>
+
+ The method :meth:`coord_function` returns instead a function of the
+ chart coordinates, i.e. an instance of
+ :class:`~sage.manifolds.coord_func.CoordFunction`::
+
+ sage: f.coord_function(c_uv)
+ (u^2 + v^2)/(u^2 + v^2 + 1)
+ sage: type(f.coord_function(c_uv))
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymbRing_with_category.element_class'>
+ sage: f.coord_function(c_uv).display()
+ (u, v) |--> (u^2 + v^2)/(u^2 + v^2 + 1)
+
+ The value returned by the method :meth:`expr` is actually the coordinate
+ expression of the chart function::
+
+ sage: f.expr(c_uv) is f.coord_function(c_uv).expr()
+ True
+
+ A constant scalar field is declared by setting the argument ``chart`` to
+ ``'all'``::
+
+ sage: c = M.scalar_field(2, chart='all', name='c') ; c
+ Scalar field c on the 2-dimensional topological manifold M
+ sage: c.display()
+ c: M --> R
+ on U: (x, y) |--> 2
+ on V: (u, v) |--> 2
+
+ A shortcut is to use the method
+ :meth:`~sage.manifolds.manifold.TopologicalManifold.constant_scalar_field`::
+
+ sage: c == M.constant_scalar_field(2)
+ True
+
+ The constant value can be some unspecified parameter::
+
+ sage: var('a')
+ a
+ sage: c = M.constant_scalar_field(a, name='c') ; c
+ Scalar field c on the 2-dimensional topological manifold M
+ sage: c.display()
+ c: M --> R
+ on U: (x, y) |--> a
+ on V: (u, v) |--> a
+
+ A special case of constant field is the zero scalar field::
+
+ sage: zer = M.constant_scalar_field(0) ; zer
+ Scalar field zero on the 2-dimensional topological manifold M
+ sage: zer.display()
+ zero: M --> R
+ on U: (x, y) |--> 0
+ on V: (u, v) |--> 0
+
+ It can be obtained directly by means of the function
+ :meth:`~sage.manifolds.manifold.TopologicalManifold.zero_scalar_field`::
+
+ sage: zer is M.zero_scalar_field()
+ True
+
+ A third way is to get it as the zero element of the algebra `C^0(M)`
+ of scalar fields on `M` (see below)::
+
+ sage: zer is M.scalar_field_algebra().zero()
+ True
+
+ By definition, a scalar field acts on the manifold's points, sending
+ them to elements of the manifold's base field (real numbers in the
+ present case)::
+
+ sage: N = M.point((0,0), chart=c_uv) # the North pole
+ sage: S = M.point((0,0), chart=c_xy) # the South pole
+ sage: E = M.point((1,0), chart=c_xy) # a point at the equator
+ sage: f(N)
+ 0
+ sage: f(S)
+ 1
+ sage: f(E)
+ 1/2
+ sage: h(E)
+ H(1, 0)
+ sage: c(E)
+ a
+ sage: zer(E)
+ 0
+
+ A scalar field can be compared to another scalar field::
+
+ sage: f == g
+ False
+
+ ...to a symbolic expression::
+
+ sage: f == x*y
+ False
+ sage: g == x*y
+ True
+ sage: c == a
+ True
+
+ ...to a number::
+
+ sage: f == 2
+ False
+ sage: zer == 0
+ True
+
+ ...to anything else::
+
+ sage: f == M
+ False
+
+ Standard mathematical functions are implemented::
+
+ sage: sqrt(f)
+ Scalar field sqrt(f) on the 2-dimensional topological manifold M
+ sage: sqrt(f).display()
+ sqrt(f): M --> R
+ on U: (x, y) |--> 1/sqrt(x^2 + y^2 + 1)
+ on V: (u, v) |--> sqrt(u^2 + v^2)/sqrt(u^2 + v^2 + 1)
+
+ ::
+
+ sage: tan(f)
+ Scalar field tan(f) on the 2-dimensional topological manifold M
+ sage: tan(f).display()
+ tan(f): M --> R
+ on U: (x, y) |--> sin(1/(x^2 + y^2 + 1))/cos(1/(x^2 + y^2 + 1))
+ on V: (u, v) |--> sin((u^2 + v^2)/(u^2 + v^2 + 1))/cos((u^2 + v^2)/(u^2 + v^2 + 1))
+
+ .. RUBRIC:: Arithmetics of scalar fields
+
+ Scalar fields on `M` (resp. `U`) belong to the algebra `C^0(M)`
+ (resp. `C^0(U)`)::
+
+ sage: f.parent()
+ Algebra of scalar fields on the 2-dimensional topological manifold M
+ sage: f.parent() is M.scalar_field_algebra()
+ True
+ sage: g.parent()
+ Algebra of scalar fields on the Open subset U of the 2-dimensional
+ topological manifold M
+ sage: g.parent() is U.scalar_field_algebra()
+ True
+
+ Consequently, scalar fields can be added::
+
+ sage: s = f + c ; s
+ Scalar field f+c on the 2-dimensional topological manifold M
+ sage: s.display()
+ f+c: M --> R
+ on U: (x, y) |--> (a*x^2 + a*y^2 + a + 1)/(x^2 + y^2 + 1)
+ on V: (u, v) |--> ((a + 1)*u^2 + (a + 1)*v^2 + a)/(u^2 + v^2 + 1)
+
+ and subtracted::
+
+ sage: s = f - c ; s
+ Scalar field f-c on the 2-dimensional topological manifold M
+ sage: s.display()
+ f-c: M --> R
+ on U: (x, y) |--> -(a*x^2 + a*y^2 + a - 1)/(x^2 + y^2 + 1)
+ on V: (u, v) |--> -((a - 1)*u^2 + (a - 1)*v^2 + a)/(u^2 + v^2 + 1)
+
+ Some tests::
+
+ sage: f + zer == f
+ True
+ sage: f - f == zer
+ True
+ sage: f + (-f) == zer
+ True
+ sage: (f+c)-f == c
+ True
+ sage: (f-c)+c == f
+ True
+
+ We may add a number (interpreted as a constant scalar field) to a scalar
+ field::
+
+ sage: s = f + 1 ; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on U: (x, y) |--> (x^2 + y^2 + 2)/(x^2 + y^2 + 1)
+ on V: (u, v) |--> (2*u^2 + 2*v^2 + 1)/(u^2 + v^2 + 1)
+ sage: (f+1)-1 == f
+ True
+
+ The number can represented by a symbolic variable::
+
+ sage: s = a + f ; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s == c + f
+ True
+
+ However if the symbolic variable is a chart coordinate, the addition
+ is performed only on the chart domain::
+
+ sage: s = f + x; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on U: (x, y) |--> (x^3 + x*y^2 + x + 1)/(x^2 + y^2 + 1)
+ sage: s = f + u; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on V: (u, v) |--> (u^3 + (u + 1)*v^2 + u^2 + u)/(u^2 + v^2 + 1)
+
+ The addition of two scalar fields with different domains is possible if
+ the domain of one of them is a subset of the domain of the other; the
+ domain of the result is then this subset::
+
+ sage: f.domain()
+ 2-dimensional topological manifold M
+ sage: g.domain()
+ Open subset U of the 2-dimensional topological manifold M
+ sage: s = f + g ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.domain()
+ Open subset U of the 2-dimensional topological manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> (x*y^3 + (x^3 + x)*y + 1)/(x^2 + y^2 + 1)
+ on W: (u, v) |--> (u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6 + u*v^3
+ + (u^3 + u)*v)/(u^6 + v^6 + (3*u^2 + 1)*v^4 + u^4 + (3*u^4 + 2*u^2)*v^2)
+
+ The operation actually performed is `f|_U + g`::
+
+ sage: s == f.restrict(U) + g
+ True
+
+ In Sage framework, the addition of `f` and `g` is permitted because
+ there is a *coercion* of the parent of `f`, namely `C^0(M)`, to
+ the parent of `g`, namely `C^0(U)` (see
+ :class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`)::
+
+ sage: CM = M.scalar_field_algebra()
+ sage: CU = U.scalar_field_algebra()
+ sage: CU.has_coerce_map_from(CM)
+ True
+
+ The coercion map is nothing but the restriction to domain `U`::
+
+ sage: CU.coerce(f) == f.restrict(U)
+ True
+
+ Since the algebra `C^0(M)` is a vector space over `\RR`, scalar fields
+ can be multiplied by a number, either an explicit one::
+
+ sage: s = 2*f ; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on U: (x, y) |--> 2/(x^2 + y^2 + 1)
+ on V: (u, v) |--> 2*(u^2 + v^2)/(u^2 + v^2 + 1)
+
+ or a symbolic one::
+
+ sage: s = a*f ; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on U: (x, y) |--> a/(x^2 + y^2 + 1)
+ on V: (u, v) |--> (u^2 + v^2)*a/(u^2 + v^2 + 1)
+
+ However, if the symbolic variable is a chart coordinate, the
+ multiplication is performed only in the corresponding chart::
+
+ sage: s = x*f; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on U: (x, y) |--> x/(x^2 + y^2 + 1)
+ sage: s = u*f; s
+ Scalar field on the 2-dimensional topological manifold M
+ sage: s.display()
+ M --> R
+ on V: (u, v) |--> (u^2 + v^2)*u/(u^2 + v^2 + 1)
+
+ Some tests::
+
+ sage: 0*f == 0
+ True
+ sage: 0*f == zer
+ True
+ sage: 1*f == f
+ True
+ sage: (-2)*f == - f - f
+ True
+
+ The ring multiplication of the algebras `C^0(M)` and `C^0(U)`
+ is the pointwise multiplication of functions::
+
+ sage: s = f*f ; s
+ Scalar field f*f on the 2-dimensional topological manifold M
+ sage: s.display()
+ f*f: M --> R
+ on U: (x, y) |--> 1/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1)
+ on V: (u, v) |--> (u^4 + 2*u^2*v^2 + v^4)/(u^4 + v^4 + 2*(u^2 + 1)*v^2
+ + 2*u^2 + 1)
+ sage: s = g*h ; s
+ Scalar field g*h on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ g*h: U --> R
+ (x, y) |--> x*y*H(x, y)
+ on W: (u, v) |--> u*v*H(u/(u^2 + v^2), v/(u^2 + v^2))/(u^4 + 2*u^2*v^2 + v^4)
+
+ Thanks to the coercion `C^0(M) \to C^0(U)` mentioned above,
+ it is possible to multiply a scalar field defined on `M` by a
+ scalar field defined on `U`, the result being a scalar field
+ defined on `U`::
+
+ sage: f.domain(), g.domain()
+ (2-dimensional topological manifold M,
+ Open subset U of the 2-dimensional topological manifold M)
+ sage: s = f*g ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> x*y/(x^2 + y^2 + 1)
+ on W: (u, v) |--> u*v/(u^4 + v^4 + (2*u^2 + 1)*v^2 + u^2)
+ sage: s == f.restrict(U)*g
+ True
+
+ Scalar fields can be divided (pointwise division)::
+
+ sage: s = f/c ; s
+ Scalar field f/c on the 2-dimensional topological manifold M
+ sage: s.display()
+ f/c: M --> R
+ on U: (x, y) |--> 1/(a*x^2 + a*y^2 + a)
+ on V: (u, v) |--> (u^2 + v^2)/(a*u^2 + a*v^2 + a)
+ sage: s = g/h ; s
+ Scalar field g/h on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ g/h: U --> R
+ (x, y) |--> x*y/H(x, y)
+ on W: (u, v) |--> u*v/((u^4 + 2*u^2*v^2 + v^4)*H(u/(u^2 + v^2), v/(u^2 + v^2)))
+ sage: s = f/g ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> 1/(x*y^3 + (x^3 + x)*y)
+ on W: (u, v) |--> (u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6)/(u*v^3 + (u^3 + u)*v)
+ sage: s == f.restrict(U)/g
+ True
+
+ For scalar fields defined on a single chart domain, we may perform some
+ arithmetics with symbolic expressions involving the chart coordinates::
+
+ sage: s = g + x^2 - y ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> x^2 + (x - 1)*y
+ on W: (u, v) |--> -(v^3 - u^2 + (u^2 - u)*v)/(u^4 + 2*u^2*v^2 + v^4)
+
+ ::
+
+ sage: s = g*x ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> x^2*y
+ on W: (u, v) |--> u^2*v/(u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6)
+
+ ::
+
+ sage: s = g/x ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> y
+ on W: (u, v) |--> v/(u^2 + v^2)
+ sage: s = x/g ; s
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: s.display()
+ U --> R
+ (x, y) |--> 1/y
+ on W: (u, v) |--> (u^2 + v^2)/v
+
+ The test suite is passed::
+
+ sage: TestSuite(f).run()
+ sage: TestSuite(zer).run()
+
+ """
+ def __init__(self, parent, coord_expression=None, chart=None, name=None,
+ latex_name=None):
+ r"""
+ Construct a scalar field.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y}, name='f') ; f
+ Scalar field f on the 2-dimensional topological manifold M
+ sage: from sage.manifolds.scalarfield import ScalarField
+ sage: isinstance(f, ScalarField)
+ True
+ sage: f.parent()
+ Algebra of scalar fields on the 2-dimensional topological
+ manifold M
+ sage: TestSuite(f).run()
+
+ """
+ CommutativeAlgebraElement.__init__(self, parent)
+ domain = parent._domain
+ self._domain = domain
+ self._manifold = domain.manifold()
+ self._is_zero = False # a priori, may be changed below or via
+ # method __nonzero__()
+ self._name = name
+ if latex_name is None:
+ self._latex_name = self._name
+ else:
+ self._latex_name = latex_name
+ self._express = {} # dict of coordinate expressions (CoordFunction
+ # instances) with charts as keys
+ if coord_expression is not None:
+ if isinstance(coord_expression, dict):
+ for chart, expression in coord_expression.iteritems():
+ if isinstance(expression, CoordFunction):
+ self._express[chart] = expression
+ else:
+ self._express[chart] = chart.function(expression)
+ elif isinstance(coord_expression, CoordFunction):
+ self._express[coord_expression.chart()] = coord_expression
+ else:
+ if chart is None:
+ chart = self._domain.default_chart()
+ if chart == 'all':
+ # coord_expression is the same in all charts (constant
+ # scalar field)
+ for ch in self._domain.atlas():
+ self._express[ch] = ch.function(coord_expression)
+ else:
+ self._express[chart] = chart.function(coord_expression)
+ self._init_derived() # initialization of derived quantities
+
+ ####### Required methods for an algebra element (beside arithmetic) #######
+
+ def __nonzero__(self):
+ r"""
+ Return ``True`` if ``self`` is nonzero and ``False`` otherwise.
+
+ This method is called by :meth:`is_zero()`.
+
+ EXAMPLES:
+
+ Tests on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart()
+ sage: f = M.scalar_field(x*y)
+ sage: f.is_zero()
+ False
+ sage: f.set_expr(0)
+ sage: f.is_zero()
+ True
+ sage: g = M.scalar_field(0)
+ sage: g.is_zero()
+ True
+ sage: M.zero_scalar_field().is_zero()
+ True
+
+ """
+ if self._is_zero:
+ return False
+ if not self._express:
+ # undefined scalar field
+ return True
+ iszero = True
+ for funct in self._express.itervalues():
+ iszero = iszero and funct.is_zero()
+ self._is_zero = iszero
+ return not iszero
+
+ def __eq__(self, other):
+ r"""
+ Comparison (equality) operator.
+
+ INPUT:
+
+ - ``other`` -- a scalar field (or something else)
+
+ 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 = M.scalar_field({X: x+y})
+ sage: f == 1
+ False
+ sage: f == M.zero_scalar_field()
+ False
+ sage: g = M.scalar_field({X: x+y})
+ sage: f == g
+ True
+ sage: h = M.scalar_field({X: 1})
+ sage: h == M.one_scalar_field()
+ True
+ sage: h == 1
+ True
+
+ """
+ if other is self:
+ return True
+ if not isinstance(other, ScalarField):
+ # We try a conversion of other to a scalar field, except if
+ # other is None (since this would generate an undefined scalar
+ # field)
+ if other is None:
+ return False
+ try:
+ other = self.parent()(other) # conversion to a scalar field
+ except TypeError:
+ return False
+ if other._domain != self._domain:
+ return False
+ if other.is_zero():
+ return self.is_zero()
+ com_charts = self.common_charts(other)
+ if com_charts is None:
+ raise ValueError("no common chart for the comparison")
+ resu = True
+ for chart in com_charts:
+ resu = resu and (self._express[chart] == other._express[chart])
+ return resu
+
+ def __ne__(self, other):
+ r"""
+ Non-equality operator.
+
+ INPUT:
+
+ - ``other`` -- a scalar field
+
+ OUTPUT:
+
+ - ``True`` if ``self`` differs from ``other``, ``False`` otherwise
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y})
+ sage: f != 1
+ True
+ sage: f != M.zero_scalar_field()
+ True
+ sage: g = M.scalar_field({X: x+y})
+ sage: f != g
+ False
+
+ """
+ return not (self == other)
+
+ ####### End of required methods for an algebra element (beside arithmetic) #######
+
+ def _init_derived(self):
+ r"""
+ Initialize the derived quantities.
+
+ TEST::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y})
+ sage: f._init_derived()
+
+ """
+ self._restrictions = {} # dict. of restrictions of self on subsets
+ # of self._domain, with the subsets as keys
+
+ def _del_derived(self):
+ r"""
+ Delete the derived quantities.
+
+ TEST::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y})
+ sage: U = M.open_subset('U', coord_def={X: x>0})
+ sage: f.restrict(U)
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M
+ sage: f._restrictions
+ {Open subset U of the 2-dimensional topological manifold M:
+ Scalar field on the Open subset U of the 2-dimensional topological
+ manifold M}
+ sage: f._del_derived()
+ sage: f._restrictions # restrictions are derived quantities
+ {}
+
+ """
+ self._restrictions.clear()
+
+ def _repr_(self):
+ r"""
+ String representation of the object.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y})
+ sage: f._repr_()
+ 'Scalar field on the 2-dimensional topological manifold M'
+ sage: f = M.scalar_field({X: x+y}, name='f')
+ sage: f._repr_()
+ 'Scalar field f on the 2-dimensional topological manifold M'
+ sage: f
+ Scalar field f on the 2-dimensional topological manifold M
+
+ """
+ description = "Scalar field"
+ if self._name is not None:
+ description += " " + self._name
+ description += " on the {}".format(self._domain)
+ return description
+
+ 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 = M.scalar_field({X: x+y})
+ sage: f._latex_()
+ '\\mbox{Scalar field on the 2-dimensional topological manifold M}'
+ sage: f = M.scalar_field({X: x+y}, name='f')
+ sage: f._latex_()
+ 'f'
+ sage: f = M.scalar_field({X: x+y}, name='f', latex_name=r'\Phi')
+ sage: f._latex_()
+ '\\Phi'
+ sage: latex(f)
+ \Phi
+
+ """
+ if self._latex_name is None:
+ return r'\mbox{' + str(self) + r'}'
+ else:
+ return self._latex_name
+
+ def set_name(self, name=None, latex_name=None):
+ r"""
+ Set (or change) the text name and LaTeX name of the scalar field.
+
+ INPUT:
+
+ - ``name`` -- (string; default: ``None``) name given to the scalar
+ field
+ - ``latex_name`` -- (string; default: ``None``) LaTeX symbol to denote
+ the scalar field; if ``None`` while ``name`` is provided, the LaTeX
+ symbol is set to ``name``
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y})
+ sage: f = M.scalar_field({X: x+y}); f
+ Scalar field on the 2-dimensional topological manifold M
+ sage: f.set_name('f'); f
+ Scalar field f on the 2-dimensional topological manifold M
+ sage: latex(f)
+ f
+ sage: f.set_name('f', latex_name=r'\Phi'); f
+ Scalar field f on the 2-dimensional topological manifold M
+ sage: latex(f)
+ \Phi
+
+ """
+ if name is not None:
+ self._name = name
+ if latex_name is None:
+ self._latex_name = self._name
+ if latex_name is not None:
+ self._latex_name = latex_name
+
+ def domain(self):
+ r"""
+ Return the open subset on which the scalar field is defined.
+
+ OUTPUT:
+
+ - instance of class
+ :class:`~sage.manifolds.manifold.TopologicalManifold`
+ representing the manifold's open subset on which the
+ scalar field is defined
+
+ EXAMPLES::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart()
+ sage: f = M.scalar_field(x+2*y)
+ sage: f.domain()
+ 2-dimensional topological manifold M
+ sage: U = M.open_subset('U', coord_def={c_xy: x<0})
+ sage: g = f.restrict(U)
+ sage: g.domain()
+ Open subset U of the 2-dimensional topological manifold M
+
+ """
+ return self._domain
+
+ def copy(self):
+ r"""
+ Return an exact copy of the scalar field.
+
+ EXAMPLES:
+
+ Copy on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart()
+ sage: f = M.scalar_field(x*y^2)
+ sage: g = f.copy()
+ sage: type(g)
+ <class 'sage.manifolds.scalarfield.ScalarFieldAlgebra_with_category.element_class'>
+ sage: g.expr()
+ x*y^2
+ sage: g == f
+ True
+ sage: g is f
+ False
+
+ """
+ result = type(self)(self.parent(), name=self._name,
+ latex_name=self._latex_name)
+ for chart, funct in self._express.iteritems():
+ result._express[chart] = funct.copy()
+ return result
+
+ def coord_function(self, chart=None, from_chart=None):
+ r"""
+ Return the function of the coordinates representing the scalar field
+ in a given chart.
+
+ INPUT:
+
+ - ``chart`` -- (default: ``None``) chart with respect to which the
+ coordinate expression is to be returned; if ``None``, the
+ default chart of the scalar field's domain will be used
+ - ``from_chart`` -- (default: ``None``) chart from which the
+ required expression is computed if it is not known already in the
+ chart ``chart``; if ``None``, a chart is picked in the known
+ expressions
+
+ OUTPUT:
+
+ - instance of :class:`~sage.manifolds.coord_func.CoordFunction`
+ representing the coordinate function of the scalar field in the
+ given chart
+
+ EXAMPLES:
+
+ Coordinate function on a 2-dimensional manifold::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: c_xy.<x,y> = M.chart()
+ sage: f = M.scalar_field(x*y^2)
+