summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Gourgoulhon <eric.gourgoulhon@obspm.fr>2016-01-03 15:36:56 +0100
committerEric Gourgoulhon <eric.gourgoulhon@obspm.fr>2016-01-03 15:36:56 +0100
commit9d25aa011ad37eaacc79fb5041dfb8f39318c73e (patch)
tree210f4f7ed49d7ee04a2471347b56cb1e9274ee13
parentRevert to AbstractNamedObject without argument full_name; restore _repr_() in... (diff)
parentScalar fields on topological manifolds: slight improvements in the documentation (diff)
Scalar field algebras with AbstractNamedObject without argument full_name
-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.py2
-rw-r--r--src/sage/manifolds/chart.py305
-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.py614
-rw-r--r--src/sage/manifolds/structure.py3
-rw-r--r--src/sage/manifolds/utilities.py915
13 files changed, 8333 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..352828f
--- /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 d7a0d18..0c4be1f 100644
--- a/src/doc/en/reference/manifolds/manifold.rst
+++ b/src/doc/en/reference/manifolds/manifold.rst
@@ -14,4 +14,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..99989a0
--- /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 c7defde..01834c1 100644
--- a/src/sage/manifolds/all.py
+++ b/src/sage/manifolds/all.py
@@ -1,2 +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 993f046..05ea5c3 100644
--- a/src/sage/manifolds/chart.py
+++ b/src/sage/manifolds/chart.py
@@ -38,6 +38,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"""
@@ -305,6 +306,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"""
@@ -810,6 +829,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):
@@ -1387,6 +1634,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.
@@ -1571,7 +1819,6 @@ class RealChart(Chart):
# All tests have been passed:
return True
-
#*****************************************************************************
class CoordChange(SageObject):
@@ -1645,10 +1892,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:
@@ -1718,13 +1964,7 @@ class CoordChange(SageObject):
(3, -1)
"""
- #*# When MultiCoordFunction is implemented (trac #18640):
- # return self._transf(*coords)
- #*# for now:
- substitutions = dict([(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"""
@@ -1760,6 +2000,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:
@@ -1785,10 +2027,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)
@@ -1799,6 +2038,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:
@@ -1807,6 +2054,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:
@@ -1868,7 +2123,7 @@ class CoordChange(SageObject):
sage: spher_to_cart.set_inverse(sqrt(x^3+y^2), atan2(y,x)) # note the x^3 typo
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)
@@ -1925,10 +2180,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):
@@ -1971,12 +2223,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"""
@@ -2012,10 +2260,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..03c18ce
--- /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..2be616e
--- /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 43dd572..08b7fb2 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:
@@ -513,6 +553,12 @@ class TopologicalManifold(AbstractSet):
# domains are self (if non-empty, self is a coordinate domain):
self._covering_charts = []
self._open_covers.append([self]) # list of open covers of self
+ # 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"""
@@ -1559,6 +1605,242 @@ class TopologicalManifold(AbstractSet):
"""
return self
+ 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