summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTravis Scrimshaw <tscrimsh at umn.edu>2016-04-26 23:57:30 -0500
committerTravis Scrimshaw <tscrimsh at umn.edu>2016-04-26 23:57:30 -0500
commitc7676969c52b01a7337d7fcc87ed1affb86d9765 (patch)
tree985af665f6915f574445ca04ab76962a38a44c61
parentUpdated SageMath version to 7.2.beta5 (diff)
parentMerge into the latest version of #18529; improve treatment of composite funct... (diff)
Merge branch 'public/manifolds/top_manif_scalar_fields' of trac.sagemath.org:sage into public/manifolds/top_manif_scalar_fields
-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/manifolds/all.py3
-rw-r--r--src/sage/manifolds/chart.py304
-rw-r--r--src/sage/manifolds/coord_func.py1762
-rw-r--r--src/sage/manifolds/coord_func_symb.py1664
-rw-r--r--src/sage/manifolds/manifold.py282
-rw-r--r--src/sage/manifolds/scalarfield.py2790
-rw-r--r--src/sage/manifolds/scalarfield_algebra.py625
-rw-r--r--src/sage/manifolds/structure.py3
-rw-r--r--src/sage/manifolds/utilities.py926
13 files changed, 8355 insertions, 32 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/manifolds/all.py b/src/sage/manifolds/all.py
index 990657e..01834c1 100644
--- a/src/sage/manifolds/all.py
+++ b/src/sage/manifolds/all.py
@@ -1,3 +1,4 @@
from sage.misc.lazy_import import lazy_import
lazy_import('sage.manifolds.manifold', 'Manifold')
-
+lazy_import('sage.manifolds.utilities', 'nice_derivatives')
+lazy_import('sage.manifolds.utilities', 'omit_function_args')
diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py
index 6a6194b..8be827d 100644
--- a/src/sage/manifolds/chart.py
+++ b/src/sage/manifolds/chart.py
@@ -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,24 @@ 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()
+ if base_field_type in ['real', 'complex']:
+ self._zero_function = CoordFunctionSymb(self, 0)
+ self._one_function = CoordFunctionSymb(self, 1)
+ else:
+ base_field = self._domain.base_field()
+ self._zero_function = CoordFunctionSymb(self, base_field.zero())
+ self._one_function = CoordFunctionSymb(self, base_field.one())
+ # 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._zero_function
+ if hasattr(dom, '_one_scalar_field'):
+ # dom is an open set
+ dom._one_scalar_field._express[self] = self._one_function
def _init_coordinates(self, coord_list):
r"""
@@ -825,6 +844,234 @@ class Chart(UniqueRepresentation, SageObject):
transformations = [transformations]
return CoordChange(chart1, chart2, *transformations)
+ 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.CoordFunctionSymb'>
+ 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 CoordFunctionSymb(self, 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.CoordFunctionSymb'>
+
+ 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._zero_function
+
+ 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.CoordFunctionSymb'>
+
+ 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._one_function
+
+
+ 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:
+
+ - an instance of :class:`~sage.manifolds.coord_func.MultiCoordFunction`
+ representing `f`
+
+ EXAMPLE:
+
+ 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: type(f)
+ <class 'sage.manifolds.coord_func.MultiCoordFunction'>
+ sage: f(2,3)
+ (5, sin(6), 13)
+
+ """
+ from sage.manifolds.coord_func import MultiCoordFunction
+ return MultiCoordFunction(self, expressions)
+
+
#*****************************************************************************
class RealChart(Chart):
@@ -1412,6 +1659,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 +1847,6 @@ class RealChart(Chart):
# All tests have been passed:
return True
-
#*****************************************************************************
class CoordChange(SageObject):
@@ -1669,10 +1916,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 +2037,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 +2073,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 +2100,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 +2111,14 @@ class CoordChange(SageObject):
if len(solutions) == 1:
x2_to_x1 = [solutions[0][x1[i]].subs(substitutions)
for i in range(n1)]
+ for transf in x2_to_x1:
+ try:
+ if self._domain.base_field_type() == 'real':
+ transf = simplify_chain_real(transf)
+ else:
+ transf = simplify_chain_generic(transf)
+ except AttributeError:
+ pass
else:
list_x2_to_x1 = []
for sol in solutions:
@@ -1879,6 +2127,14 @@ class CoordChange(SageObject):
"set_inverse() to set the inverse " +
"manually")
x2_to_x1 = [sol[x1[i]].subs(substitutions) for i in range(n1)]
+ for transf in x2_to_x1:
+ try:
+ if self._domain.base_field_type() == 'real':
+ transf = simplify_chain_real(transf)
+ else:
+ transf = simplify_chain_generic(transf)
+ except AttributeError:
+ pass
if self._chart1.valid_coordinates(*x2_to_x1):
list_x2_to_x1.append(x2_to_x1)
if len(list_x2_to_x1) == 0:
@@ -1938,7 +2194,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 +2248,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 +2291,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 +2328,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..9ae3324
--- /dev/null
+++ b/src/sage/manifolds/coord_func.py
@@ -0,0 +1,1762 @@
+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 \rightarrow 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
+
+"""
+#*****************************************************************************
+# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
+# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
+#
+# Distributed under the terms of the GNU General Public License (GPL)
+# as published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+# http://www.gnu.org/licenses/
+#*****************************************************************************
+
+from sage.structure.sage_object import SageObject
+from sage.misc.latex import latex
+
+class CoordFunction(SageObject):
+ 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 \rightarrow 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:
+
+ - ``chart`` -- the chart `(U, \varphi)`, as an instance of class
+ :class:`~sage.manifolds.chart.Chart`
+
+ """
+ def __init__(self, chart):
+ r"""
+ Base constructor for derived classes.
+
+ 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)
+
+ """
+ self._chart = chart
+ self._nc = len(chart[:]) # number of coordinates
+
+ # ----------------------------------------------------------------
+ # Methods that do not need to be re-implemented by derived classes
+ # ----------------------------------------------------------------
+
+ def chart(self):
+ r"""
+ Return the chart w.r.t. which the coordinate function is defined.
+
+ OUTPUT:
+
+ - an instance of :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._chart
+
+ def scalar_field(self, name=None, latex_name=None):
+ r"""
+ Construct the scalar field that has the coordinate function as
+ coordinate expression.
+
+ The domain of the scalar field is the open subset covered by the chart
+ on which the coordinate function 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 is provided, the LaTeX symbol is set to ``name``
+
+ OUTPUT:
+
+ - instance of class
+ :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._chart.domain().scalar_field_algebra()
+ return alg.element_class(alg, coord_expression={self._chart: self},
+ name=name, latex_name=latex_name)
+
+ def __ne__(self, other):
+ r"""
+ Inequality operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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)
+
+ def __radd__(self, other):
+ r"""
+ Reflected addition operator: performs ``other + self``.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the addition of ``self`` to
+ ``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: f.__radd__(g)
+ (x + 1)*y + x
+ sage: f.__radd__(X.zero_function()) == f
+ True
+ sage: 2 + f # indirect doctest
+ x + y + 2
+
+ """
+ return self.__add__(other) # since + is commutative
+
+ def __iadd__(self, other):
+ r"""
+ In-place addition operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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)
+ sage: g = X.function(x*y)
+ sage: f.__iadd__(g)
+ (x + 1)*y + x
+ sage: f += g; f
+ (x + 1)*y + x
+ sage: f.__iadd__(X.zero_function()) == f
+ True
+
+ """
+ return self.__add__(other)
+
+ def __rsub__(self, other):
+ r"""
+ Reflected subtraction operator: performs ``other - self``.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the subtraction of ``self`` from
+ ``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: f.__rsub__(g)
+ (x - 1)*y - x
+ sage: f.__rsub__(g) == -f + g
+ True
+ sage: 2 - f # indirect doctest
+ -x - y + 2
+
+ """
+ return self.__neg__().__add__(other)
+
+ def __isub__(self, other):
+ r"""
+ In-place subtraction operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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)
+ sage: g = X.function(x*y)
+ sage: f.__isub__(g)
+ -(x - 1)*y + x
+ sage: f -= g; f
+ -(x - 1)*y + x
+ sage: f.__isub__(X.zero_function()) == f
+ True
+
+ """
+ return self.__sub__(other)
+
+ def __rmul__(self, other):
+ r"""
+ Reflected multiplication operator: performs ``other * self``.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the multiplication of ``other``
+ by ``self``
+
+ 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.__rmul__(g)
+ x^2*y + x*y^2
+ sage: f.__rmul__(g) == g*f
+ True
+ sage: f.__rmul__(X.one_function()) == f
+ True
+ sage: 2*f # indirect doctest
+ 2*x + 2*y
+
+ """
+ return self.__mul__(other) # since * is commutative
+
+ def __imul__(self, other):
+ r"""
+ In-place multiplication operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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: f.__imul__(g)
+ x^2*y + x*y^2
+ sage: f *= g; f
+ x^2*y + x*y^2
+ sage: f.__imul__(X.one_function()) == f
+ True
+
+ """
+ return self.__mul__(other)
+
+ def __rdiv__(self, other):
+ r"""
+ Reflected division operator: performs ``other / self``.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the division of ``other`` by
+ ``self``
+
+ 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.__rdiv__(g)
+ x*y/(x + y)
+ sage: f.__rdiv__(g) == g/f
+ True
+ sage: 2/f # indirect doctest
+ 2/(x + y)
+
+ """
+ return self.__invert__().__mul__(other)
+
+ def __idiv__(self, other):
+ r"""
+ In-place division operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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(x*y)
+ sage: f.__idiv__(g)
+ (x + y)/(x*y)
+ sage: f /= g; f
+ (x + y)/(x*y)
+ sage: f.__idiv__(X.one_function()) == f
+ True
+
+ """
+ return self.__div__(other)
+
+ # --------------------------------------------
+ # Methods to be implemented by derived classes
+ # --------------------------------------------
+
+ def _repr_(self):
+ r"""
+ String representation of the object.
+
+ TEST:
+
+ 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)
+ sage: f._repr_()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction._repr_ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction._repr_ not implemented")
+
+ def _latex_(self):
+ r"""
+ LaTeX representation of the object.
+
+ TEST:
+
+ 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)
+ sage: f._latex_()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction._latex_ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction._latex_ not implemented")
+
+ def display(self):
+ r"""
+ Display the function in arrow notation.
+
+ TEST:
+
+ 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)
+ sage: f.display()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.display not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.display not implemented")
+
+ disp = display
+
+ 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`)
+
+ TEST:
+
+ 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)
+ sage: f.expr()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.expr not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.expr not implemented")
+
+ def __call__(self, *coords, **options):
+ r"""
+ Compute the value of the function at specified coordinates.
+
+ INPUT:
+
+ - ``*coords`` -- list of coordinates `(x^1,...,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,...,x^n)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.__call__(2,-3)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__call__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__call__ not implemented")
+
+ def is_zero(self):
+ r"""
+ Return ``True`` if the function is zero and ``False`` otherwise.
+
+ TEST:
+
+ 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)
+ sage: f.is_zero()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.is_zero not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.is_zero not implemented")
+
+ def copy(self):
+ r"""
+ Return an exact copy of the object.
+
+ OUTPUT:
+
+ - an instance of :class:`CoordFunction`
+
+ TEST:
+
+ 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)
+ sage: f.copy()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.copy not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.copy not implemented")
+
+ 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:
+
+ - instance of :class:`CoordFunction` representing the partial
+ derivative `\frac{\partial f}{\partial x^i}`
+
+ TEST:
+
+ 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)
+ sage: f.diff(x)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.diff not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.diff not implemented")
+
+ def __eq__(self, other):
+ r"""
+ Comparison (equality) operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - ``True`` if ``self`` is equal to ``other``, or ``False`` otherwise
+
+ TEST:
+
+ 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)
+ sage: g = CoordFunction(X)
+ sage: f == g
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__eq__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__eq__ not implemented")
+
+ def __pos__(self):
+ r"""
+ Unary plus operator.
+
+ OUTPUT:
+
+ - an exact copy of ``self``
+
+ TEST:
+
+ 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)
+ sage: f.__pos__()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__pos__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__pos__ not implemented")
+
+ def __neg__(self):
+ r"""
+ Unary minus operator.
+
+ OUTPUT:
+
+ - the opposite of the coordinate function ``self``
+
+ TEST:
+
+ 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)
+ sage: f.__neg__()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__neg__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__neg__ not implemented")
+
+ 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 the coordinate function ``self``
+
+ TEST:
+
+ 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)
+ sage: f.__invert__()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__invert__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__invert__ not implemented")
+
+ def __add__(self, other):
+ r"""
+ Addition operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the addition of ``self`` and
+ ``other``
+
+ TEST:
+
+ 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)
+ sage: f.__add__(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__add__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__add__ not implemented")
+
+ def __sub__(self, other):
+ r"""
+ Subtraction operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the subtraction of ``other`` from
+ ``self``
+
+ TEST:
+
+ 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)
+ sage: f.__sub__(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__sub__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__sub__ not implemented")
+
+ def __mul__(self, other):
+ r"""
+ Multiplication operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the multiplication of ``self`` by
+ ``other``
+
+ TEST:
+
+ 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)
+ sage: f.__mul__(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__mul__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__mul__ not implemented")
+
+ def __div__(self, other):
+ r"""
+ Division operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :class:`CoordFunction` or a value
+
+ OUTPUT:
+
+ - coordinate function resulting from the division of ``self`` by
+ ``other``
+
+ TEST:
+
+ 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)
+ sage: f.__div__(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__div__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__div__ not implemented")
+
+ def exp(self):
+ r"""
+ Exponential of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\exp(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.exp()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.exp not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.exp not implemented")
+
+
+ def log(self, base=None):
+ r"""
+ Logarithm of the coordinate function.
+
+ 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
+
+ TEST:
+
+ 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)
+ sage: f.log()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.log not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.log not implemented")
+
+
+ def __pow__(self, exponent):
+ r"""
+ Power of the coordinate function.
+
+ INPUT:
+
+ - ``exponent`` -- the exponent
+
+ OUTPUT:
+
+ - coordinate function `f^a`, where `f` is the current coordinate
+ function and `a` is the exponent
+
+ TEST:
+
+ 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)
+ sage: f.__pow__(2)
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.__pow__ not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.__pow__ not implemented")
+
+
+ def sqrt(self):
+ r"""
+ Square root of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\sqrt{f}`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.sqrt()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.sqrt not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.sqrt not implemented")
+
+ def cos(self):
+ r"""
+ Cosine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\cos(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.cos()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.cos not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.cos not implemented")
+
+ def sin(self):
+ r"""
+ Sine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\sin(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.sin()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.sin not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.sin not implemented")
+
+ def tan(self):
+ r"""
+ Tangent of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\tan(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.tan()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.tan not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.tan not implemented")
+
+ def arccos(self):
+ r"""
+ Arc cosine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\arccos(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.arccos()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.arccos not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.arccos not implemented")
+
+ def arcsin(self):
+ r"""
+ Arc sine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\arcsin(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.arcsin()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.arcsin not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.arcsin not implemented")
+
+ def arctan(self):
+ r"""
+ Arc tangent of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\arctan(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.arctan()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.arctan not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.arctan not implemented")
+
+ def cosh(self):
+ r"""
+ Hyperbolic cosine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\cosh(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.cosh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.cosh not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.cosh not implemented")
+
+ def sinh(self):
+ r"""
+ Hyperbolic sine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\sinh(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.sinh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.sinh not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.sinh not implemented")
+
+ def tanh(self):
+ r"""
+ Hyperbolic tangent of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\tanh(f)`, where `f` is the current coordinate
+ function.
+
+ TEST:
+
+ 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)
+ sage: f.tanh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.tanh not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.tanh not implemented")
+
+ def arccosh(self):
+ r"""
+ Inverse hyperbolic cosine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{arcosh}(f)`, where `f` is the current
+ coordinate function.
+
+ TEST:
+
+ 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)
+ sage: f.arccosh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.arccosh not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.arccosh not implemented")
+
+ def arcsinh(self):
+ r"""
+ Inverse hyperbolic sine of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{arsinh}(f)`, where `f` is the current
+ coordinate function.
+
+ TEST:
+
+ 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)
+ sage: f.arcsinh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.arcsinh not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.arcsinh not implemented")
+
+ def arctanh(self):
+ r"""
+ Inverse hyperbolic tangent of the coordinate function.
+
+ OUTPUT:
+
+ - coordinate function `\mathrm{artanh}(f)`, where `f` is the current
+ coordinate function.
+
+ TEST:
+
+ 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)
+ sage: f.arctanh()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: CoordFunction.arctanh not implemented
+
+ """
+ raise NotImplementedError("CoordFunction.arctanh not implemented")
+
+
+#*****************************************************************************
+
+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 an instance of
+ :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
+
+ 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]`` belongs to the subclass
+ :class:`~sage.manifolds.coord_func_symb.CoordFunctionSymb`::
+
+ sage: type(f[0])
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymb'>
+ sage: f[0].display()
+ (x, y) |--> x - y
+
+ An instance of 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"""
+ Construct a multi-coordinate function.
+
+ 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)
+ # Derived quantities:
+ self._jacob = None
+ self._jacob_det = None
+
+ 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 = 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: repr(f) # indirect doctest
+ '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) # indirect doctest
+ \left(x - y, x y, \cos\left(x\right) e^{y}\right)
+
+ """
+ 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.
+
+ EXAMPLE::
+
+ 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 w.r.t. which the multi-coordinate function is defined.
+
+ OUTPUT:
+
+ - an instance of :class:`~sage.manifolds.chart.Chart`
+
+ EXAMPLE::
+
+ 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`` -- another instance of :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`` -- another instance of :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
+
+ -- instance of :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, like
+ ``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)
+
+ 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).
+
+ Each `J_{ij}` is an instance of :class:`CoordFunction`.
+
+ OUTPUT:
+
+ - Jacobian matrix as a 2-dimensional array ``J`` of coordinate functions,
+ ``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 coordinate function::
+
+ sage: type(f.jacobian()[2][0])
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymb'>
+ 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]]
+
+ """
+ if self._jacob is None:
+ xx = self._chart[:] # coordinates x^j
+ self._jacob = [[ self._functions[i].diff(xx[j])
+ for j in range(self._nc) ] for i in range(self._nf) ]
+ return self._jacob
+
+ 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:
+
+ - instance of :class:`CoordFunction` representing the determinant
+
+ EXAMPLE:
+
+ 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.CoordFunctionSymb'>
+ 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
+
+ Test::
+
+ 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
+
+ Test::
+
+ 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._jacob_det is None:
+ if (self._nf != self._nc):
+ raise ValueError("the Jacobian matrix is not a square matrix")
+ self.jacobian() # forces the computation of self._jacob
+ self._jacob_det = simple_determinant(self._jacob)
+ return self._jacob_det
diff --git a/src/sage/manifolds/coord_func_symb.py b/src/sage/manifolds/coord_func_symb.py
new file mode 100644
index 0000000..4c45741
--- /dev/null
+++ b/src/sage/manifolds/coord_func_symb.py
@@ -0,0 +1,1664 @@
+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 \rightarrow 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
+
+"""
+#*****************************************************************************
+# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
+# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
+#
+# Distributed under the terms of the GNU General Public License (GPL)
+# as published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+# http://www.gnu.org/licenses/
+#*****************************************************************************
+
+from sage.symbolic.ring import SR
+from sage.structure.element import RingElement
+from sage.misc.latex import latex
+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:
+
+ - ``chart`` -- the chart `(U, \varphi)`, as an instance of class
+ :class:`~sage.manifolds.chart.Chart`
+ - ``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.CoordFunctionSymb'>
+ 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 via the command
+ :func:`~sage.manifolds.utilities.nice_derivatives`::
+
+ sage: nice_derivatives(False)
+ sage: latex(f)
+ D[0]\left(g\right)\left(x, y\right) + D[1]\left(g\right)\left(x, y\right)
+ sage: nice_derivatives(True)
+ 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: omit_function_args(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: omit_function_args(False)
+ sage: f
+ u(x, y)*v(x, y)
+
+ """
+
+ _nice_output = True # static flag for textbook-like output instead of the
+ # Pynac output for derivatives
+
+ _omit_fargs = False # static flag to govern whether or not
+ # the arguments of symbolic functions are printed
+
+ def __init__(self, chart, expression):
+ r"""
+ Construct a coordinate function.
+
+ 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.CoordFunctionSymb'>
+ 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()
+
+ """
+ self._chart = chart
+ self._nc = len(chart[:]) # number of coordinates
+ self._express = SR(expression) # symbolic expression enforced
+ # Definition of the simplification chain to be applied in
+ # symbolic calculus:
+ if self._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 the object.
+
+ 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 CoordFunctionSymb._nice_output:
+ return str(ExpressionNice(self._express))
+ else:
+ return str(self._express)
+
+ 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.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)
+
+ """
+ if CoordFunctionSymb._nice_output:
+ return latex(ExpressionNice(self._express))
+ else:
+ return latex(self._express)
+
+ def display(self):
+ r"""
+ Display the function 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
+ resu_txt = str((self._chart)[:]) + ' |--> ' + \
+ str(ExpressionNice(self._express))
+ resu_latex = latex(self._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 the coordinate
+ function.
+
+ OUTPUT:
+
+ - symbolic expression, involving the chart coordinates (instance of
+ :class:`sage.symbolic.expression.Expression`)
+
+ 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,...,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,...,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._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:
+
+ - an instance of :class:`CoordFunctionSymb`
+
+ EXAMPLE::
+
+ 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 CoordFunctionSymb(self._chart, 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:
+
+ - instance of :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.CoordFunctionSymb'>
+
+ 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 = [CoordFunctionSymb(self._chart,
+ self._simplify(diff(self._express,
+ xx)))
+ for xx in self._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._chart.domain().start_index()
+ coordsi = coord - self._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._chart[:].index(coord)]
+
+ def __eq__(self, other):
+ r"""
+ Comparison (equality) operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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._chart != self._chart:
+ return False
+ else:
+ return bool(other._express == self._express)
+ else:
+ return bool(self._express == other)
+
+ def __pos__(self):
+ r"""
+ Unary plus operator.
+
+ This method is identical to :meth:`copy`.
+
+ OUTPUT:
+
+ - an exact copy 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.CoordFunctionSymb'>
+ sage: g == f
+ True
+
+ """
+ return CoordFunctionSymb(self._chart, self._express)
+
+ def __neg__(self):
+ r"""
+ Unary minus operator.
+
+ OUTPUT:
+
+ - the opposite of the coordinate function ``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.CoordFunctionSymb'>
+ sage: -g == f
+ True
+
+ """
+ return CoordFunctionSymb(self._chart, 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 the coordinate function ``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.CoordFunctionSymb'>
+ sage: g == ~f
+ True
+ sage: g.__invert__() == f
+ True
+
+ """
+ return CoordFunctionSymb(self._chart,
+ self._simplify(SR(1) / 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`` -- another instance of :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.__add__(g); s.display()
+ (x, y) |--> y^2 + 2*x + 1
+ sage: type(s)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymb'>
+ sage: f.__add__(0).display()
+ (x, y) |--> y^2 + x
+ sage: f.__add__(X.zero_function()).display()
+ (x, y) |--> y^2 + x
+ sage: f.__add__(1).display()
+ (x, y) |--> y^2 + x + 1
+ sage: f.__add__(pi).display()
+ (x, y) |--> pi + y^2 + x
+ sage: f.__add__(x).display()
+ (x, y) |--> y^2 + 2*x
+ sage: f.__add__(-f).display()
+ (x, y) |--> 0
+ sage: f.__radd__(g) == g.__add__(f)
+ True
+
+ """
+ if isinstance(other, CoordFunctionSymb):
+ if other._chart != self._chart:
+ raise ValueError("two coordinate functions not defined on " +
+ "the same chart cannot be added")
+ res = self._simplify(self._express + other._express)
+ elif isinstance(other, (int, RingElement)):
+ res = self._simplify(self._express + SR(other))
+ else:
+ # addition to a numerical coord. function shall fall into this case
+ return other.__radd__(self)
+ if res == 0:
+ return self._chart._zero_function
+ else:
+ return CoordFunctionSymb(self._chart, res)
+
+ def __sub__(self, other):
+ r"""
+ Subtraction operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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.__sub__(g); s.display()
+ (x, y) |--> y^2 - 1
+ sage: type(s)
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymb'>
+ sage: f.__sub__(0).display()
+ (x, y) |--> y^2 + x
+ sage: f.__sub__(X.zero_function()).display()
+ (x, y) |--> y^2 + x
+ sage: f.__sub__(1).display()
+ (x, y) |--> y^2 + x - 1
+ sage: f.__sub__(x).display()
+ (x, y) |--> y^2
+ sage: f.__sub__(pi).display()
+ (x, y) |--> -pi + y^2 + x
+ sage: f.__sub__(f).display()
+ (x, y) |--> 0
+ sage: f.__sub__(g) == - (g.__sub__(f))
+ True
+ sage: f.__rsub__(g) == g.__sub__(f)
+ True
+
+ """
+ if isinstance(other, CoordFunctionSymb):
+ if other._chart != self._chart:
+ raise ValueError("two coordinate functions not defined on " +
+ "the same chart cannot be subtracted")
+ res = self._simplify(self._express - other._express)
+ elif isinstance(other, (int, RingElement)):
+ res = self._simplify(self._express - SR(other))
+ else:
+ # subtraction of a numerical coord. function shall fall into this
+ # case
+ return other.__rsub__(self)
+ if res == 0:
+ return self._chart._zero_function
+ else:
+ return CoordFunctionSymb(self._chart, res)
+
+ def __mul__(self, other):
+ r"""
+ Multiplication operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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.CoordFunctionSymb'>
+ sage: f.__mul__(0).display()
+ (x, y) |--> 0
+ sage: f.__mul__(X.zero_function()).display()
+ (x, y) |--> 0
+ sage: f.__mul__(2).display()
+ (x, y) |--> 2*x + 2*y
+ sage: f.__mul__(pi).display()
+ (x, y) |--> pi*x + pi*y
+ sage: f.__mul__(x).display()
+ (x, y) |--> x^2 + x*y
+ sage: f.__mul__(1/f).display()
+ (x, y) |--> 1
+ sage: f.__rmul__(g) == g.__mul__(f)
+ True
+
+ """
+ if isinstance(other, CoordFunctionSymb):
+ if other._chart != self._chart:
+ raise ValueError("two coordinate functions not defined on " +
+ "the same chart cannot be multiplied")
+ res = self._simplify(self._express * other._express)
+ elif isinstance(other, (int, RingElement)):
+ res = self._simplify(self._express * SR(other))
+ else:
+ # multiplication by a numerical coord. function shall fall into
+ # this case
+ return other.__rmul__(self)
+ if res == 0:
+ return self._chart._zero_function
+ else:
+ return CoordFunctionSymb(self._chart, res)
+
+ def __div__(self, other):
+ r"""
+ Division operator.
+
+ INPUT:
+
+ - ``other`` -- another instance of :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.CoordFunctionSymb'>
+ sage: f.__div__(X.zero_function())
+ Traceback (most recent call last):
+ ...
+ ZeroDivisionError: Division of a coordinate function by zero
+ sage: f.__div__(1).display()
+ (x, y) |--> x + y
+ sage: f.__div__(2).display()
+ (x, y) |--> 1/2*x + 1/2*y
+ sage: f.__div__(pi).display()
+ (x, y) |--> (x + y)/pi
+ sage: f.__div__(1+x^2).display()
+ (x, y) |--> (x + y)/(x^2 + 1)
+ sage: f.__div__(1+x^2).display()
+ (x, y) |--> (x + y)/(x^2 + 1)
+ sage: f.__div__(g) == ~(g.__div__(f))
+ True
+ sage: f.__rdiv__(g) == g.__div__(f)
+ True
+
+ """
+ if isinstance(other, CoordFunctionSymb):
+ if other._chart != self._chart:
+ raise ValueError("two coordinate functions not defined on " +
+ "the same chart cannot be divided")
+ if other._express.is_zero():
+ raise ZeroDivisionError("Division of a coordinate function " +
+ "by zero")
+ res = self._simplify(self._express / other._express)
+ elif isinstance(other, (int, RingElement)):
+ res = self._simplify(self._express / SR(other))
+ else:
+ # division by a numerical coord. function shall fall into this
+ # case
+ return other.__rdiv__(self)
+ if res == 0:
+ return self._chart._zero_function
+ else:
+ return CoordFunctionSymb(self._chart, res)
+
+ def exp(self):
+ r"""
+ Exponential of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.exp()))
+
+ def log(self, base=None):
+ r"""
+ Logarithm of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.log(base)))
+
+ def __pow__(self, exponent):
+ r"""
+ Power of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(pow(self._express, exponent)))
+
+ def sqrt(self):
+ r"""
+ Square root of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.sqrt()))
+
+ def cos(self):
+ r"""
+ Cosine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.cos()))
+
+ def sin(self):
+ r"""
+ Sine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.sin()))
+
+ def tan(self):
+ r"""
+ Tangent of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.tan()))
+
+ def arccos(self):
+ r"""
+ Arc cosine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.arccos()))
+
+ def arcsin(self):
+ r"""
+ Arc sine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.arcsin()))
+
+ def arctan(self):
+ r"""
+ Arc tangent of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.arctan()))
+
+ def cosh(self):
+ r"""
+ Hyperbolic cosine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.cosh()))
+
+ def sinh(self):
+ r"""
+ Hyperbolic sine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.sinh()))
+
+ def tanh(self):
+ r"""
+ Hyperbolic tangent of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.tanh()))
+
+ def arccosh(self):
+ r"""
+ Inverse hyperbolic cosine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.arccosh()))
+
+ def arcsinh(self):
+ r"""
+ Inverse hyperbolic sine of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ self._simplify(self._express.arcsinh()))
+
+ def arctanh(self):
+ r"""
+ Inverse hyperbolic tangent of the coordinate function.
+
+ 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 CoordFunctionSymb(self._chart,
+ 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 the function.
+
+ 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:
+
+ - the coordinate function, 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 the function.
+
+ OUTPUT:
+
+ - the coordinate function, 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 the function.
+
+ OUTPUT:
+
+ - the coordinate function, 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 the coordinate
+ function into a group.
+
+ INPUT:
+
+ - ``s`` -- the symbol whose coefficients will be collected.
+
+ OUTPUT:
+
+ - the coordinate function, 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 the coordinate
+ function.
+
+ This method does not perform a full factorization but only looks
+ for factors which are already explicitly present.
+
+ OUTPUT:
+
+ - the coordinate function, 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
diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py
index 1f12a2f..28cc60c 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\rightarrow \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:
@@ -536,6 +576,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"""
@@ -1488,6 +1534,242 @@ 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``.
+
+ EXAMPLE:
+
+ 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.
+
+ 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`
+ - ``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``) name given to the scalar field
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the scalar
+ field; if none is provided, the LaTeX symbol is set to ``name``
+
+ 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 is provided, 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
+
##############################################################################
## Constructor function
diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py
new file mode 100644
index 0000000..7ce1b31
--- /dev/null
+++ b/src/sage/manifolds/scalarfield.py
@@ -0,0 +1,2790 @@
+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
+
+REFERENCES:
+
+- J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed., Springer (New
+ York) (2011)
+- 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>
+#
+# 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 .
+
+ The class :class:`ScalarField` inherits from the class :class:`~sage.structure.element.CommutativeAlgebraElement`, a scalar field
+ on `M` being 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``
+
+ NB: 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`
+ - ``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``
+
+ 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.CoordFunctionSymb'>
+ 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) |--> (a*u^2 + a*v^2)/(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^3 + u*v^2)/(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)\rightarrow C^0(U)` mentionned
+ 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.
+
+ TEST::
+
+ 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 self.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)
+
+ def __cmp__(self, other):
+ r"""
+ Old-style (Python 2) comparison operator.
+
+ This is provisory, until migration to Python 3 is achieved.
+
+ TESTS::
+
+ sage: M = Manifold(2, 'M', structure='topological')
+ sage: X.<x,y> = M.chart()
+ sage: f = M.scalar_field({X: x+y})
+ sage: f.__cmp__(1)
+ -1
+ sage: f.__cmp__(M.zero_scalar_field())
+ -1
+ sage: g = M.scalar_field({X: x+y})
+ sage: f.__cmp__(g)
+ 0
+
+ """
+ if self == other:
+ return 0
+ else:
+ return -1
+
+ ####### 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: repr(f) # indirect doctest
+ '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) # indirect doctest
+ \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)
+ sage: f.coord_function()
+ x*y^2
+ sage: f.coord_function(c_xy) # equivalent form (since c_xy is the default chart)
+ x*y^2
+ sage: type(f.coord_function())
+ <class 'sage.manifolds.coord_func_symb.CoordFunctionSymb'>
+
+ Expression via a change of coordinates::
+
+ sage: c_uv.<u,v> = M.chart()
+ sage: c_uv.transition_map(c_xy, [u+v, u-v])
+ Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y))
+ sage: f._express # at this stage, f is expressed only in terms of (x,y) coordinates
+ {Chart (M, (x, y)): x*y^2}
+ sage: f.coord_function(c_uv) # forces the computation of the expression of f in terms of (u,v) coordinates
+ u^3 - u^2*v - u*v^2 + v^3
+ sage: f.coord_function(c_uv) == (u+v)*(u-v)^2 # check
+ True
+ sage: f._express # random (dict. output); f has now 2 coordinate expressions:
+ {Chart (M, (x, y)): x*y^2, Chart (M, (u, v)): u^3 - u^2*v - u*v^2 + v^3}
+