passagemath-symbolics 10.8.1a1__cp311-cp311-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
- passagemath_symbolics-10.8.1a1.dist-info/RECORD +182 -0
- passagemath_symbolics-10.8.1a1.dist-info/WHEEL +6 -0
- passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2838 -0
- sage/calculus/desolvers.py +1864 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-311-darwin.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-311-darwin.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +2991 -0
- sage/interfaces/magma_free.py +90 -0
- sage/interfaces/maple.py +1402 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +553 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4010 -0
- sage/manifolds/chart_func.py +3416 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
- sage/manifolds/differentiable/diff_form.py +1660 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1522 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +912 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1725 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2721 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +883 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1347 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-311-darwin.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1030 -0
- sage/matrix/matrix_symbolic_sparse.cpython-311-darwin.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5205 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +987 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +456 -0
- sage/symbolic/callable.pyi +66 -0
- sage/symbolic/comparison_impl.pyi +38 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1286 -0
- sage/symbolic/constants_c_impl.pyi +10 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1727 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/function_factory.pyi +41 -0
- sage/symbolic/getitem_impl.pyi +24 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +271 -0
- sage/symbolic/integration/integral.py +1075 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/operators.pyi +61 -0
- sage/symbolic/pynac_constant_impl.pyi +13 -0
- sage/symbolic/pynac_function_impl.pyi +8 -0
- sage/symbolic/random_tests.py +461 -0
- sage/symbolic/relation.py +2062 -0
- sage/symbolic/ring.cpython-311-darwin.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyi +110 -0
- sage/symbolic/ring.pyx +1393 -0
- sage/symbolic/series_impl.pyi +10 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- sage/symbolic/units.py +1468 -0
|
@@ -0,0 +1,3718 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-symbolics
|
|
2
|
+
r"""
|
|
3
|
+
Scalar Fields
|
|
4
|
+
|
|
5
|
+
Given a topological manifold `M` over a topological field `K` (in most
|
|
6
|
+
applications, `K = \RR` or `K = \CC`), a *scalar field* on `M` is a
|
|
7
|
+
continuous map
|
|
8
|
+
|
|
9
|
+
.. MATH::
|
|
10
|
+
|
|
11
|
+
f: M \longrightarrow K
|
|
12
|
+
|
|
13
|
+
Scalar fields are implemented by the class :class:`ScalarField`.
|
|
14
|
+
|
|
15
|
+
AUTHORS:
|
|
16
|
+
|
|
17
|
+
- Eric Gourgoulhon, Michal Bejger (2013-2015): initial version
|
|
18
|
+
- Travis Scrimshaw (2016): review tweaks
|
|
19
|
+
- Marco Mancini (2017): SymPy as an optional symbolic engine, alternative to SR
|
|
20
|
+
- Florentin Jaffredo (2018) : series expansion with respect to a given
|
|
21
|
+
parameter
|
|
22
|
+
- Michael Jung (2019) : improve restrictions; make ``display()`` show all
|
|
23
|
+
distinct expressions
|
|
24
|
+
|
|
25
|
+
REFERENCES:
|
|
26
|
+
|
|
27
|
+
- [Lee2011]_
|
|
28
|
+
- [KN1963]_
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# *****************************************************************************
|
|
32
|
+
# Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
|
|
33
|
+
# Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
|
|
34
|
+
# Copyright (C) 2016 Travis Scrimshaw <tscrimsh@umn.edu>
|
|
35
|
+
# Copyright (C) 2017 Marco Mancini <marco.mancini@obspm.fr>
|
|
36
|
+
#
|
|
37
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
38
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
39
|
+
# the License, or (at your option) any later version.
|
|
40
|
+
# https://www.gnu.org/licenses/
|
|
41
|
+
# *****************************************************************************
|
|
42
|
+
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
from typing import TYPE_CHECKING, Optional
|
|
46
|
+
|
|
47
|
+
from sage.manifolds.chart_func import ChartFunction
|
|
48
|
+
from sage.misc.cachefunc import cached_method
|
|
49
|
+
from sage.structure.element import (
|
|
50
|
+
CommutativeAlgebraElement,
|
|
51
|
+
ModuleElementWithMutability,
|
|
52
|
+
)
|
|
53
|
+
from sage.symbolic.expression import Expression
|
|
54
|
+
|
|
55
|
+
if TYPE_CHECKING:
|
|
56
|
+
from sage.manifolds.chart import Chart
|
|
57
|
+
from sage.tensor.modules.format_utilities import FormattedExpansion
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ScalarField(CommutativeAlgebraElement, ModuleElementWithMutability):
|
|
61
|
+
r"""
|
|
62
|
+
Scalar field on a topological manifold.
|
|
63
|
+
|
|
64
|
+
Given a topological manifold `M` over a topological field `K` (in most
|
|
65
|
+
applications, `K = \RR` or `K = \CC`), a *scalar field on* `M` is a
|
|
66
|
+
continuous map
|
|
67
|
+
|
|
68
|
+
.. MATH::
|
|
69
|
+
|
|
70
|
+
f: M \longrightarrow K.
|
|
71
|
+
|
|
72
|
+
A scalar field on `M` is an element of the commutative algebra
|
|
73
|
+
`C^0(M)` (see
|
|
74
|
+
:class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`).
|
|
75
|
+
|
|
76
|
+
INPUT:
|
|
77
|
+
|
|
78
|
+
- ``parent`` -- the algebra of scalar fields containing the scalar field
|
|
79
|
+
(must be an instance of class
|
|
80
|
+
:class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`)
|
|
81
|
+
|
|
82
|
+
- ``coord_expression`` -- (default: ``None``) coordinate expression(s) of
|
|
83
|
+
the scalar field; this can be either
|
|
84
|
+
|
|
85
|
+
* a dictionary of coordinate expressions in various charts on
|
|
86
|
+
the domain, with the charts as keys;
|
|
87
|
+
* a single coordinate expression; if the argument ``chart`` is
|
|
88
|
+
``'all'``, this expression is set to all the charts defined
|
|
89
|
+
on the open set; otherwise, the expression is set in the
|
|
90
|
+
specific chart provided by the argument ``chart``
|
|
91
|
+
|
|
92
|
+
- ``chart`` -- (default: ``None``) chart defining the coordinates used
|
|
93
|
+
in ``coord_expression`` when the latter is a single coordinate
|
|
94
|
+
expression; if none is provided (default), the default chart of the
|
|
95
|
+
open set is assumed. If ``chart=='all'``, ``coord_expression`` is
|
|
96
|
+
assumed to be independent of the chart (constant scalar field).
|
|
97
|
+
|
|
98
|
+
- ``name`` -- (default: ``None``) string; name (symbol) given to the
|
|
99
|
+
scalar field
|
|
100
|
+
|
|
101
|
+
- ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
|
|
102
|
+
scalar field; if none is provided, the LaTeX symbol is set to ``name``
|
|
103
|
+
|
|
104
|
+
If ``coord_expression`` is ``None`` or incomplete, coordinate
|
|
105
|
+
expressions can be added after the creation of the object, by means of
|
|
106
|
+
the methods :meth:`add_expr`, :meth:`add_expr_by_continuation` and
|
|
107
|
+
:meth:`set_expr`.
|
|
108
|
+
|
|
109
|
+
EXAMPLES:
|
|
110
|
+
|
|
111
|
+
A scalar field on the 2-sphere::
|
|
112
|
+
|
|
113
|
+
sage: M = Manifold(2, 'M', structure='topological') # the 2-dimensional sphere S^2
|
|
114
|
+
sage: U = M.open_subset('U') # complement of the North pole
|
|
115
|
+
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
|
|
116
|
+
sage: V = M.open_subset('V') # complement of the South pole
|
|
117
|
+
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
|
|
118
|
+
sage: M.declare_union(U,V) # S^2 is the union of U and V
|
|
119
|
+
sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
|
|
120
|
+
....: intersection_name='W',
|
|
121
|
+
....: restrictions1= x^2+y^2!=0,
|
|
122
|
+
....: restrictions2= u^2+v^2!=0)
|
|
123
|
+
sage: uv_to_xy = xy_to_uv.inverse()
|
|
124
|
+
sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2), c_uv: (u^2+v^2)/(1+u^2+v^2)},
|
|
125
|
+
....: name='f') ; f
|
|
126
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
127
|
+
sage: f.display()
|
|
128
|
+
f: M → ℝ
|
|
129
|
+
on U: (x, y) ↦ 1/(x^2 + y^2 + 1)
|
|
130
|
+
on V: (u, v) ↦ (u^2 + v^2)/(u^2 + v^2 + 1)
|
|
131
|
+
|
|
132
|
+
For scalar fields defined by a single coordinate expression, the latter
|
|
133
|
+
can be passed instead of the dictionary over the charts::
|
|
134
|
+
|
|
135
|
+
sage: g = U.scalar_field(x*y, chart=c_xy, name='g') ; g
|
|
136
|
+
Scalar field g on the Open subset U of the 2-dimensional topological
|
|
137
|
+
manifold M
|
|
138
|
+
|
|
139
|
+
The above is indeed equivalent to::
|
|
140
|
+
|
|
141
|
+
sage: g = U.scalar_field({c_xy: x*y}, name='g') ; g
|
|
142
|
+
Scalar field g on the Open subset U of the 2-dimensional topological
|
|
143
|
+
manifold M
|
|
144
|
+
|
|
145
|
+
Since ``c_xy`` is the default chart of ``U``, the argument ``chart`` can
|
|
146
|
+
be skipped::
|
|
147
|
+
|
|
148
|
+
sage: g = U.scalar_field(x*y, name='g') ; g
|
|
149
|
+
Scalar field g on the Open subset U of the 2-dimensional topological
|
|
150
|
+
manifold M
|
|
151
|
+
|
|
152
|
+
The scalar field `g` is defined on `U` and has an expression in terms of
|
|
153
|
+
the coordinates `(u,v)` on `W=U\cap V`::
|
|
154
|
+
|
|
155
|
+
sage: g.display()
|
|
156
|
+
g: U → ℝ
|
|
157
|
+
(x, y) ↦ x*y
|
|
158
|
+
on W: (u, v) ↦ u*v/(u^4 + 2*u^2*v^2 + v^4)
|
|
159
|
+
|
|
160
|
+
Scalar fields on `M` can also be declared with a single chart::
|
|
161
|
+
|
|
162
|
+
sage: f = M.scalar_field(1/(1+x^2+y^2), chart=c_xy, name='f') ; f
|
|
163
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
164
|
+
|
|
165
|
+
Their definition must then be completed by providing the expressions on
|
|
166
|
+
other charts, via the method :meth:`add_expr`, to get a global cover of
|
|
167
|
+
the manifold::
|
|
168
|
+
|
|
169
|
+
sage: f.add_expr((u^2+v^2)/(1+u^2+v^2), chart=c_uv)
|
|
170
|
+
sage: f.display()
|
|
171
|
+
f: M → ℝ
|
|
172
|
+
on U: (x, y) ↦ 1/(x^2 + y^2 + 1)
|
|
173
|
+
on V: (u, v) ↦ (u^2 + v^2)/(u^2 + v^2 + 1)
|
|
174
|
+
|
|
175
|
+
We can even first declare the scalar field without any coordinate
|
|
176
|
+
expression and provide them subsequently::
|
|
177
|
+
|
|
178
|
+
sage: f = M.scalar_field(name='f')
|
|
179
|
+
sage: f.add_expr(1/(1+x^2+y^2), chart=c_xy)
|
|
180
|
+
sage: f.add_expr((u^2+v^2)/(1+u^2+v^2), chart=c_uv)
|
|
181
|
+
sage: f.display()
|
|
182
|
+
f: M → ℝ
|
|
183
|
+
on U: (x, y) ↦ 1/(x^2 + y^2 + 1)
|
|
184
|
+
on V: (u, v) ↦ (u^2 + v^2)/(u^2 + v^2 + 1)
|
|
185
|
+
|
|
186
|
+
We may also use the method :meth:`add_expr_by_continuation` to complete
|
|
187
|
+
the coordinate definition using the analytic continuation from domains in
|
|
188
|
+
which charts overlap::
|
|
189
|
+
|
|
190
|
+
sage: f = M.scalar_field(1/(1+x^2+y^2), chart=c_xy, name='f') ; f
|
|
191
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
192
|
+
sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
|
|
193
|
+
sage: f.display()
|
|
194
|
+
f: M → ℝ
|
|
195
|
+
on U: (x, y) ↦ 1/(x^2 + y^2 + 1)
|
|
196
|
+
on V: (u, v) ↦ (u^2 + v^2)/(u^2 + v^2 + 1)
|
|
197
|
+
|
|
198
|
+
A scalar field can also be defined by some unspecified function of the
|
|
199
|
+
coordinates::
|
|
200
|
+
|
|
201
|
+
sage: h = U.scalar_field(function('H')(x, y), name='h') ; h
|
|
202
|
+
Scalar field h on the Open subset U of the 2-dimensional topological
|
|
203
|
+
manifold M
|
|
204
|
+
sage: h.display()
|
|
205
|
+
h: U → ℝ
|
|
206
|
+
(x, y) ↦ H(x, y)
|
|
207
|
+
on W: (u, v) ↦ H(u/(u^2 + v^2), v/(u^2 + v^2))
|
|
208
|
+
|
|
209
|
+
We may use the argument ``latex_name`` to specify the LaTeX symbol denoting
|
|
210
|
+
the scalar field if the latter is different from ``name``::
|
|
211
|
+
|
|
212
|
+
sage: latex(f)
|
|
213
|
+
f
|
|
214
|
+
sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2), c_uv: (u^2+v^2)/(1+u^2+v^2)},
|
|
215
|
+
....: name='f', latex_name=r'\mathcal{F}')
|
|
216
|
+
sage: latex(f)
|
|
217
|
+
\mathcal{F}
|
|
218
|
+
|
|
219
|
+
The coordinate expression in a given chart is obtained via the method
|
|
220
|
+
:meth:`expr`, which returns a symbolic expression::
|
|
221
|
+
|
|
222
|
+
sage: f.expr(c_uv)
|
|
223
|
+
(u^2 + v^2)/(u^2 + v^2 + 1)
|
|
224
|
+
sage: type(f.expr(c_uv))
|
|
225
|
+
<class 'sage.symbolic.expression.Expression'>
|
|
226
|
+
|
|
227
|
+
The method :meth:`coord_function` returns instead a function of the
|
|
228
|
+
chart coordinates, i.e. an instance of
|
|
229
|
+
:class:`~sage.manifolds.chart_func.ChartFunction`::
|
|
230
|
+
|
|
231
|
+
sage: f.coord_function(c_uv)
|
|
232
|
+
(u^2 + v^2)/(u^2 + v^2 + 1)
|
|
233
|
+
sage: type(f.coord_function(c_uv))
|
|
234
|
+
<class 'sage.manifolds.chart_func.ChartFunctionRing_with_category.element_class'>
|
|
235
|
+
sage: f.coord_function(c_uv).display()
|
|
236
|
+
(u, v) ↦ (u^2 + v^2)/(u^2 + v^2 + 1)
|
|
237
|
+
|
|
238
|
+
The value returned by the method :meth:`expr` is actually the coordinate
|
|
239
|
+
expression of the chart function::
|
|
240
|
+
|
|
241
|
+
sage: f.expr(c_uv) is f.coord_function(c_uv).expr()
|
|
242
|
+
True
|
|
243
|
+
|
|
244
|
+
A constant scalar field is declared by setting the argument ``chart`` to
|
|
245
|
+
``'all'``::
|
|
246
|
+
|
|
247
|
+
sage: c = M.scalar_field(2, chart='all', name='c') ; c
|
|
248
|
+
Scalar field c on the 2-dimensional topological manifold M
|
|
249
|
+
sage: c.display()
|
|
250
|
+
c: M → ℝ
|
|
251
|
+
on U: (x, y) ↦ 2
|
|
252
|
+
on V: (u, v) ↦ 2
|
|
253
|
+
|
|
254
|
+
A shortcut is to use the method
|
|
255
|
+
:meth:`~sage.manifolds.manifold.TopologicalManifold.constant_scalar_field`::
|
|
256
|
+
|
|
257
|
+
sage: c == M.constant_scalar_field(2)
|
|
258
|
+
True
|
|
259
|
+
|
|
260
|
+
The constant value can be some unspecified parameter::
|
|
261
|
+
|
|
262
|
+
sage: var('a')
|
|
263
|
+
a
|
|
264
|
+
sage: c = M.constant_scalar_field(a, name='c') ; c
|
|
265
|
+
Scalar field c on the 2-dimensional topological manifold M
|
|
266
|
+
sage: c.display()
|
|
267
|
+
c: M → ℝ
|
|
268
|
+
on U: (x, y) ↦ a
|
|
269
|
+
on V: (u, v) ↦ a
|
|
270
|
+
|
|
271
|
+
A special case of constant field is the zero scalar field::
|
|
272
|
+
|
|
273
|
+
sage: zer = M.constant_scalar_field(0) ; zer
|
|
274
|
+
Scalar field zero on the 2-dimensional topological manifold M
|
|
275
|
+
sage: zer.display()
|
|
276
|
+
zero: M → ℝ
|
|
277
|
+
on U: (x, y) ↦ 0
|
|
278
|
+
on V: (u, v) ↦ 0
|
|
279
|
+
|
|
280
|
+
It can be obtained directly by means of the function
|
|
281
|
+
:meth:`~sage.manifolds.manifold.TopologicalManifold.zero_scalar_field`::
|
|
282
|
+
|
|
283
|
+
sage: zer is M.zero_scalar_field()
|
|
284
|
+
True
|
|
285
|
+
|
|
286
|
+
A third way is to get it as the zero element of the algebra `C^0(M)`
|
|
287
|
+
of scalar fields on `M` (see below)::
|
|
288
|
+
|
|
289
|
+
sage: zer is M.scalar_field_algebra().zero()
|
|
290
|
+
True
|
|
291
|
+
|
|
292
|
+
The constant scalar fields zero and one are immutable, and therefore
|
|
293
|
+
their expressions cannot be changed::
|
|
294
|
+
|
|
295
|
+
sage: zer.is_immutable()
|
|
296
|
+
True
|
|
297
|
+
sage: zer.set_expr(x)
|
|
298
|
+
Traceback (most recent call last):
|
|
299
|
+
...
|
|
300
|
+
ValueError: the expressions of an immutable element cannot be
|
|
301
|
+
changed
|
|
302
|
+
sage: one = M.one_scalar_field()
|
|
303
|
+
sage: one.is_immutable()
|
|
304
|
+
True
|
|
305
|
+
sage: one.set_expr(x)
|
|
306
|
+
Traceback (most recent call last):
|
|
307
|
+
...
|
|
308
|
+
ValueError: the expressions of an immutable element cannot be
|
|
309
|
+
changed
|
|
310
|
+
|
|
311
|
+
Other scalar fields can be declared immutable, too::
|
|
312
|
+
|
|
313
|
+
sage: c.is_immutable()
|
|
314
|
+
False
|
|
315
|
+
sage: c.set_immutable()
|
|
316
|
+
sage: c.is_immutable()
|
|
317
|
+
True
|
|
318
|
+
sage: c.set_expr(y^2)
|
|
319
|
+
Traceback (most recent call last):
|
|
320
|
+
...
|
|
321
|
+
ValueError: the expressions of an immutable element cannot be
|
|
322
|
+
changed
|
|
323
|
+
sage: c.set_name('b')
|
|
324
|
+
Traceback (most recent call last):
|
|
325
|
+
...
|
|
326
|
+
ValueError: the name of an immutable element cannot be changed
|
|
327
|
+
|
|
328
|
+
Immutable elements are hashable and can therefore be used as keys for
|
|
329
|
+
dictionaries::
|
|
330
|
+
|
|
331
|
+
sage: {c: 1}[c]
|
|
332
|
+
1
|
|
333
|
+
|
|
334
|
+
By definition, a scalar field acts on the manifold's points, sending
|
|
335
|
+
them to elements of the manifold's base field (real numbers in the
|
|
336
|
+
present case)::
|
|
337
|
+
|
|
338
|
+
sage: N = M.point((0,0), chart=c_uv) # the North pole
|
|
339
|
+
sage: S = M.point((0,0), chart=c_xy) # the South pole
|
|
340
|
+
sage: E = M.point((1,0), chart=c_xy) # a point at the equator
|
|
341
|
+
sage: f(N)
|
|
342
|
+
0
|
|
343
|
+
sage: f(S)
|
|
344
|
+
1
|
|
345
|
+
sage: f(E)
|
|
346
|
+
1/2
|
|
347
|
+
sage: h(E)
|
|
348
|
+
H(1, 0)
|
|
349
|
+
sage: c(E)
|
|
350
|
+
a
|
|
351
|
+
sage: zer(E)
|
|
352
|
+
0
|
|
353
|
+
|
|
354
|
+
A scalar field can be compared to another scalar field::
|
|
355
|
+
|
|
356
|
+
sage: f == g
|
|
357
|
+
False
|
|
358
|
+
|
|
359
|
+
...to a symbolic expression::
|
|
360
|
+
|
|
361
|
+
sage: f == x*y
|
|
362
|
+
False
|
|
363
|
+
sage: g == x*y
|
|
364
|
+
True
|
|
365
|
+
sage: c == a
|
|
366
|
+
True
|
|
367
|
+
|
|
368
|
+
...to a number::
|
|
369
|
+
|
|
370
|
+
sage: f == 2
|
|
371
|
+
False
|
|
372
|
+
sage: zer == 0
|
|
373
|
+
True
|
|
374
|
+
|
|
375
|
+
...to anything else::
|
|
376
|
+
|
|
377
|
+
sage: f == M
|
|
378
|
+
False
|
|
379
|
+
|
|
380
|
+
Standard mathematical functions are implemented::
|
|
381
|
+
|
|
382
|
+
sage: sqrt(f)
|
|
383
|
+
Scalar field sqrt(f) on the 2-dimensional topological manifold M
|
|
384
|
+
sage: sqrt(f).display()
|
|
385
|
+
sqrt(f): M → ℝ
|
|
386
|
+
on U: (x, y) ↦ 1/sqrt(x^2 + y^2 + 1)
|
|
387
|
+
on V: (u, v) ↦ sqrt(u^2 + v^2)/sqrt(u^2 + v^2 + 1)
|
|
388
|
+
|
|
389
|
+
::
|
|
390
|
+
|
|
391
|
+
sage: tan(f)
|
|
392
|
+
Scalar field tan(f) on the 2-dimensional topological manifold M
|
|
393
|
+
sage: tan(f).display()
|
|
394
|
+
tan(f): M → ℝ
|
|
395
|
+
on U: (x, y) ↦ sin(1/(x^2 + y^2 + 1))/cos(1/(x^2 + y^2 + 1))
|
|
396
|
+
on V: (u, v) ↦ sin((u^2 + v^2)/(u^2 + v^2 + 1))/cos((u^2 + v^2)/(u^2 + v^2 + 1))
|
|
397
|
+
|
|
398
|
+
.. RUBRIC:: Arithmetics of scalar fields
|
|
399
|
+
|
|
400
|
+
Scalar fields on `M` (resp. `U`) belong to the algebra `C^0(M)`
|
|
401
|
+
(resp. `C^0(U)`)::
|
|
402
|
+
|
|
403
|
+
sage: f.parent()
|
|
404
|
+
Algebra of scalar fields on the 2-dimensional topological manifold M
|
|
405
|
+
sage: f.parent() is M.scalar_field_algebra()
|
|
406
|
+
True
|
|
407
|
+
sage: g.parent()
|
|
408
|
+
Algebra of scalar fields on the Open subset U of the 2-dimensional
|
|
409
|
+
topological manifold M
|
|
410
|
+
sage: g.parent() is U.scalar_field_algebra()
|
|
411
|
+
True
|
|
412
|
+
|
|
413
|
+
Consequently, scalar fields can be added::
|
|
414
|
+
|
|
415
|
+
sage: s = f + c ; s
|
|
416
|
+
Scalar field f+c on the 2-dimensional topological manifold M
|
|
417
|
+
sage: s.display()
|
|
418
|
+
f+c: M → ℝ
|
|
419
|
+
on U: (x, y) ↦ (a*x^2 + a*y^2 + a + 1)/(x^2 + y^2 + 1)
|
|
420
|
+
on V: (u, v) ↦ ((a + 1)*u^2 + (a + 1)*v^2 + a)/(u^2 + v^2 + 1)
|
|
421
|
+
|
|
422
|
+
and subtracted::
|
|
423
|
+
|
|
424
|
+
sage: s = f - c ; s
|
|
425
|
+
Scalar field f-c on the 2-dimensional topological manifold M
|
|
426
|
+
sage: s.display()
|
|
427
|
+
f-c: M → ℝ
|
|
428
|
+
on U: (x, y) ↦ -(a*x^2 + a*y^2 + a - 1)/(x^2 + y^2 + 1)
|
|
429
|
+
on V: (u, v) ↦ -((a - 1)*u^2 + (a - 1)*v^2 + a)/(u^2 + v^2 + 1)
|
|
430
|
+
|
|
431
|
+
Some tests::
|
|
432
|
+
|
|
433
|
+
sage: f + zer == f
|
|
434
|
+
True
|
|
435
|
+
sage: f - f == zer
|
|
436
|
+
True
|
|
437
|
+
sage: f + (-f) == zer
|
|
438
|
+
True
|
|
439
|
+
sage: (f+c)-f == c
|
|
440
|
+
True
|
|
441
|
+
sage: (f-c)+c == f
|
|
442
|
+
True
|
|
443
|
+
|
|
444
|
+
We may add a number (interpreted as a constant scalar field) to a scalar
|
|
445
|
+
field::
|
|
446
|
+
|
|
447
|
+
sage: s = f + 1 ; s
|
|
448
|
+
Scalar field f+1 on the 2-dimensional topological manifold M
|
|
449
|
+
sage: s.display()
|
|
450
|
+
f+1: M → ℝ
|
|
451
|
+
on U: (x, y) ↦ (x^2 + y^2 + 2)/(x^2 + y^2 + 1)
|
|
452
|
+
on V: (u, v) ↦ (2*u^2 + 2*v^2 + 1)/(u^2 + v^2 + 1)
|
|
453
|
+
sage: (f+1)-1 == f
|
|
454
|
+
True
|
|
455
|
+
|
|
456
|
+
The number can represented by a symbolic variable::
|
|
457
|
+
|
|
458
|
+
sage: s = a + f ; s
|
|
459
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
460
|
+
sage: s == c + f
|
|
461
|
+
True
|
|
462
|
+
|
|
463
|
+
However if the symbolic variable is a chart coordinate, the addition
|
|
464
|
+
is performed only on the chart domain::
|
|
465
|
+
|
|
466
|
+
sage: s = f + x; s
|
|
467
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
468
|
+
sage: s.display()
|
|
469
|
+
M → ℝ
|
|
470
|
+
on U: (x, y) ↦ (x^3 + x*y^2 + x + 1)/(x^2 + y^2 + 1)
|
|
471
|
+
on W: (u, v) ↦ (u^4 + v^4 + u^3 + (2*u^2 + u)*v^2 + u)/(u^4 + v^4 + (2*u^2 + 1)*v^2 + u^2)
|
|
472
|
+
sage: s = f + u; s
|
|
473
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
474
|
+
sage: s.display()
|
|
475
|
+
M → ℝ
|
|
476
|
+
on W: (x, y) ↦ (x^3 + (x + 1)*y^2 + x^2 + x)/(x^4 + y^4 + (2*x^2 + 1)*y^2 + x^2)
|
|
477
|
+
on V: (u, v) ↦ (u^3 + (u + 1)*v^2 + u^2 + u)/(u^2 + v^2 + 1)
|
|
478
|
+
|
|
479
|
+
The addition of two scalar fields with different domains is possible if
|
|
480
|
+
the domain of one of them is a subset of the domain of the other; the
|
|
481
|
+
domain of the result is then this subset::
|
|
482
|
+
|
|
483
|
+
sage: f.domain()
|
|
484
|
+
2-dimensional topological manifold M
|
|
485
|
+
sage: g.domain()
|
|
486
|
+
Open subset U of the 2-dimensional topological manifold M
|
|
487
|
+
sage: s = f + g ; s
|
|
488
|
+
Scalar field f+g on the Open subset U of the 2-dimensional topological
|
|
489
|
+
manifold M
|
|
490
|
+
sage: s.domain()
|
|
491
|
+
Open subset U of the 2-dimensional topological manifold M
|
|
492
|
+
sage: s.display()
|
|
493
|
+
f+g: U → ℝ
|
|
494
|
+
(x, y) ↦ (x*y^3 + (x^3 + x)*y + 1)/(x^2 + y^2 + 1)
|
|
495
|
+
on W: (u, v) ↦ (u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6 + u*v^3
|
|
496
|
+
+ (u^3 + u)*v)/(u^6 + v^6 + (3*u^2 + 1)*v^4 + u^4 + (3*u^4 + 2*u^2)*v^2)
|
|
497
|
+
|
|
498
|
+
The operation actually performed is `f|_U + g`::
|
|
499
|
+
|
|
500
|
+
sage: s == f.restrict(U) + g
|
|
501
|
+
True
|
|
502
|
+
|
|
503
|
+
In Sage framework, the addition of `f` and `g` is permitted because
|
|
504
|
+
there is a *coercion* of the parent of `f`, namely `C^0(M)`, to
|
|
505
|
+
the parent of `g`, namely `C^0(U)` (see
|
|
506
|
+
:class:`~sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra`)::
|
|
507
|
+
|
|
508
|
+
sage: CM = M.scalar_field_algebra()
|
|
509
|
+
sage: CU = U.scalar_field_algebra()
|
|
510
|
+
sage: CU.has_coerce_map_from(CM)
|
|
511
|
+
True
|
|
512
|
+
|
|
513
|
+
The coercion map is nothing but the restriction to domain `U`::
|
|
514
|
+
|
|
515
|
+
sage: CU.coerce(f) == f.restrict(U)
|
|
516
|
+
True
|
|
517
|
+
|
|
518
|
+
Since the algebra `C^0(M)` is a vector space over `\RR`, scalar fields
|
|
519
|
+
can be multiplied by a number, either an explicit one::
|
|
520
|
+
|
|
521
|
+
sage: s = 2*f ; s
|
|
522
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
523
|
+
sage: s.display()
|
|
524
|
+
M → ℝ
|
|
525
|
+
on U: (x, y) ↦ 2/(x^2 + y^2 + 1)
|
|
526
|
+
on V: (u, v) ↦ 2*(u^2 + v^2)/(u^2 + v^2 + 1)
|
|
527
|
+
|
|
528
|
+
or a symbolic one::
|
|
529
|
+
|
|
530
|
+
sage: s = a*f ; s
|
|
531
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
532
|
+
sage: s.display()
|
|
533
|
+
M → ℝ
|
|
534
|
+
on U: (x, y) ↦ a/(x^2 + y^2 + 1)
|
|
535
|
+
on V: (u, v) ↦ (u^2 + v^2)*a/(u^2 + v^2 + 1)
|
|
536
|
+
|
|
537
|
+
However, if the symbolic variable is a chart coordinate, the
|
|
538
|
+
multiplication is performed only in the corresponding chart::
|
|
539
|
+
|
|
540
|
+
sage: s = x*f; s
|
|
541
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
542
|
+
sage: s.display()
|
|
543
|
+
M → ℝ
|
|
544
|
+
on U: (x, y) ↦ x/(x^2 + y^2 + 1)
|
|
545
|
+
on W: (u, v) ↦ u/(u^2 + v^2 + 1)
|
|
546
|
+
sage: s = u*f; s
|
|
547
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
548
|
+
sage: s.display()
|
|
549
|
+
M → ℝ
|
|
550
|
+
on W: (x, y) ↦ x/(x^4 + y^4 + (2*x^2 + 1)*y^2 + x^2)
|
|
551
|
+
on V: (u, v) ↦ (u^2 + v^2)*u/(u^2 + v^2 + 1)
|
|
552
|
+
|
|
553
|
+
Some tests::
|
|
554
|
+
|
|
555
|
+
sage: 0*f == 0
|
|
556
|
+
True
|
|
557
|
+
sage: 0*f == zer
|
|
558
|
+
True
|
|
559
|
+
sage: 1*f == f
|
|
560
|
+
True
|
|
561
|
+
sage: (-2)*f == - f - f
|
|
562
|
+
True
|
|
563
|
+
|
|
564
|
+
The ring multiplication of the algebras `C^0(M)` and `C^0(U)`
|
|
565
|
+
is the pointwise multiplication of functions::
|
|
566
|
+
|
|
567
|
+
sage: s = f*f ; s
|
|
568
|
+
Scalar field f*f on the 2-dimensional topological manifold M
|
|
569
|
+
sage: s.display()
|
|
570
|
+
f*f: M → ℝ
|
|
571
|
+
on U: (x, y) ↦ 1/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1)
|
|
572
|
+
on V: (u, v) ↦ (u^4 + 2*u^2*v^2 + v^4)/(u^4 + v^4 + 2*(u^2 + 1)*v^2
|
|
573
|
+
+ 2*u^2 + 1)
|
|
574
|
+
sage: s = g*h ; s
|
|
575
|
+
Scalar field g*h on the Open subset U of the 2-dimensional topological
|
|
576
|
+
manifold M
|
|
577
|
+
sage: s.display()
|
|
578
|
+
g*h: U → ℝ
|
|
579
|
+
(x, y) ↦ x*y*H(x, y)
|
|
580
|
+
on W: (u, v) ↦ u*v*H(u/(u^2 + v^2), v/(u^2 + v^2))/(u^4 + 2*u^2*v^2 + v^4)
|
|
581
|
+
|
|
582
|
+
Thanks to the coercion `C^0(M) \to C^0(U)` mentioned above,
|
|
583
|
+
it is possible to multiply a scalar field defined on `M` by a
|
|
584
|
+
scalar field defined on `U`, the result being a scalar field
|
|
585
|
+
defined on `U`::
|
|
586
|
+
|
|
587
|
+
sage: f.domain(), g.domain()
|
|
588
|
+
(2-dimensional topological manifold M,
|
|
589
|
+
Open subset U of the 2-dimensional topological manifold M)
|
|
590
|
+
sage: s = f*g ; s
|
|
591
|
+
Scalar field f*g on the Open subset U of the 2-dimensional topological
|
|
592
|
+
manifold M
|
|
593
|
+
sage: s.display()
|
|
594
|
+
f*g: U → ℝ
|
|
595
|
+
(x, y) ↦ x*y/(x^2 + y^2 + 1)
|
|
596
|
+
on W: (u, v) ↦ u*v/(u^4 + v^4 + (2*u^2 + 1)*v^2 + u^2)
|
|
597
|
+
sage: s == f.restrict(U)*g
|
|
598
|
+
True
|
|
599
|
+
|
|
600
|
+
Scalar fields can be divided (pointwise division)::
|
|
601
|
+
|
|
602
|
+
sage: s = f/c ; s
|
|
603
|
+
Scalar field f/c on the 2-dimensional topological manifold M
|
|
604
|
+
sage: s.display()
|
|
605
|
+
f/c: M → ℝ
|
|
606
|
+
on U: (x, y) ↦ 1/(a*x^2 + a*y^2 + a)
|
|
607
|
+
on V: (u, v) ↦ (u^2 + v^2)/(a*u^2 + a*v^2 + a)
|
|
608
|
+
sage: s = g/h ; s
|
|
609
|
+
Scalar field g/h on the Open subset U of the 2-dimensional topological
|
|
610
|
+
manifold M
|
|
611
|
+
sage: s.display()
|
|
612
|
+
g/h: U → ℝ
|
|
613
|
+
(x, y) ↦ x*y/H(x, y)
|
|
614
|
+
on W: (u, v) ↦ u*v/((u^4 + 2*u^2*v^2 + v^4)*H(u/(u^2 + v^2), v/(u^2 + v^2)))
|
|
615
|
+
sage: s = f/g ; s
|
|
616
|
+
Scalar field f/g on the Open subset U of the 2-dimensional topological
|
|
617
|
+
manifold M
|
|
618
|
+
sage: s.display()
|
|
619
|
+
f/g: U → ℝ
|
|
620
|
+
(x, y) ↦ 1/(x*y^3 + (x^3 + x)*y)
|
|
621
|
+
on W: (u, v) ↦ (u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6)/(u*v^3 + (u^3 + u)*v)
|
|
622
|
+
sage: s == f.restrict(U)/g
|
|
623
|
+
True
|
|
624
|
+
|
|
625
|
+
For scalar fields defined on a single chart domain, we may perform some
|
|
626
|
+
arithmetics with symbolic expressions involving the chart coordinates::
|
|
627
|
+
|
|
628
|
+
sage: s = g + x^2 - y ; s
|
|
629
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
630
|
+
manifold M
|
|
631
|
+
sage: s.display()
|
|
632
|
+
U → ℝ
|
|
633
|
+
(x, y) ↦ x^2 + (x - 1)*y
|
|
634
|
+
on W: (u, v) ↦ -(v^3 - u^2 + (u^2 - u)*v)/(u^4 + 2*u^2*v^2 + v^4)
|
|
635
|
+
|
|
636
|
+
::
|
|
637
|
+
|
|
638
|
+
sage: s = g*x ; s
|
|
639
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
640
|
+
manifold M
|
|
641
|
+
sage: s.display()
|
|
642
|
+
U → ℝ
|
|
643
|
+
(x, y) ↦ x^2*y
|
|
644
|
+
on W: (u, v) ↦ u^2*v/(u^6 + 3*u^4*v^2 + 3*u^2*v^4 + v^6)
|
|
645
|
+
|
|
646
|
+
::
|
|
647
|
+
|
|
648
|
+
sage: s = g/x ; s
|
|
649
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
650
|
+
manifold M
|
|
651
|
+
sage: s.display()
|
|
652
|
+
U → ℝ
|
|
653
|
+
(x, y) ↦ y
|
|
654
|
+
on W: (u, v) ↦ v/(u^2 + v^2)
|
|
655
|
+
sage: s = x/g ; s
|
|
656
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
657
|
+
manifold M
|
|
658
|
+
sage: s.display()
|
|
659
|
+
U → ℝ
|
|
660
|
+
(x, y) ↦ 1/y
|
|
661
|
+
on W: (u, v) ↦ (u^2 + v^2)/v
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
.. RUBRIC:: Examples with SymPy as the symbolic engine
|
|
665
|
+
|
|
666
|
+
From now on, we ask that all symbolic calculus on manifold `M` are
|
|
667
|
+
performed by SymPy::
|
|
668
|
+
|
|
669
|
+
sage: M.set_calculus_method('sympy')
|
|
670
|
+
|
|
671
|
+
We define `f` as above::
|
|
672
|
+
|
|
673
|
+
sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2), c_uv: (u^2+v^2)/(1+u^2+v^2)},
|
|
674
|
+
....: name='f') ; f
|
|
675
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
676
|
+
sage: f.display() # notice the SymPy display of exponents
|
|
677
|
+
f: M → ℝ
|
|
678
|
+
on U: (x, y) ↦ 1/(x**2 + y**2 + 1)
|
|
679
|
+
on V: (u, v) ↦ (u**2 + v**2)/(u**2 + v**2 + 1)
|
|
680
|
+
sage: type(f.coord_function(c_xy).expr())
|
|
681
|
+
<class 'sympy.core.power.Pow'>
|
|
682
|
+
|
|
683
|
+
The scalar field `g` defined on `U`::
|
|
684
|
+
|
|
685
|
+
sage: g = U.scalar_field({c_xy: x*y}, name='g')
|
|
686
|
+
sage: g.display() # again notice the SymPy display of exponents
|
|
687
|
+
g: U → ℝ
|
|
688
|
+
(x, y) ↦ x*y
|
|
689
|
+
on W: (u, v) ↦ u*v/(u**4 + 2*u**2*v**2 + v**4)
|
|
690
|
+
|
|
691
|
+
Definition on a single chart and subsequent completion::
|
|
692
|
+
|
|
693
|
+
sage: f = M.scalar_field(1/(1+x^2+y^2), chart=c_xy, name='f')
|
|
694
|
+
sage: f.add_expr((u^2+v^2)/(1+u^2+v^2), chart=c_uv)
|
|
695
|
+
sage: f.display()
|
|
696
|
+
f: M → ℝ
|
|
697
|
+
on U: (x, y) ↦ 1/(x**2 + y**2 + 1)
|
|
698
|
+
on V: (u, v) ↦ (u**2 + v**2)/(u**2 + v**2 + 1)
|
|
699
|
+
|
|
700
|
+
Definition without any coordinate expression and subsequent completion::
|
|
701
|
+
|
|
702
|
+
sage: f = M.scalar_field(name='f')
|
|
703
|
+
sage: f.add_expr(1/(1+x^2+y^2), chart=c_xy)
|
|
704
|
+
sage: f.add_expr((u^2+v^2)/(1+u^2+v^2), chart=c_uv)
|
|
705
|
+
sage: f.display()
|
|
706
|
+
f: M → ℝ
|
|
707
|
+
on U: (x, y) ↦ 1/(x**2 + y**2 + 1)
|
|
708
|
+
on V: (u, v) ↦ (u**2 + v**2)/(u**2 + v**2 + 1)
|
|
709
|
+
|
|
710
|
+
Use of :meth:`add_expr_by_continuation`::
|
|
711
|
+
|
|
712
|
+
sage: f = M.scalar_field(1/(1+x^2+y^2), chart=c_xy, name='f')
|
|
713
|
+
sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
|
|
714
|
+
sage: f.display()
|
|
715
|
+
f: M → ℝ
|
|
716
|
+
on U: (x, y) ↦ 1/(x**2 + y**2 + 1)
|
|
717
|
+
on V: (u, v) ↦ (u**2 + v**2)/(u**2 + v**2 + 1)
|
|
718
|
+
|
|
719
|
+
A scalar field defined by some unspecified function of the
|
|
720
|
+
coordinates::
|
|
721
|
+
|
|
722
|
+
sage: h = U.scalar_field(function('H')(x, y), name='h') ; h
|
|
723
|
+
Scalar field h on the Open subset U of the 2-dimensional topological
|
|
724
|
+
manifold M
|
|
725
|
+
sage: h.display()
|
|
726
|
+
h: U → ℝ
|
|
727
|
+
(x, y) ↦ H(x, y)
|
|
728
|
+
on W: (u, v) ↦ H(u/(u**2 + v**2), v/(u**2 + v**2))
|
|
729
|
+
|
|
730
|
+
The coordinate expression in a given chart is obtained via the method
|
|
731
|
+
:meth:`expr`, which in the present context, returns a SymPy object::
|
|
732
|
+
|
|
733
|
+
sage: f.expr(c_uv)
|
|
734
|
+
(u**2 + v**2)/(u**2 + v**2 + 1)
|
|
735
|
+
sage: type(f.expr(c_uv))
|
|
736
|
+
<class 'sympy.core.mul.Mul'>
|
|
737
|
+
|
|
738
|
+
The method :meth:`coord_function` returns instead a function of the
|
|
739
|
+
chart coordinates, i.e. an instance of
|
|
740
|
+
:class:`~sage.manifolds.chart_func.ChartFunction`::
|
|
741
|
+
|
|
742
|
+
sage: f.coord_function(c_uv)
|
|
743
|
+
(u**2 + v**2)/(u**2 + v**2 + 1)
|
|
744
|
+
sage: type(f.coord_function(c_uv))
|
|
745
|
+
<class 'sage.manifolds.chart_func.ChartFunctionRing_with_category.element_class'>
|
|
746
|
+
sage: f.coord_function(c_uv).display()
|
|
747
|
+
(u, v) ↦ (u**2 + v**2)/(u**2 + v**2 + 1)
|
|
748
|
+
|
|
749
|
+
The value returned by the method :meth:`expr` is actually the coordinate
|
|
750
|
+
expression of the chart function::
|
|
751
|
+
|
|
752
|
+
sage: f.expr(c_uv) is f.coord_function(c_uv).expr()
|
|
753
|
+
True
|
|
754
|
+
|
|
755
|
+
We may ask for the ``SR`` representation of the coordinate function::
|
|
756
|
+
|
|
757
|
+
sage: f.coord_function(c_uv).expr('SR')
|
|
758
|
+
(u^2 + v^2)/(u^2 + v^2 + 1)
|
|
759
|
+
|
|
760
|
+
A constant scalar field with SymPy representation::
|
|
761
|
+
|
|
762
|
+
sage: c = M.constant_scalar_field(2, name='c')
|
|
763
|
+
sage: c.display()
|
|
764
|
+
c: M → ℝ
|
|
765
|
+
on U: (x, y) ↦ 2
|
|
766
|
+
on V: (u, v) ↦ 2
|
|
767
|
+
sage: type(c.expr(c_xy))
|
|
768
|
+
<class 'sympy.core.numbers.Integer'>
|
|
769
|
+
|
|
770
|
+
The constant value can be some unspecified parameter::
|
|
771
|
+
|
|
772
|
+
sage: var('a')
|
|
773
|
+
a
|
|
774
|
+
sage: c = M.constant_scalar_field(a, name='c')
|
|
775
|
+
sage: c.display()
|
|
776
|
+
c: M → ℝ
|
|
777
|
+
on U: (x, y) ↦ a
|
|
778
|
+
on V: (u, v) ↦ a
|
|
779
|
+
sage: type(c.expr(c_xy))
|
|
780
|
+
<class 'sympy.core.symbol.Symbol'>
|
|
781
|
+
|
|
782
|
+
The zero scalar field::
|
|
783
|
+
|
|
784
|
+
sage: zer = M.constant_scalar_field(0) ; zer
|
|
785
|
+
Scalar field zero on the 2-dimensional topological manifold M
|
|
786
|
+
sage: zer.display()
|
|
787
|
+
zero: M → ℝ
|
|
788
|
+
on U: (x, y) ↦ 0
|
|
789
|
+
on V: (u, v) ↦ 0
|
|
790
|
+
sage: type(zer.expr(c_xy))
|
|
791
|
+
<class 'sympy.core.numbers.Zero'>
|
|
792
|
+
sage: zer is M.zero_scalar_field()
|
|
793
|
+
True
|
|
794
|
+
|
|
795
|
+
Action of scalar fields on manifold's points::
|
|
796
|
+
|
|
797
|
+
sage: N = M.point((0,0), chart=c_uv) # the North pole
|
|
798
|
+
sage: S = M.point((0,0), chart=c_xy) # the South pole
|
|
799
|
+
sage: E = M.point((1,0), chart=c_xy) # a point at the equator
|
|
800
|
+
sage: f(N)
|
|
801
|
+
0
|
|
802
|
+
sage: f(S)
|
|
803
|
+
1
|
|
804
|
+
sage: f(E)
|
|
805
|
+
1/2
|
|
806
|
+
sage: h(E)
|
|
807
|
+
H(1, 0)
|
|
808
|
+
sage: c(E)
|
|
809
|
+
a
|
|
810
|
+
sage: zer(E)
|
|
811
|
+
0
|
|
812
|
+
|
|
813
|
+
A scalar field can be compared to another scalar field::
|
|
814
|
+
|
|
815
|
+
sage: f == g
|
|
816
|
+
False
|
|
817
|
+
|
|
818
|
+
...to a symbolic expression::
|
|
819
|
+
|
|
820
|
+
sage: f == x*y
|
|
821
|
+
False
|
|
822
|
+
sage: g == x*y
|
|
823
|
+
True
|
|
824
|
+
sage: c == a
|
|
825
|
+
True
|
|
826
|
+
|
|
827
|
+
...to a number::
|
|
828
|
+
|
|
829
|
+
sage: f == 2
|
|
830
|
+
False
|
|
831
|
+
sage: zer == 0
|
|
832
|
+
True
|
|
833
|
+
|
|
834
|
+
...to anything else::
|
|
835
|
+
|
|
836
|
+
sage: f == M
|
|
837
|
+
False
|
|
838
|
+
|
|
839
|
+
Standard mathematical functions are implemented::
|
|
840
|
+
|
|
841
|
+
sage: sqrt(f)
|
|
842
|
+
Scalar field sqrt(f) on the 2-dimensional topological manifold M
|
|
843
|
+
sage: sqrt(f).display()
|
|
844
|
+
sqrt(f): M → ℝ
|
|
845
|
+
on U: (x, y) ↦ 1/sqrt(x**2 + y**2 + 1)
|
|
846
|
+
on V: (u, v) ↦ sqrt(u**2 + v**2)/sqrt(u**2 + v**2 + 1)
|
|
847
|
+
|
|
848
|
+
::
|
|
849
|
+
|
|
850
|
+
sage: tan(f)
|
|
851
|
+
Scalar field tan(f) on the 2-dimensional topological manifold M
|
|
852
|
+
sage: tan(f).display()
|
|
853
|
+
tan(f): M → ℝ
|
|
854
|
+
on U: (x, y) ↦ tan(1/(x**2 + y**2 + 1))
|
|
855
|
+
on V: (u, v) ↦ tan((u**2 + v**2)/(u**2 + v**2 + 1))
|
|
856
|
+
|
|
857
|
+
.. RUBRIC:: Arithmetics of scalar fields with SymPy
|
|
858
|
+
|
|
859
|
+
Scalar fields on `M` (resp. `U`) belong to the algebra `C^0(M)`
|
|
860
|
+
(resp. `C^0(U)`)::
|
|
861
|
+
|
|
862
|
+
sage: f.parent()
|
|
863
|
+
Algebra of scalar fields on the 2-dimensional topological manifold M
|
|
864
|
+
sage: f.parent() is M.scalar_field_algebra()
|
|
865
|
+
True
|
|
866
|
+
sage: g.parent()
|
|
867
|
+
Algebra of scalar fields on the Open subset U of the 2-dimensional
|
|
868
|
+
topological manifold M
|
|
869
|
+
sage: g.parent() is U.scalar_field_algebra()
|
|
870
|
+
True
|
|
871
|
+
|
|
872
|
+
Consequently, scalar fields can be added::
|
|
873
|
+
|
|
874
|
+
sage: s = f + c ; s
|
|
875
|
+
Scalar field f+c on the 2-dimensional topological manifold M
|
|
876
|
+
sage: s.display()
|
|
877
|
+
f+c: M → ℝ
|
|
878
|
+
on U: (x, y) ↦ (a*x**2 + a*y**2 + a + 1)/(x**2 + y**2 + 1)
|
|
879
|
+
on V: (u, v) ↦ (a*u**2 + a*v**2 + a + u**2 + v**2)/(u**2 + v**2 + 1)
|
|
880
|
+
|
|
881
|
+
and subtracted::
|
|
882
|
+
|
|
883
|
+
sage: s = f - c ; s
|
|
884
|
+
Scalar field f-c on the 2-dimensional topological manifold M
|
|
885
|
+
sage: s.display()
|
|
886
|
+
f-c: M → ℝ
|
|
887
|
+
on U: (x, y) ↦ (-a*x**2 - a*y**2 - a + 1)/(x**2 + y**2 + 1)
|
|
888
|
+
on V: (u, v) ↦ (-a*u**2 - a*v**2 - a + u**2 + v**2)/(u**2 + v**2 + 1)
|
|
889
|
+
|
|
890
|
+
Some tests::
|
|
891
|
+
|
|
892
|
+
sage: f + zer == f
|
|
893
|
+
True
|
|
894
|
+
sage: f - f == zer
|
|
895
|
+
True
|
|
896
|
+
sage: f + (-f) == zer
|
|
897
|
+
True
|
|
898
|
+
sage: (f+c)-f == c
|
|
899
|
+
True
|
|
900
|
+
sage: (f-c)+c == f
|
|
901
|
+
True
|
|
902
|
+
|
|
903
|
+
We may add a number (interpreted as a constant scalar field) to a scalar
|
|
904
|
+
field::
|
|
905
|
+
|
|
906
|
+
sage: s = f + 1 ; s
|
|
907
|
+
Scalar field f+1 on the 2-dimensional topological manifold M
|
|
908
|
+
sage: s.display()
|
|
909
|
+
f+1: M → ℝ
|
|
910
|
+
on U: (x, y) ↦ (x**2 + y**2 + 2)/(x**2 + y**2 + 1)
|
|
911
|
+
on V: (u, v) ↦ (2*u**2 + 2*v**2 + 1)/(u**2 + v**2 + 1)
|
|
912
|
+
sage: (f+1)-1 == f
|
|
913
|
+
True
|
|
914
|
+
|
|
915
|
+
The number can represented by a symbolic variable::
|
|
916
|
+
|
|
917
|
+
sage: s = a + f ; s
|
|
918
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
919
|
+
sage: s == c + f
|
|
920
|
+
True
|
|
921
|
+
|
|
922
|
+
However if the symbolic variable is a chart coordinate, the addition
|
|
923
|
+
is performed only on the chart domain::
|
|
924
|
+
|
|
925
|
+
sage: s = f + x; s
|
|
926
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
927
|
+
sage: s.display()
|
|
928
|
+
M → ℝ
|
|
929
|
+
on U: (x, y) ↦ (x**3 + x*y**2 + x + 1)/(x**2 + y**2 + 1)
|
|
930
|
+
on W: (u, v) ↦ (u**4 + u**3 + 2*u**2*v**2 + u*v**2 + u + v**4)/(u**4 + 2*u**2*v**2 + u**2 + v**4 + v**2)
|
|
931
|
+
sage: s = f + u; s
|
|
932
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
933
|
+
sage: s.display()
|
|
934
|
+
M → ℝ
|
|
935
|
+
on W: (x, y) ↦ (x**3 + x**2 + x*y**2 + x + y**2)/(x**4 + 2*x**2*y**2 + x**2 + y**4 + y**2)
|
|
936
|
+
on V: (u, v) ↦ (u**3 + u**2 + u*v**2 + u + v**2)/(u**2 + v**2 + 1)
|
|
937
|
+
|
|
938
|
+
The addition of two scalar fields with different domains is possible if
|
|
939
|
+
the domain of one of them is a subset of the domain of the other; the
|
|
940
|
+
domain of the result is then this subset::
|
|
941
|
+
|
|
942
|
+
sage: f.domain()
|
|
943
|
+
2-dimensional topological manifold M
|
|
944
|
+
sage: g.domain()
|
|
945
|
+
Open subset U of the 2-dimensional topological manifold M
|
|
946
|
+
sage: s = f + g ; s
|
|
947
|
+
Scalar field f+g on the Open subset U of the 2-dimensional topological
|
|
948
|
+
manifold M
|
|
949
|
+
sage: s.domain()
|
|
950
|
+
Open subset U of the 2-dimensional topological manifold M
|
|
951
|
+
sage: s.display()
|
|
952
|
+
f+g: U → ℝ
|
|
953
|
+
(x, y) ↦ (x**3*y + x*y**3 + x*y + 1)/(x**2 + y**2 + 1)
|
|
954
|
+
on W: (u, v) ↦ (u**6 + 3*u**4*v**2 + u**3*v + 3*u**2*v**4 + u*v**3 + u*v + v**6)/(u**6 + 3*u**4*v**2 + u**4 + 3*u**2*v**4 + 2*u**2*v**2 + v**6 + v**4)
|
|
955
|
+
|
|
956
|
+
The operation actually performed is `f|_U + g`::
|
|
957
|
+
|
|
958
|
+
sage: s == f.restrict(U) + g
|
|
959
|
+
True
|
|
960
|
+
|
|
961
|
+
Since the algebra `C^0(M)` is a vector space over `\RR`, scalar fields
|
|
962
|
+
can be multiplied by a number, either an explicit one::
|
|
963
|
+
|
|
964
|
+
sage: s = 2*f ; s
|
|
965
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
966
|
+
sage: s.display()
|
|
967
|
+
M → ℝ
|
|
968
|
+
on U: (x, y) ↦ 2/(x**2 + y**2 + 1)
|
|
969
|
+
on V: (u, v) ↦ 2*(u**2 + v**2)/(u**2 + v**2 + 1)
|
|
970
|
+
|
|
971
|
+
or a symbolic one::
|
|
972
|
+
|
|
973
|
+
sage: s = a*f ; s
|
|
974
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
975
|
+
sage: s.display()
|
|
976
|
+
M → ℝ
|
|
977
|
+
on U: (x, y) ↦ a/(x**2 + y**2 + 1)
|
|
978
|
+
on V: (u, v) ↦ a*(u**2 + v**2)/(u**2 + v**2 + 1)
|
|
979
|
+
|
|
980
|
+
However, if the symbolic variable is a chart coordinate, the
|
|
981
|
+
multiplication is performed only in the corresponding chart::
|
|
982
|
+
|
|
983
|
+
sage: s = x*f; s
|
|
984
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
985
|
+
sage: s.display()
|
|
986
|
+
M → ℝ
|
|
987
|
+
on U: (x, y) ↦ x/(x**2 + y**2 + 1)
|
|
988
|
+
on W: (u, v) ↦ u/(u**2 + v**2 + 1)
|
|
989
|
+
sage: s = u*f; s
|
|
990
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
991
|
+
sage: s.display()
|
|
992
|
+
M → ℝ
|
|
993
|
+
on W: (x, y) ↦ x/(x**4 + 2*x**2*y**2 + x**2 + y**4 + y**2)
|
|
994
|
+
on V: (u, v) ↦ u*(u**2 + v**2)/(u**2 + v**2 + 1)
|
|
995
|
+
|
|
996
|
+
Some tests::
|
|
997
|
+
|
|
998
|
+
sage: 0*f == 0
|
|
999
|
+
True
|
|
1000
|
+
sage: 0*f == zer
|
|
1001
|
+
True
|
|
1002
|
+
sage: 1*f == f
|
|
1003
|
+
True
|
|
1004
|
+
sage: (-2)*f == - f - f
|
|
1005
|
+
True
|
|
1006
|
+
|
|
1007
|
+
The ring multiplication of the algebras `C^0(M)` and `C^0(U)`
|
|
1008
|
+
is the pointwise multiplication of functions::
|
|
1009
|
+
|
|
1010
|
+
sage: s = f*f ; s
|
|
1011
|
+
Scalar field f*f on the 2-dimensional topological manifold M
|
|
1012
|
+
sage: s.display()
|
|
1013
|
+
f*f: M → ℝ
|
|
1014
|
+
on U: (x, y) ↦ 1/(x**4 + 2*x**2*y**2 + 2*x**2 + y**4 + 2*y**2 + 1)
|
|
1015
|
+
on V: (u, v) ↦ (u**4 + 2*u**2*v**2 + v**4)/(u**4 + 2*u**2*v**2 + 2*u**2 + v**4 + 2*v**2 + 1)
|
|
1016
|
+
|
|
1017
|
+
sage: s = g*h ; s
|
|
1018
|
+
Scalar field g*h on the Open subset U of the 2-dimensional topological
|
|
1019
|
+
manifold M
|
|
1020
|
+
sage: s.display()
|
|
1021
|
+
g*h: U → ℝ
|
|
1022
|
+
(x, y) ↦ x*y*H(x, y)
|
|
1023
|
+
on W: (u, v) ↦ u*v*H(u/(u**2 + v**2), v/(u**2 + v**2))/(u**4 + 2*u**2*v**2 + v**4)
|
|
1024
|
+
|
|
1025
|
+
Thanks to the coercion `C^0(M) \to C^0(U)` mentioned above,
|
|
1026
|
+
it is possible to multiply a scalar field defined on `M` by a
|
|
1027
|
+
scalar field defined on `U`, the result being a scalar field
|
|
1028
|
+
defined on `U`::
|
|
1029
|
+
|
|
1030
|
+
sage: f.domain(), g.domain()
|
|
1031
|
+
(2-dimensional topological manifold M,
|
|
1032
|
+
Open subset U of the 2-dimensional topological manifold M)
|
|
1033
|
+
sage: s = f*g ; s
|
|
1034
|
+
Scalar field f*g on the Open subset U of the 2-dimensional topological
|
|
1035
|
+
manifold M
|
|
1036
|
+
sage: s.display()
|
|
1037
|
+
f*g: U → ℝ
|
|
1038
|
+
(x, y) ↦ x*y/(x**2 + y**2 + 1)
|
|
1039
|
+
on W: (u, v) ↦ u*v/(u**4 + 2*u**2*v**2 + u**2 + v**4 + v**2)
|
|
1040
|
+
|
|
1041
|
+
sage: s == f.restrict(U)*g
|
|
1042
|
+
True
|
|
1043
|
+
|
|
1044
|
+
Scalar fields can be divided (pointwise division)::
|
|
1045
|
+
|
|
1046
|
+
sage: s = f/c ; s
|
|
1047
|
+
Scalar field f/c on the 2-dimensional topological manifold M
|
|
1048
|
+
sage: s.display()
|
|
1049
|
+
f/c: M → ℝ
|
|
1050
|
+
on U: (x, y) ↦ 1/(a*(x**2 + y**2 + 1))
|
|
1051
|
+
on V: (u, v) ↦ (u**2 + v**2)/(a*(u**2 + v**2 + 1))
|
|
1052
|
+
sage: s = g/h ; s
|
|
1053
|
+
Scalar field g/h on the Open subset U of the 2-dimensional topological
|
|
1054
|
+
manifold M
|
|
1055
|
+
sage: s.display()
|
|
1056
|
+
g/h: U → ℝ
|
|
1057
|
+
(x, y) ↦ x*y/H(x, y)
|
|
1058
|
+
on W: (u, v) ↦ u*v/((u**4 + 2*u**2*v**2 + v**4)*H(u/(u**2 + v**2), v/(u**2 + v**2)))
|
|
1059
|
+
|
|
1060
|
+
sage: s = f/g ; s
|
|
1061
|
+
Scalar field f/g on the Open subset U of the 2-dimensional topological
|
|
1062
|
+
manifold M
|
|
1063
|
+
sage: s.display()
|
|
1064
|
+
f/g: U → ℝ
|
|
1065
|
+
(x, y) ↦ 1/(x*y*(x**2 + y**2 + 1))
|
|
1066
|
+
on W: (u, v) ↦ (u**6 + 3*u**4*v**2 + 3*u**2*v**4 + v**6)/(u*v*(u**2 + v**2 + 1))
|
|
1067
|
+
sage: s == f.restrict(U)/g
|
|
1068
|
+
True
|
|
1069
|
+
|
|
1070
|
+
For scalar fields defined on a single chart domain, we may perform some
|
|
1071
|
+
arithmetics with symbolic expressions involving the chart coordinates::
|
|
1072
|
+
|
|
1073
|
+
sage: s = g + x^2 - y ; s
|
|
1074
|
+
Scalar field on the Open subset U of the 2-dimensional topological manifold M
|
|
1075
|
+
sage: s.display()
|
|
1076
|
+
U → ℝ
|
|
1077
|
+
(x, y) ↦ x**2 + x*y - y
|
|
1078
|
+
on W: (u, v) ↦ (-u**2*v + u**2 + u*v - v**3)/(u**4 + 2*u**2*v**2 + v**4)
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
::
|
|
1082
|
+
|
|
1083
|
+
sage: s = g*x ; s
|
|
1084
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
1085
|
+
manifold M
|
|
1086
|
+
sage: s.display()
|
|
1087
|
+
U → ℝ
|
|
1088
|
+
(x, y) ↦ x**2*y
|
|
1089
|
+
on W: (u, v) ↦ u**2*v/(u**6 + 3*u**4*v**2 + 3*u**2*v**4 + v**6)
|
|
1090
|
+
|
|
1091
|
+
::
|
|
1092
|
+
|
|
1093
|
+
sage: s = g/x ; s
|
|
1094
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
1095
|
+
manifold M
|
|
1096
|
+
sage: s.display()
|
|
1097
|
+
U → ℝ
|
|
1098
|
+
(x, y) ↦ y
|
|
1099
|
+
on W: (u, v) ↦ v/(u**2 + v**2)
|
|
1100
|
+
sage: s = x/g ; s
|
|
1101
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
1102
|
+
manifold M
|
|
1103
|
+
sage: s.display()
|
|
1104
|
+
U → ℝ
|
|
1105
|
+
(x, y) ↦ 1/y
|
|
1106
|
+
on W: (u, v) ↦ u**2/v + v
|
|
1107
|
+
|
|
1108
|
+
The test suite is passed::
|
|
1109
|
+
|
|
1110
|
+
sage: TestSuite(f).run()
|
|
1111
|
+
sage: TestSuite(zer).run()
|
|
1112
|
+
"""
|
|
1113
|
+
|
|
1114
|
+
_name: Optional[str]
|
|
1115
|
+
|
|
1116
|
+
def __init__(self, parent, coord_expression=None, chart=None, name=None,
|
|
1117
|
+
latex_name=None):
|
|
1118
|
+
r"""
|
|
1119
|
+
Construct a scalar field.
|
|
1120
|
+
|
|
1121
|
+
TESTS::
|
|
1122
|
+
|
|
1123
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1124
|
+
sage: X.<x,y> = M.chart()
|
|
1125
|
+
sage: f = M.scalar_field({X: x+y}, name='f') ; f
|
|
1126
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
1127
|
+
sage: from sage.manifolds.scalarfield import ScalarField
|
|
1128
|
+
sage: isinstance(f, ScalarField)
|
|
1129
|
+
True
|
|
1130
|
+
sage: f.parent()
|
|
1131
|
+
Algebra of scalar fields on the 2-dimensional topological
|
|
1132
|
+
manifold M
|
|
1133
|
+
sage: TestSuite(f).run()
|
|
1134
|
+
"""
|
|
1135
|
+
super().__init__(parent) # both super classes have same signature
|
|
1136
|
+
domain = parent._domain
|
|
1137
|
+
self._domain = domain
|
|
1138
|
+
self._manifold = domain.manifold()
|
|
1139
|
+
self._is_zero = False
|
|
1140
|
+
# a priori, may be changed below or via
|
|
1141
|
+
# method __bool__()
|
|
1142
|
+
|
|
1143
|
+
self._name = name
|
|
1144
|
+
if latex_name is None:
|
|
1145
|
+
self._latex_name = self._name
|
|
1146
|
+
else:
|
|
1147
|
+
self._latex_name = latex_name
|
|
1148
|
+
self._express = {}
|
|
1149
|
+
# dict of coordinate expressions (ChartFunction
|
|
1150
|
+
# instances) with charts as keys
|
|
1151
|
+
|
|
1152
|
+
if coord_expression is not None:
|
|
1153
|
+
if isinstance(coord_expression, dict):
|
|
1154
|
+
for chart, expression in coord_expression.items():
|
|
1155
|
+
if isinstance(expression, ChartFunction):
|
|
1156
|
+
self._express[chart] = expression
|
|
1157
|
+
else:
|
|
1158
|
+
self._express[chart] = chart.function(expression)
|
|
1159
|
+
elif isinstance(coord_expression, ChartFunction):
|
|
1160
|
+
self._express[coord_expression.chart()] = coord_expression
|
|
1161
|
+
else:
|
|
1162
|
+
if chart is None:
|
|
1163
|
+
chart = self._domain.default_chart()
|
|
1164
|
+
if chart == 'all':
|
|
1165
|
+
# coord_expression is the same in all charts (constant
|
|
1166
|
+
# scalar field)
|
|
1167
|
+
for ch in self._domain.atlas():
|
|
1168
|
+
self._express[ch] = ch.function(coord_expression)
|
|
1169
|
+
else:
|
|
1170
|
+
self._express[chart] = chart.function(coord_expression)
|
|
1171
|
+
self._init_derived() # initialization of derived quantities
|
|
1172
|
+
|
|
1173
|
+
# ### Required methods for an algebra element (beside arithmetic) ###
|
|
1174
|
+
|
|
1175
|
+
def __bool__(self):
|
|
1176
|
+
r"""
|
|
1177
|
+
Return ``True`` if ``self`` is nonzero and ``False`` otherwise.
|
|
1178
|
+
|
|
1179
|
+
This method is called by :meth:`~sage.structure.element.Element.is_zero()`.
|
|
1180
|
+
|
|
1181
|
+
EXAMPLES:
|
|
1182
|
+
|
|
1183
|
+
Tests on a 2-dimensional manifold::
|
|
1184
|
+
|
|
1185
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1186
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1187
|
+
sage: f = M.scalar_field(x*y)
|
|
1188
|
+
sage: f.is_zero()
|
|
1189
|
+
False
|
|
1190
|
+
sage: f.set_expr(0)
|
|
1191
|
+
sage: f.is_zero()
|
|
1192
|
+
True
|
|
1193
|
+
sage: g = M.scalar_field(0)
|
|
1194
|
+
sage: g.is_zero()
|
|
1195
|
+
True
|
|
1196
|
+
sage: M.zero_scalar_field().is_zero()
|
|
1197
|
+
True
|
|
1198
|
+
"""
|
|
1199
|
+
if self._is_zero:
|
|
1200
|
+
return False
|
|
1201
|
+
if not self._express:
|
|
1202
|
+
# undefined scalar field
|
|
1203
|
+
return True
|
|
1204
|
+
for funct in self._express.values():
|
|
1205
|
+
if not funct.is_zero():
|
|
1206
|
+
self._is_zero = False
|
|
1207
|
+
return True
|
|
1208
|
+
self._is_zero = True
|
|
1209
|
+
return False
|
|
1210
|
+
|
|
1211
|
+
def is_trivial_zero(self):
|
|
1212
|
+
r"""
|
|
1213
|
+
Check if ``self`` is trivially equal to zero without any
|
|
1214
|
+
simplification.
|
|
1215
|
+
|
|
1216
|
+
This method is supposed to be fast as compared with
|
|
1217
|
+
``self.is_zero()`` or ``self == 0`` and is intended to be
|
|
1218
|
+
used in library code where trying to obtain a mathematically
|
|
1219
|
+
correct result by applying potentially expensive rewrite rules
|
|
1220
|
+
is not desirable.
|
|
1221
|
+
|
|
1222
|
+
EXAMPLES::
|
|
1223
|
+
|
|
1224
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1225
|
+
sage: X.<x,y> = M.chart()
|
|
1226
|
+
sage: f = M.scalar_field({X: 0})
|
|
1227
|
+
sage: f.is_trivial_zero()
|
|
1228
|
+
True
|
|
1229
|
+
sage: f = M.scalar_field(0)
|
|
1230
|
+
sage: f.is_trivial_zero()
|
|
1231
|
+
True
|
|
1232
|
+
sage: M.zero_scalar_field().is_trivial_zero()
|
|
1233
|
+
True
|
|
1234
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1235
|
+
sage: f.is_trivial_zero()
|
|
1236
|
+
False
|
|
1237
|
+
|
|
1238
|
+
Scalar field defined by means of two charts::
|
|
1239
|
+
|
|
1240
|
+
sage: U1 = M.open_subset('U1'); X1.<x1,y1> = U1.chart()
|
|
1241
|
+
sage: U2 = M.open_subset('U2'); X2.<x2,y2> = U2.chart()
|
|
1242
|
+
sage: f = M.scalar_field({X1: 0, X2: 0})
|
|
1243
|
+
sage: f.is_trivial_zero()
|
|
1244
|
+
True
|
|
1245
|
+
sage: f = M.scalar_field({X1: 0, X2: 1})
|
|
1246
|
+
sage: f.is_trivial_zero()
|
|
1247
|
+
False
|
|
1248
|
+
|
|
1249
|
+
No simplification is attempted, so that ``False`` is returned for
|
|
1250
|
+
non-trivial cases::
|
|
1251
|
+
|
|
1252
|
+
sage: f = M.scalar_field({X: cos(x)^2 + sin(x)^2 - 1})
|
|
1253
|
+
sage: f.is_trivial_zero()
|
|
1254
|
+
False
|
|
1255
|
+
|
|
1256
|
+
On the contrary, the method
|
|
1257
|
+
:meth:`~sage.structure.element.Element.is_zero` and the direct
|
|
1258
|
+
comparison to zero involve some simplification algorithms and
|
|
1259
|
+
return ``True``::
|
|
1260
|
+
|
|
1261
|
+
sage: f.is_zero()
|
|
1262
|
+
True
|
|
1263
|
+
sage: f == 0
|
|
1264
|
+
True
|
|
1265
|
+
"""
|
|
1266
|
+
if self._is_zero:
|
|
1267
|
+
return True
|
|
1268
|
+
return all(func.is_trivial_zero() for func in self._express.values())
|
|
1269
|
+
|
|
1270
|
+
def is_trivial_one(self):
|
|
1271
|
+
r"""
|
|
1272
|
+
Check if ``self`` is trivially equal to one without any
|
|
1273
|
+
simplification.
|
|
1274
|
+
|
|
1275
|
+
This method is supposed to be fast as compared with
|
|
1276
|
+
``self == 1`` and is intended to be used in library code where
|
|
1277
|
+
trying to obtain a mathematically correct result by applying
|
|
1278
|
+
potentially expensive rewrite rules is not desirable.
|
|
1279
|
+
|
|
1280
|
+
EXAMPLES::
|
|
1281
|
+
|
|
1282
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1283
|
+
sage: X.<x,y> = M.chart()
|
|
1284
|
+
sage: f = M.scalar_field({X: 1})
|
|
1285
|
+
sage: f.is_trivial_one()
|
|
1286
|
+
True
|
|
1287
|
+
sage: f = M.scalar_field(1)
|
|
1288
|
+
sage: f.is_trivial_one()
|
|
1289
|
+
True
|
|
1290
|
+
sage: M.one_scalar_field().is_trivial_one()
|
|
1291
|
+
True
|
|
1292
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1293
|
+
sage: f.is_trivial_one()
|
|
1294
|
+
False
|
|
1295
|
+
|
|
1296
|
+
Scalar field defined by means of two charts::
|
|
1297
|
+
|
|
1298
|
+
sage: U1 = M.open_subset('U1'); X1.<x1,y1> = U1.chart()
|
|
1299
|
+
sage: U2 = M.open_subset('U2'); X2.<x2,y2> = U2.chart()
|
|
1300
|
+
sage: f = M.scalar_field({X1: 1, X2: 1})
|
|
1301
|
+
sage: f.is_trivial_one()
|
|
1302
|
+
True
|
|
1303
|
+
sage: f = M.scalar_field({X1: 0, X2: 1})
|
|
1304
|
+
sage: f.is_trivial_one()
|
|
1305
|
+
False
|
|
1306
|
+
|
|
1307
|
+
No simplification is attempted, so that ``False`` is returned for
|
|
1308
|
+
non-trivial cases::
|
|
1309
|
+
|
|
1310
|
+
sage: f = M.scalar_field({X: cos(x)^2 + sin(x)^2})
|
|
1311
|
+
sage: f.is_trivial_one()
|
|
1312
|
+
False
|
|
1313
|
+
|
|
1314
|
+
On the contrary, the method
|
|
1315
|
+
:meth:`~sage.structure.element.Element.is_zero` and the direct
|
|
1316
|
+
comparison to one involve some simplification algorithms and
|
|
1317
|
+
return ``True``::
|
|
1318
|
+
|
|
1319
|
+
sage: (f - 1).is_zero()
|
|
1320
|
+
True
|
|
1321
|
+
sage: f == 1
|
|
1322
|
+
True
|
|
1323
|
+
"""
|
|
1324
|
+
return all(func.is_trivial_one() for func in self._express.values())
|
|
1325
|
+
|
|
1326
|
+
# TODO: Remove this method as soon as issue #28629 is solved?
|
|
1327
|
+
def is_unit(self):
|
|
1328
|
+
r"""
|
|
1329
|
+
Return ``True`` iff ``self`` is not trivially zero in at least one of
|
|
1330
|
+
the given expressions since most scalar fields are invertible and a
|
|
1331
|
+
complete computation would take too much time.
|
|
1332
|
+
|
|
1333
|
+
EXAMPLES::
|
|
1334
|
+
|
|
1335
|
+
sage: M = Manifold(2, 'M', structure='top')
|
|
1336
|
+
sage: one = M.scalar_field_algebra().one()
|
|
1337
|
+
sage: one.is_unit()
|
|
1338
|
+
True
|
|
1339
|
+
sage: zero = M.scalar_field_algebra().zero()
|
|
1340
|
+
sage: zero.is_unit()
|
|
1341
|
+
False
|
|
1342
|
+
"""
|
|
1343
|
+
if self._is_zero:
|
|
1344
|
+
return False
|
|
1345
|
+
return not any(func.is_trivial_zero()
|
|
1346
|
+
for func in self._express.values())
|
|
1347
|
+
|
|
1348
|
+
def __eq__(self, other):
|
|
1349
|
+
r"""
|
|
1350
|
+
Comparison (equality) operator.
|
|
1351
|
+
|
|
1352
|
+
INPUT:
|
|
1353
|
+
|
|
1354
|
+
- ``other`` -- a scalar field (or something else)
|
|
1355
|
+
|
|
1356
|
+
OUTPUT: ``True`` if ``self`` is equal to ``other``, ``False`` otherwise
|
|
1357
|
+
|
|
1358
|
+
TESTS::
|
|
1359
|
+
|
|
1360
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1361
|
+
sage: X.<x,y> = M.chart()
|
|
1362
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1363
|
+
sage: f == 1
|
|
1364
|
+
False
|
|
1365
|
+
sage: f == M.zero_scalar_field()
|
|
1366
|
+
False
|
|
1367
|
+
sage: g = M.scalar_field({X: x+y})
|
|
1368
|
+
sage: f == g
|
|
1369
|
+
True
|
|
1370
|
+
sage: h = M.scalar_field({X: 1})
|
|
1371
|
+
sage: h == M.one_scalar_field()
|
|
1372
|
+
True
|
|
1373
|
+
sage: h == 1
|
|
1374
|
+
True
|
|
1375
|
+
"""
|
|
1376
|
+
from sage.manifolds.differentiable.mixed_form import MixedForm
|
|
1377
|
+
|
|
1378
|
+
if other is self:
|
|
1379
|
+
return True
|
|
1380
|
+
if isinstance(other, MixedForm):
|
|
1381
|
+
# use comparison of MixedForm:
|
|
1382
|
+
return other == self
|
|
1383
|
+
if not isinstance(other, ScalarField):
|
|
1384
|
+
# We try a conversion of other to a scalar field, except if
|
|
1385
|
+
# other is None (since this would generate an undefined scalar
|
|
1386
|
+
# field)
|
|
1387
|
+
if other is None:
|
|
1388
|
+
return False
|
|
1389
|
+
try:
|
|
1390
|
+
other = self.parent()(other) # conversion to a scalar field
|
|
1391
|
+
except Exception:
|
|
1392
|
+
return False
|
|
1393
|
+
if other._domain != self._domain:
|
|
1394
|
+
return False
|
|
1395
|
+
if other.is_zero():
|
|
1396
|
+
return self.is_zero()
|
|
1397
|
+
com_charts = self.common_charts(other)
|
|
1398
|
+
if com_charts is None:
|
|
1399
|
+
raise ValueError("no common chart for the comparison")
|
|
1400
|
+
for chart in com_charts:
|
|
1401
|
+
if not (self._express[chart] == other._express[chart]):
|
|
1402
|
+
return False
|
|
1403
|
+
return True
|
|
1404
|
+
|
|
1405
|
+
def __ne__(self, other):
|
|
1406
|
+
r"""
|
|
1407
|
+
Non-equality operator.
|
|
1408
|
+
|
|
1409
|
+
INPUT:
|
|
1410
|
+
|
|
1411
|
+
- ``other`` -- a scalar field
|
|
1412
|
+
|
|
1413
|
+
OUTPUT: ``True`` if ``self`` differs from ``other``, ``False`` otherwise
|
|
1414
|
+
|
|
1415
|
+
TESTS::
|
|
1416
|
+
|
|
1417
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1418
|
+
sage: X.<x,y> = M.chart()
|
|
1419
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1420
|
+
sage: f != 1
|
|
1421
|
+
True
|
|
1422
|
+
sage: f != M.zero_scalar_field()
|
|
1423
|
+
True
|
|
1424
|
+
sage: g = M.scalar_field({X: x+y})
|
|
1425
|
+
sage: f != g
|
|
1426
|
+
False
|
|
1427
|
+
"""
|
|
1428
|
+
return not (self == other)
|
|
1429
|
+
|
|
1430
|
+
# ## End of required methods for an algebra element (beside arithmetic) ##
|
|
1431
|
+
|
|
1432
|
+
def _init_derived(self):
|
|
1433
|
+
r"""
|
|
1434
|
+
Initialize the derived quantities.
|
|
1435
|
+
|
|
1436
|
+
TESTS::
|
|
1437
|
+
|
|
1438
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1439
|
+
sage: X.<x,y> = M.chart()
|
|
1440
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1441
|
+
sage: f._init_derived()
|
|
1442
|
+
"""
|
|
1443
|
+
self._restrictions = {}
|
|
1444
|
+
# dict. of restrictions of ``self`` on subsets
|
|
1445
|
+
# of self._domain, with the subsets as keys
|
|
1446
|
+
|
|
1447
|
+
def _del_derived(self):
|
|
1448
|
+
r"""
|
|
1449
|
+
Delete the derived quantities.
|
|
1450
|
+
|
|
1451
|
+
TESTS::
|
|
1452
|
+
|
|
1453
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1454
|
+
sage: X.<x,y> = M.chart()
|
|
1455
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1456
|
+
sage: U = M.open_subset('U', coord_def={X: x>0})
|
|
1457
|
+
sage: f.restrict(U)
|
|
1458
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
1459
|
+
manifold M
|
|
1460
|
+
sage: f._restrictions
|
|
1461
|
+
{Open subset U of the 2-dimensional topological manifold M:
|
|
1462
|
+
Scalar field on the Open subset U of the 2-dimensional topological
|
|
1463
|
+
manifold M}
|
|
1464
|
+
sage: f._del_derived()
|
|
1465
|
+
sage: f._restrictions # restrictions are derived quantities
|
|
1466
|
+
{}
|
|
1467
|
+
"""
|
|
1468
|
+
self._restrictions.clear()
|
|
1469
|
+
|
|
1470
|
+
def _repr_(self):
|
|
1471
|
+
r"""
|
|
1472
|
+
String representation of the object.
|
|
1473
|
+
|
|
1474
|
+
TESTS::
|
|
1475
|
+
|
|
1476
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1477
|
+
sage: X.<x,y> = M.chart()
|
|
1478
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1479
|
+
sage: f._repr_()
|
|
1480
|
+
'Scalar field on the 2-dimensional topological manifold M'
|
|
1481
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
1482
|
+
sage: f._repr_()
|
|
1483
|
+
'Scalar field f on the 2-dimensional topological manifold M'
|
|
1484
|
+
sage: f
|
|
1485
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
1486
|
+
"""
|
|
1487
|
+
description = "Scalar field"
|
|
1488
|
+
if self._name is not None:
|
|
1489
|
+
description += " " + self._name
|
|
1490
|
+
description += " on the {}".format(self._domain)
|
|
1491
|
+
return description
|
|
1492
|
+
|
|
1493
|
+
def _latex_(self):
|
|
1494
|
+
r"""
|
|
1495
|
+
LaTeX representation of the object.
|
|
1496
|
+
|
|
1497
|
+
TESTS::
|
|
1498
|
+
|
|
1499
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1500
|
+
sage: X.<x,y> = M.chart()
|
|
1501
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1502
|
+
sage: f._latex_()
|
|
1503
|
+
'\\text{Scalar field on the 2-dimensional topological manifold M}'
|
|
1504
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
1505
|
+
sage: f._latex_()
|
|
1506
|
+
'f'
|
|
1507
|
+
sage: f = M.scalar_field({X: x+y}, name='f', latex_name=r'\Phi')
|
|
1508
|
+
sage: f._latex_()
|
|
1509
|
+
'\\Phi'
|
|
1510
|
+
sage: latex(f)
|
|
1511
|
+
\Phi
|
|
1512
|
+
"""
|
|
1513
|
+
if self._latex_name is None:
|
|
1514
|
+
return r'\text{' + str(self) + r'}'
|
|
1515
|
+
return self._latex_name
|
|
1516
|
+
|
|
1517
|
+
def set_name(self, name=None, latex_name=None):
|
|
1518
|
+
r"""
|
|
1519
|
+
Set (or change) the text name and LaTeX name of the scalar field.
|
|
1520
|
+
|
|
1521
|
+
INPUT:
|
|
1522
|
+
|
|
1523
|
+
- ``name`` -- (string; default: ``None``) name given to the scalar
|
|
1524
|
+
field
|
|
1525
|
+
- ``latex_name`` -- (string; default: ``None``) LaTeX symbol to denote
|
|
1526
|
+
the scalar field; if ``None`` while ``name`` is provided, the LaTeX
|
|
1527
|
+
symbol is set to ``name``
|
|
1528
|
+
|
|
1529
|
+
EXAMPLES::
|
|
1530
|
+
|
|
1531
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1532
|
+
sage: X.<x,y> = M.chart()
|
|
1533
|
+
sage: f = M.scalar_field({X: x+y})
|
|
1534
|
+
sage: f = M.scalar_field({X: x+y}); f
|
|
1535
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
1536
|
+
sage: f.set_name('f'); f
|
|
1537
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
1538
|
+
sage: latex(f)
|
|
1539
|
+
f
|
|
1540
|
+
sage: f.set_name('f', latex_name=r'\Phi'); f
|
|
1541
|
+
Scalar field f on the 2-dimensional topological manifold M
|
|
1542
|
+
sage: latex(f)
|
|
1543
|
+
\Phi
|
|
1544
|
+
"""
|
|
1545
|
+
if self.is_immutable():
|
|
1546
|
+
raise ValueError("the name of an immutable element "
|
|
1547
|
+
"cannot be changed")
|
|
1548
|
+
if name is not None:
|
|
1549
|
+
self._name = name
|
|
1550
|
+
if latex_name is None:
|
|
1551
|
+
self._latex_name = self._name
|
|
1552
|
+
if latex_name is not None:
|
|
1553
|
+
self._latex_name = latex_name
|
|
1554
|
+
for rst in self._restrictions.values():
|
|
1555
|
+
rst.set_name(name=name, latex_name=latex_name)
|
|
1556
|
+
|
|
1557
|
+
def domain(self):
|
|
1558
|
+
r"""
|
|
1559
|
+
Return the open subset on which the scalar field is defined.
|
|
1560
|
+
|
|
1561
|
+
OUTPUT:
|
|
1562
|
+
|
|
1563
|
+
- instance of class
|
|
1564
|
+
:class:`~sage.manifolds.manifold.TopologicalManifold`
|
|
1565
|
+
representing the manifold's open subset on which the
|
|
1566
|
+
scalar field is defined
|
|
1567
|
+
|
|
1568
|
+
EXAMPLES::
|
|
1569
|
+
|
|
1570
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1571
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1572
|
+
sage: f = M.scalar_field(x+2*y)
|
|
1573
|
+
sage: f.domain()
|
|
1574
|
+
2-dimensional topological manifold M
|
|
1575
|
+
sage: U = M.open_subset('U', coord_def={c_xy: x<0})
|
|
1576
|
+
sage: g = f.restrict(U)
|
|
1577
|
+
sage: g.domain()
|
|
1578
|
+
Open subset U of the 2-dimensional topological manifold M
|
|
1579
|
+
"""
|
|
1580
|
+
return self._domain
|
|
1581
|
+
|
|
1582
|
+
def codomain(self):
|
|
1583
|
+
r"""
|
|
1584
|
+
Return the codomain of the scalar field.
|
|
1585
|
+
|
|
1586
|
+
EXAMPLES::
|
|
1587
|
+
|
|
1588
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1589
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1590
|
+
sage: f = M.scalar_field(x+2*y)
|
|
1591
|
+
sage: f.codomain()
|
|
1592
|
+
Real Field with 53 bits of precision
|
|
1593
|
+
"""
|
|
1594
|
+
return self._domain.base_field()
|
|
1595
|
+
|
|
1596
|
+
def copy(self, name=None, latex_name=None):
|
|
1597
|
+
r"""
|
|
1598
|
+
Return an exact copy of the scalar field.
|
|
1599
|
+
|
|
1600
|
+
INPUT:
|
|
1601
|
+
|
|
1602
|
+
- ``name`` -- (default: ``None``) name given to the copy
|
|
1603
|
+
- ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
|
|
1604
|
+
copy; if none is provided, the LaTeX symbol is set to ``name``
|
|
1605
|
+
|
|
1606
|
+
EXAMPLES:
|
|
1607
|
+
|
|
1608
|
+
Copy on a 2-dimensional manifold::
|
|
1609
|
+
|
|
1610
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1611
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1612
|
+
sage: f = M.scalar_field(x*y^2)
|
|
1613
|
+
sage: g = f.copy()
|
|
1614
|
+
sage: type(g)
|
|
1615
|
+
<class 'sage.manifolds.scalarfield_algebra.ScalarFieldAlgebra_with_category.element_class'>
|
|
1616
|
+
sage: g.expr()
|
|
1617
|
+
x*y^2
|
|
1618
|
+
sage: g == f
|
|
1619
|
+
True
|
|
1620
|
+
sage: g is f
|
|
1621
|
+
False
|
|
1622
|
+
"""
|
|
1623
|
+
result = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
1624
|
+
for chart, funct in self._express.items():
|
|
1625
|
+
result._express[chart] = funct.copy()
|
|
1626
|
+
result._is_zero = self._is_zero
|
|
1627
|
+
return result
|
|
1628
|
+
|
|
1629
|
+
def copy_from(self, other):
|
|
1630
|
+
r"""
|
|
1631
|
+
Make ``self`` a copy of ``other``.
|
|
1632
|
+
|
|
1633
|
+
INPUT:
|
|
1634
|
+
|
|
1635
|
+
- ``other`` -- other scalar field, in the same module as ``self``
|
|
1636
|
+
|
|
1637
|
+
.. NOTE::
|
|
1638
|
+
|
|
1639
|
+
While the derived quantities are not copied, the name is kept.
|
|
1640
|
+
|
|
1641
|
+
.. WARNING::
|
|
1642
|
+
|
|
1643
|
+
All previous defined expressions and restrictions will be deleted!
|
|
1644
|
+
|
|
1645
|
+
EXAMPLES::
|
|
1646
|
+
|
|
1647
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1648
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1649
|
+
sage: f = M.scalar_field(x*y^2, name='f')
|
|
1650
|
+
sage: f.display()
|
|
1651
|
+
f: M → ℝ
|
|
1652
|
+
(x, y) ↦ x*y^2
|
|
1653
|
+
sage: g = M.scalar_field(name='g')
|
|
1654
|
+
sage: g.copy_from(f)
|
|
1655
|
+
sage: g.display()
|
|
1656
|
+
g: M → ℝ
|
|
1657
|
+
(x, y) ↦ x*y^2
|
|
1658
|
+
sage: f == g
|
|
1659
|
+
True
|
|
1660
|
+
|
|
1661
|
+
While the original scalar field is modified, the copy is not::
|
|
1662
|
+
|
|
1663
|
+
sage: f.set_expr(x-y)
|
|
1664
|
+
sage: f.display()
|
|
1665
|
+
f: M → ℝ
|
|
1666
|
+
(x, y) ↦ x - y
|
|
1667
|
+
sage: g.display()
|
|
1668
|
+
g: M → ℝ
|
|
1669
|
+
(x, y) ↦ x*y^2
|
|
1670
|
+
sage: f == g
|
|
1671
|
+
False
|
|
1672
|
+
"""
|
|
1673
|
+
if self.is_immutable():
|
|
1674
|
+
raise ValueError("the expressions of an immutable element "
|
|
1675
|
+
"cannot be changed")
|
|
1676
|
+
if other not in self.parent():
|
|
1677
|
+
raise TypeError("the original must be an element of "
|
|
1678
|
+
f"{self.parent()}")
|
|
1679
|
+
self._del_derived()
|
|
1680
|
+
for chart, funct in other._express.items():
|
|
1681
|
+
self._express[chart] = funct.copy()
|
|
1682
|
+
self._is_zero = other._is_zero
|
|
1683
|
+
|
|
1684
|
+
def coord_function(self, chart=None, from_chart=None):
|
|
1685
|
+
r"""
|
|
1686
|
+
Return the function of the coordinates representing the scalar field
|
|
1687
|
+
in a given chart.
|
|
1688
|
+
|
|
1689
|
+
INPUT:
|
|
1690
|
+
|
|
1691
|
+
- ``chart`` -- (default: ``None``) chart with respect to which the
|
|
1692
|
+
coordinate expression is to be returned; if ``None``, the
|
|
1693
|
+
default chart of the scalar field's domain will be used
|
|
1694
|
+
- ``from_chart`` -- (default: ``None``) chart from which the
|
|
1695
|
+
required expression is computed if it is not known already in the
|
|
1696
|
+
chart ``chart``; if ``None``, a chart is picked in the known
|
|
1697
|
+
expressions
|
|
1698
|
+
|
|
1699
|
+
OUTPUT:
|
|
1700
|
+
|
|
1701
|
+
- instance of :class:`~sage.manifolds.chart_func.ChartFunction`
|
|
1702
|
+
representing the coordinate function of the scalar field in the
|
|
1703
|
+
given chart
|
|
1704
|
+
|
|
1705
|
+
EXAMPLES:
|
|
1706
|
+
|
|
1707
|
+
Coordinate function on a 2-dimensional manifold::
|
|
1708
|
+
|
|
1709
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1710
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1711
|
+
sage: f = M.scalar_field(x*y^2)
|
|
1712
|
+
sage: f.coord_function()
|
|
1713
|
+
x*y^2
|
|
1714
|
+
sage: f.coord_function(c_xy) # equivalent form (since c_xy is the default chart)
|
|
1715
|
+
x*y^2
|
|
1716
|
+
sage: type(f.coord_function())
|
|
1717
|
+
<class 'sage.manifolds.chart_func.ChartFunctionRing_with_category.element_class'>
|
|
1718
|
+
|
|
1719
|
+
Expression via a change of coordinates::
|
|
1720
|
+
|
|
1721
|
+
sage: c_uv.<u,v> = M.chart()
|
|
1722
|
+
sage: c_uv.transition_map(c_xy, [u+v, u-v])
|
|
1723
|
+
Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y))
|
|
1724
|
+
sage: f._express # at this stage, f is expressed only in terms of (x,y) coordinates
|
|
1725
|
+
{Chart (M, (x, y)): x*y^2}
|
|
1726
|
+
sage: f.coord_function(c_uv) # forces the computation of the expression of f in terms of (u,v) coordinates
|
|
1727
|
+
u^3 - u^2*v - u*v^2 + v^3
|
|
1728
|
+
sage: f.coord_function(c_uv) == (u+v)*(u-v)^2 # check
|
|
1729
|
+
True
|
|
1730
|
+
sage: f._express # random (dict. output); f has now 2 coordinate expressions:
|
|
1731
|
+
{Chart (M, (x, y)): x*y^2, Chart (M, (u, v)): u^3 - u^2*v - u*v^2 + v^3}
|
|
1732
|
+
|
|
1733
|
+
Usage in a physical context (simple Lorentz transformation - boost in
|
|
1734
|
+
``x`` direction, with relative velocity ``v`` between ``o1``
|
|
1735
|
+
and ``o2`` frames)::
|
|
1736
|
+
|
|
1737
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1738
|
+
sage: o1.<t,x> = M.chart()
|
|
1739
|
+
sage: o2.<T,X> = M.chart()
|
|
1740
|
+
sage: f = M.scalar_field(x^2 - t^2)
|
|
1741
|
+
sage: f.coord_function(o1)
|
|
1742
|
+
-t^2 + x^2
|
|
1743
|
+
sage: v = var('v'); gam = 1/sqrt(1-v^2)
|
|
1744
|
+
sage: o2.transition_map(o1, [gam*(T - v*X), gam*(X - v*T)])
|
|
1745
|
+
Change of coordinates from Chart (M, (T, X)) to Chart (M, (t, x))
|
|
1746
|
+
sage: f.coord_function(o2)
|
|
1747
|
+
-T^2 + X^2
|
|
1748
|
+
"""
|
|
1749
|
+
if chart is None:
|
|
1750
|
+
chart = self._domain._def_chart
|
|
1751
|
+
else:
|
|
1752
|
+
if chart not in self._domain._atlas:
|
|
1753
|
+
raise ValueError("the {} is not a chart ".format(chart) +
|
|
1754
|
+
"defined on the {}".format(self._domain))
|
|
1755
|
+
if chart not in self._express:
|
|
1756
|
+
# Check whether chart corresponds to a subchart of a chart
|
|
1757
|
+
# where the expression of self is known:
|
|
1758
|
+
for known_chart in self._express:
|
|
1759
|
+
if chart in known_chart._subcharts:
|
|
1760
|
+
new_expr = self._express[known_chart].expr()
|
|
1761
|
+
self._express[chart] = chart.function(new_expr)
|
|
1762
|
+
return self._express[chart]
|
|
1763
|
+
# If this point is reached, the expression must be computed
|
|
1764
|
+
# from that in the chart from_chart, by means of a
|
|
1765
|
+
# change-of-coordinates formula:
|
|
1766
|
+
if from_chart is None:
|
|
1767
|
+
# from_chart in searched among the charts of known expressions
|
|
1768
|
+
# and subcharts of them
|
|
1769
|
+
known_express = self._express.copy()
|
|
1770
|
+
found = False
|
|
1771
|
+
for kchart in known_express:
|
|
1772
|
+
for skchart in kchart._subcharts:
|
|
1773
|
+
if (chart, skchart) in self._domain._coord_changes:
|
|
1774
|
+
from_chart = skchart
|
|
1775
|
+
found = True
|
|
1776
|
+
if skchart not in self._express:
|
|
1777
|
+
self._express[skchart] = skchart.function(
|
|
1778
|
+
self._express[kchart].expr())
|
|
1779
|
+
break
|
|
1780
|
+
if found:
|
|
1781
|
+
break
|
|
1782
|
+
if not found:
|
|
1783
|
+
raise ValueError("no starting chart could be found to " +
|
|
1784
|
+
"compute the expression in the {}".format(chart))
|
|
1785
|
+
change = self._domain._coord_changes[(chart, from_chart)]
|
|
1786
|
+
# old coordinates expressed in terms of the new ones:
|
|
1787
|
+
coords = [change._transf._functions[i].expr()
|
|
1788
|
+
for i in range(self._manifold.dim())]
|
|
1789
|
+
new_expr = self._express[from_chart](*coords)
|
|
1790
|
+
self._express[chart] = chart.function(new_expr)
|
|
1791
|
+
self._del_derived()
|
|
1792
|
+
return self._express[chart]
|
|
1793
|
+
|
|
1794
|
+
def expr(self, chart=None, from_chart=None):
|
|
1795
|
+
r"""
|
|
1796
|
+
Return the coordinate expression of the scalar field in a given
|
|
1797
|
+
chart.
|
|
1798
|
+
|
|
1799
|
+
INPUT:
|
|
1800
|
+
|
|
1801
|
+
- ``chart`` -- (default: ``None``) chart with respect to which the
|
|
1802
|
+
coordinate expression is required; if ``None``, the default
|
|
1803
|
+
chart of the scalar field's domain will be used
|
|
1804
|
+
- ``from_chart`` -- (default: ``None``) chart from which the
|
|
1805
|
+
required expression is computed if it is not known already in the
|
|
1806
|
+
chart ``chart``; if ``None``, a chart is picked in ``self._express``
|
|
1807
|
+
|
|
1808
|
+
OUTPUT:
|
|
1809
|
+
|
|
1810
|
+
- the coordinate expression of the scalar field in the given chart,
|
|
1811
|
+
either as a Sage's symbolic expression or as a SymPy object,
|
|
1812
|
+
depending on the symbolic calculus method used on the chart
|
|
1813
|
+
|
|
1814
|
+
EXAMPLES:
|
|
1815
|
+
|
|
1816
|
+
Expression of a scalar field on a 2-dimensional manifold::
|
|
1817
|
+
|
|
1818
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1819
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1820
|
+
sage: f = M.scalar_field(x*y^2)
|
|
1821
|
+
sage: f.expr()
|
|
1822
|
+
x*y^2
|
|
1823
|
+
sage: f.expr(c_xy) # equivalent form (since c_xy is the default chart)
|
|
1824
|
+
x*y^2
|
|
1825
|
+
|
|
1826
|
+
Expression via a change of coordinates::
|
|
1827
|
+
|
|
1828
|
+
sage: c_uv.<u,v> = M.chart()
|
|
1829
|
+
sage: c_uv.transition_map(c_xy, [u+v, u-v])
|
|
1830
|
+
Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y))
|
|
1831
|
+
sage: f._express # at this stage, f is expressed only in terms of (x,y) coordinates
|
|
1832
|
+
{Chart (M, (x, y)): x*y^2}
|
|
1833
|
+
sage: f.expr(c_uv) # forces the computation of the expression of f in terms of (u,v) coordinates
|
|
1834
|
+
u^3 - u^2*v - u*v^2 + v^3
|
|
1835
|
+
sage: bool( f.expr(c_uv) == (u+v)*(u-v)^2 ) # check
|
|
1836
|
+
True
|
|
1837
|
+
sage: f._express # random (dict. output); f has now 2 coordinate expressions:
|
|
1838
|
+
{Chart (M, (x, y)): x*y^2, Chart (M, (u, v)): u^3 - u^2*v - u*v^2 + v^3}
|
|
1839
|
+
|
|
1840
|
+
Note that the object returned by ``expr()`` depends on the symbolic
|
|
1841
|
+
backend used for coordinate computations::
|
|
1842
|
+
|
|
1843
|
+
sage: type(f.expr())
|
|
1844
|
+
<class 'sage.symbolic.expression.Expression'>
|
|
1845
|
+
sage: M.set_calculus_method('sympy')
|
|
1846
|
+
sage: type(f.expr())
|
|
1847
|
+
<class 'sympy.core.mul.Mul'>
|
|
1848
|
+
sage: f.expr() # note the SymPy exponent notation
|
|
1849
|
+
x*y**2
|
|
1850
|
+
"""
|
|
1851
|
+
return self.coord_function(chart, from_chart).expr()
|
|
1852
|
+
|
|
1853
|
+
def set_expr(self, coord_expression, chart=None):
|
|
1854
|
+
r"""
|
|
1855
|
+
Set the coordinate expression of the scalar field.
|
|
1856
|
+
|
|
1857
|
+
The expressions with respect to other charts are deleted, in order to
|
|
1858
|
+
avoid any inconsistency. To keep them, use :meth:`add_expr` instead.
|
|
1859
|
+
|
|
1860
|
+
INPUT:
|
|
1861
|
+
|
|
1862
|
+
- ``coord_expression`` -- coordinate expression of the scalar field
|
|
1863
|
+
- ``chart`` -- (default: ``None``) chart in which ``coord_expression``
|
|
1864
|
+
is defined; if ``None``, the default chart of the scalar field's
|
|
1865
|
+
domain is assumed
|
|
1866
|
+
|
|
1867
|
+
EXAMPLES:
|
|
1868
|
+
|
|
1869
|
+
Setting scalar field expressions on a 2-dimensional manifold::
|
|
1870
|
+
|
|
1871
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1872
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1873
|
+
sage: f = M.scalar_field(x^2 + 2*x*y +1)
|
|
1874
|
+
sage: f._express
|
|
1875
|
+
{Chart (M, (x, y)): x^2 + 2*x*y + 1}
|
|
1876
|
+
sage: f.set_expr(3*y)
|
|
1877
|
+
sage: f._express # the (x,y) expression has been changed:
|
|
1878
|
+
{Chart (M, (x, y)): 3*y}
|
|
1879
|
+
sage: c_uv.<u,v> = M.chart()
|
|
1880
|
+
sage: f.set_expr(cos(u)-sin(v), c_uv)
|
|
1881
|
+
sage: f._express # the (x,y) expression has been lost:
|
|
1882
|
+
{Chart (M, (u, v)): cos(u) - sin(v)}
|
|
1883
|
+
sage: f.set_expr(3*y)
|
|
1884
|
+
sage: f._express # the (u,v) expression has been lost:
|
|
1885
|
+
{Chart (M, (x, y)): 3*y}
|
|
1886
|
+
|
|
1887
|
+
Since zero and one are special elements, their expressions cannot be
|
|
1888
|
+
changed::
|
|
1889
|
+
|
|
1890
|
+
sage: z = M.zero_scalar_field()
|
|
1891
|
+
sage: z.set_expr(3*y)
|
|
1892
|
+
Traceback (most recent call last):
|
|
1893
|
+
...
|
|
1894
|
+
ValueError: the expressions of an immutable element cannot be
|
|
1895
|
+
changed
|
|
1896
|
+
sage: one = M.one_scalar_field()
|
|
1897
|
+
sage: one.set_expr(3*y)
|
|
1898
|
+
Traceback (most recent call last):
|
|
1899
|
+
...
|
|
1900
|
+
ValueError: the expressions of an immutable element cannot be
|
|
1901
|
+
changed
|
|
1902
|
+
"""
|
|
1903
|
+
if self.is_immutable():
|
|
1904
|
+
raise ValueError("the expressions of an immutable element "
|
|
1905
|
+
"cannot be changed")
|
|
1906
|
+
if chart is None:
|
|
1907
|
+
chart = self._domain._def_chart
|
|
1908
|
+
self._express.clear()
|
|
1909
|
+
self._express[chart] = chart.function(coord_expression)
|
|
1910
|
+
self._is_zero = False # a priori
|
|
1911
|
+
self._del_derived()
|
|
1912
|
+
|
|
1913
|
+
def add_expr(self, coord_expression, chart=None):
|
|
1914
|
+
r"""
|
|
1915
|
+
Add some coordinate expression to the scalar field.
|
|
1916
|
+
|
|
1917
|
+
The previous expressions with respect to other charts are kept. To
|
|
1918
|
+
clear them, use :meth:`set_expr` instead.
|
|
1919
|
+
|
|
1920
|
+
INPUT:
|
|
1921
|
+
|
|
1922
|
+
- ``coord_expression`` -- coordinate expression of the scalar field
|
|
1923
|
+
- ``chart`` -- (default: ``None``) chart in which ``coord_expression``
|
|
1924
|
+
is defined; if ``None``, the default chart of the scalar field's
|
|
1925
|
+
domain is assumed
|
|
1926
|
+
|
|
1927
|
+
.. WARNING::
|
|
1928
|
+
|
|
1929
|
+
If the scalar field has already expressions in other charts, it
|
|
1930
|
+
is the user's responsibility to make sure that the expression
|
|
1931
|
+
to be added is consistent with them.
|
|
1932
|
+
|
|
1933
|
+
EXAMPLES:
|
|
1934
|
+
|
|
1935
|
+
Adding scalar field expressions on a 2-dimensional manifold::
|
|
1936
|
+
|
|
1937
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
1938
|
+
sage: c_xy.<x,y> = M.chart()
|
|
1939
|
+
sage: f = M.scalar_field(x^2 + 2*x*y +1)
|
|
1940
|
+
sage: f._express
|
|
1941
|
+
{Chart (M, (x, y)): x^2 + 2*x*y + 1}
|
|
1942
|
+
sage: f.add_expr(3*y)
|
|
1943
|
+
sage: f._express # the (x,y) expression has been changed:
|
|
1944
|
+
{Chart (M, (x, y)): 3*y}
|
|
1945
|
+
sage: c_uv.<u,v> = M.chart()
|
|
1946
|
+
sage: f.add_expr(cos(u)-sin(v), c_uv)
|
|
1947
|
+
sage: f._express # random (dict. output); f has now 2 expressions:
|
|
1948
|
+
{Chart (M, (x, y)): 3*y, Chart (M, (u, v)): cos(u) - sin(v)}
|
|
1949
|
+
|
|
1950
|
+
Since zero and one are special elements, their expressions cannot be
|
|
1951
|
+
changed::
|
|
1952
|
+
|
|
1953
|
+
sage: z = M.zero_scalar_field()
|
|
1954
|
+
sage: z.add_expr(cos(u)-sin(v), c_uv)
|
|
1955
|
+
Traceback (most recent call last):
|
|
1956
|
+
...
|
|
1957
|
+
ValueError: the expressions of an immutable element cannot be
|
|
1958
|
+
changed
|
|
1959
|
+
sage: one = M.one_scalar_field()
|
|
1960
|
+
sage: one.add_expr(cos(u)-sin(v), c_uv)
|
|
1961
|
+
Traceback (most recent call last):
|
|
1962
|
+
...
|
|
1963
|
+
ValueError: the expressions of an immutable element cannot be
|
|
1964
|
+
changed
|
|
1965
|
+
"""
|
|
1966
|
+
if self.is_immutable():
|
|
1967
|
+
raise ValueError("the expressions of an immutable element "
|
|
1968
|
+
"cannot be changed")
|
|
1969
|
+
if chart is None:
|
|
1970
|
+
chart = self._domain._def_chart
|
|
1971
|
+
self._express[chart] = chart.function(coord_expression)
|
|
1972
|
+
self._is_zero = False # a priori
|
|
1973
|
+
self._del_derived()
|
|
1974
|
+
|
|
1975
|
+
def add_expr_by_continuation(self, chart, subdomain):
|
|
1976
|
+
r"""
|
|
1977
|
+
Set coordinate expression in a chart by continuation of the
|
|
1978
|
+
coordinate expression in a subchart.
|
|
1979
|
+
|
|
1980
|
+
The continuation is performed by demanding that the coordinate
|
|
1981
|
+
expression is identical to that in the restriction of the chart to
|
|
1982
|
+
a given subdomain.
|
|
1983
|
+
|
|
1984
|
+
INPUT:
|
|
1985
|
+
|
|
1986
|
+
- ``chart`` -- coordinate chart `(U,(x^i))` in which the expression of
|
|
1987
|
+
the scalar field is to set
|
|
1988
|
+
- ``subdomain`` -- open subset `V\subset U` in which the expression
|
|
1989
|
+
in terms of the restriction of the coordinate chart `(U,(x^i))` to
|
|
1990
|
+
`V` is already known or can be evaluated by a change of coordinates.
|
|
1991
|
+
|
|
1992
|
+
EXAMPLES:
|
|
1993
|
+
|
|
1994
|
+
Scalar field on the sphere `S^2`::
|
|
1995
|
+
|
|
1996
|
+
sage: M = Manifold(2, 'S^2', structure='topological')
|
|
1997
|
+
sage: U = M.open_subset('U') ; V = M.open_subset('V') # the complement of resp. N pole and S pole
|
|
1998
|
+
sage: M.declare_union(U,V) # S^2 is the union of U and V
|
|
1999
|
+
sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() # stereographic coordinates
|
|
2000
|
+
sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
|
|
2001
|
+
....: intersection_name='W', restrictions1= x^2+y^2!=0,
|
|
2002
|
+
....: restrictions2= u^2+v^2!=0)
|
|
2003
|
+
sage: uv_to_xy = xy_to_uv.inverse()
|
|
2004
|
+
sage: W = U.intersection(V) # S^2 minus the two poles
|
|
2005
|
+
sage: f = M.scalar_field(atan(x^2+y^2), chart=c_xy, name='f')
|
|
2006
|
+
|
|
2007
|
+
The scalar field has been defined only on the domain covered by the
|
|
2008
|
+
chart ``c_xy``, i.e. `U`::
|
|
2009
|
+
|
|
2010
|
+
sage: f.display()
|
|
2011
|
+
f: S^2 → ℝ
|
|
2012
|
+
on U: (x, y) ↦ arctan(x^2 + y^2)
|
|
2013
|
+
on W: (u, v) ↦ arctan(1/(u^2 + v^2))
|
|
2014
|
+
|
|
2015
|
+
We note that on `W = U \cap V`, the expression of `f` in terms of
|
|
2016
|
+
coordinates `(u,v)` can be deduced from that in the coordinates
|
|
2017
|
+
`(x,y)` thanks to the transition map between the two charts::
|
|
2018
|
+
|
|
2019
|
+
sage: f.display(c_uv.restrict(W))
|
|
2020
|
+
f: S^2 → ℝ
|
|
2021
|
+
on W: (u, v) ↦ arctan(1/(u^2 + v^2))
|
|
2022
|
+
|
|
2023
|
+
We use this fact to extend the definition of `f` to the open
|
|
2024
|
+
subset `V`, covered by the chart ``c_uv``::
|
|
2025
|
+
|
|
2026
|
+
sage: f.add_expr_by_continuation(c_uv, W)
|
|
2027
|
+
|
|
2028
|
+
Then, `f` is known on the whole sphere::
|
|
2029
|
+
|
|
2030
|
+
sage: f.display()
|
|
2031
|
+
f: S^2 → ℝ
|
|
2032
|
+
on U: (x, y) ↦ arctan(x^2 + y^2)
|
|
2033
|
+
on V: (u, v) ↦ arctan(1/(u^2 + v^2))
|
|
2034
|
+
"""
|
|
2035
|
+
if self.is_immutable():
|
|
2036
|
+
raise ValueError("the expressions of an immutable element "
|
|
2037
|
+
"cannot be changed")
|
|
2038
|
+
if not chart.domain().is_subset(self._domain):
|
|
2039
|
+
raise ValueError("the chart is not defined on a subset of " +
|
|
2040
|
+
"the scalar field domain")
|
|
2041
|
+
schart = chart.restrict(subdomain)
|
|
2042
|
+
self._express[chart] = chart.function(self.expr(schart))
|
|
2043
|
+
self._is_zero = False # a priori
|
|
2044
|
+
self._del_derived()
|
|
2045
|
+
|
|
2046
|
+
def set_restriction(self, rst):
|
|
2047
|
+
r"""
|
|
2048
|
+
Define a restriction of ``self`` to some subdomain.
|
|
2049
|
+
|
|
2050
|
+
INPUT:
|
|
2051
|
+
|
|
2052
|
+
- ``rst`` -- :class:`ScalarField` defined on a subdomain of
|
|
2053
|
+
the domain of ``self``
|
|
2054
|
+
|
|
2055
|
+
EXAMPLES::
|
|
2056
|
+
|
|
2057
|
+
sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
|
|
2058
|
+
sage: U = M.open_subset('U') # complement of the North pole
|
|
2059
|
+
sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
|
|
2060
|
+
sage: V = M.open_subset('V') # complement of the South pole
|
|
2061
|
+
sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
|
|
2062
|
+
sage: M.declare_union(U,V) # S^2 is the union of U and V
|
|
2063
|
+
sage: f = M.scalar_field(name='f')
|
|
2064
|
+
sage: g = U.scalar_field(x^2+y)
|
|
2065
|
+
sage: f.set_restriction(g)
|
|
2066
|
+
sage: f.display()
|
|
2067
|
+
f: M → ℝ
|
|
2068
|
+
on U: (x, y) ↦ x^2 + y
|
|
2069
|
+
sage: f.restrict(U) == g
|
|
2070
|
+
True
|
|
2071
|
+
"""
|
|
2072
|
+
if self.is_immutable():
|
|
2073
|
+
raise ValueError("the expressions of an immutable element "
|
|
2074
|
+
"cannot be changed")
|
|
2075
|
+
if not isinstance(rst, ScalarField):
|
|
2076
|
+
raise TypeError("the argument must be a scalar field")
|
|
2077
|
+
if not rst._domain.is_subset(self._domain):
|
|
2078
|
+
raise ValueError("the domain of the declared restriction is not " +
|
|
2079
|
+
"a subset of the field's domain")
|
|
2080
|
+
self._restrictions[rst._domain] = rst.copy(name=self._name,
|
|
2081
|
+
latex_name=self._latex_name)
|
|
2082
|
+
for chart, expr in rst._express.items():
|
|
2083
|
+
intersection = chart.domain().intersection(rst._domain)
|
|
2084
|
+
self._express[chart.restrict(intersection)] = expr
|
|
2085
|
+
self._is_zero = False # a priori
|
|
2086
|
+
|
|
2087
|
+
def display(self, chart: Optional[Chart] = None) -> FormattedExpansion:
|
|
2088
|
+
r"""
|
|
2089
|
+
Display the expression of the scalar field in a given chart.
|
|
2090
|
+
|
|
2091
|
+
Without any argument, this function displays all known, distinct
|
|
2092
|
+
expressions.
|
|
2093
|
+
|
|
2094
|
+
INPUT:
|
|
2095
|
+
|
|
2096
|
+
- ``chart`` -- (default: ``None``) chart with respect to which
|
|
2097
|
+
the coordinate expression is to be displayed; if ``None``, the
|
|
2098
|
+
display is performed in all the greatest charts in which the
|
|
2099
|
+
coordinate expression is known
|
|
2100
|
+
|
|
2101
|
+
The output is either text-formatted (console mode) or LaTeX-formatted
|
|
2102
|
+
(notebook mode).
|
|
2103
|
+
|
|
2104
|
+
EXAMPLES:
|
|
2105
|
+
|
|
2106
|
+
Various displays::
|
|
2107
|
+
|
|
2108
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2109
|
+
sage: c_xy.<x,y> = M.chart()
|
|
2110
|
+
sage: f = M.scalar_field(sqrt(x+1), name='f')
|
|
2111
|
+
sage: f.display()
|
|
2112
|
+
f: M → ℝ
|
|
2113
|
+
(x, y) ↦ sqrt(x + 1)
|
|
2114
|
+
sage: latex(f.display())
|
|
2115
|
+
\begin{array}{llcl} f:& M & \longrightarrow & \mathbb{R} \\ & \left(x, y\right) & \longmapsto & \sqrt{x + 1} \end{array}
|
|
2116
|
+
sage: g = M.scalar_field(function('G')(x, y), name='g')
|
|
2117
|
+
sage: g.display()
|
|
2118
|
+
g: M → ℝ
|
|
2119
|
+
(x, y) ↦ G(x, y)
|
|
2120
|
+
sage: latex(g.display())
|
|
2121
|
+
\begin{array}{llcl} g:& M & \longrightarrow & \mathbb{R} \\ & \left(x, y\right) & \longmapsto & G\left(x, y\right) \end{array}
|
|
2122
|
+
|
|
2123
|
+
A shortcut of ``display()`` is ``disp()``::
|
|
2124
|
+
|
|
2125
|
+
sage: f.disp()
|
|
2126
|
+
f: M → ℝ
|
|
2127
|
+
(x, y) ↦ sqrt(x + 1)
|
|
2128
|
+
|
|
2129
|
+
In case the scalar field is piecewise-defined, the ``display()``
|
|
2130
|
+
command still outputs all expressions. Each expression displayed
|
|
2131
|
+
corresponds to the chart on the greatest domain where this particular
|
|
2132
|
+
expression is known::
|
|
2133
|
+
|
|
2134
|
+
sage: U = M.open_subset('U')
|
|
2135
|
+
sage: f.set_expr(y^2, c_xy.restrict(U))
|
|
2136
|
+
sage: f.display()
|
|
2137
|
+
f: M → ℝ
|
|
2138
|
+
on U: (x, y) ↦ y^2
|
|
2139
|
+
sage: latex(f.display())
|
|
2140
|
+
\begin{array}{llcl} f:& M & \longrightarrow & \mathbb{R} \\ \text{on}\ U : & \left(x, y\right) & \longmapsto & y^{2} \end{array}
|
|
2141
|
+
"""
|
|
2142
|
+
from sage.misc.latex import latex
|
|
2143
|
+
from sage.tensor.modules.format_utilities import FormattedExpansion
|
|
2144
|
+
from sage.typeset.unicode_characters import (
|
|
2145
|
+
unicode_mapsto,
|
|
2146
|
+
unicode_mathbbC,
|
|
2147
|
+
unicode_mathbbR,
|
|
2148
|
+
unicode_to,
|
|
2149
|
+
)
|
|
2150
|
+
|
|
2151
|
+
def _display_expression(self, chart, result):
|
|
2152
|
+
r"""
|
|
2153
|
+
Helper function for :meth:`display`.
|
|
2154
|
+
"""
|
|
2155
|
+
try:
|
|
2156
|
+
# get coordinate expression
|
|
2157
|
+
expression = self.coord_function(chart)
|
|
2158
|
+
except (TypeError, ValueError):
|
|
2159
|
+
pass
|
|
2160
|
+
# if that succeeds, proceed:
|
|
2161
|
+
coords = chart[:]
|
|
2162
|
+
if len(coords) == 1:
|
|
2163
|
+
coords = coords[0]
|
|
2164
|
+
if chart.domain() == self._domain:
|
|
2165
|
+
if self._name is not None:
|
|
2166
|
+
result._txt += " "
|
|
2167
|
+
result._latex += " & "
|
|
2168
|
+
else:
|
|
2169
|
+
result._txt += "on " + chart.domain()._name + ": "
|
|
2170
|
+
result._latex += r"\text{on}\ " + latex(chart.domain()) \
|
|
2171
|
+
+ r": & "
|
|
2172
|
+
result._txt += repr(coords) + " " + unicode_mapsto + " " \
|
|
2173
|
+
+ repr(expression) + "\n"
|
|
2174
|
+
result._latex += latex(coords) + r"& \longmapsto & " \
|
|
2175
|
+
+ latex(expression) + r"\\"
|
|
2176
|
+
|
|
2177
|
+
# Name of the base field:
|
|
2178
|
+
field = self._domain.base_field()
|
|
2179
|
+
field_type = self._domain.base_field_type()
|
|
2180
|
+
if field_type == 'real':
|
|
2181
|
+
field_name = unicode_mathbbR
|
|
2182
|
+
field_latex_name = r'\mathbb{R}'
|
|
2183
|
+
elif field_type == 'complex':
|
|
2184
|
+
field_name = unicode_mathbbC
|
|
2185
|
+
field_latex_name = r'\mathbb{C}'
|
|
2186
|
+
else:
|
|
2187
|
+
field_name = str(field)
|
|
2188
|
+
field_latex_name = latex(field)
|
|
2189
|
+
|
|
2190
|
+
result = FormattedExpansion()
|
|
2191
|
+
if self._name is None:
|
|
2192
|
+
symbol = ""
|
|
2193
|
+
else:
|
|
2194
|
+
symbol = self._name + ": "
|
|
2195
|
+
result._txt = symbol + self._domain._name + " " + unicode_to + " " \
|
|
2196
|
+
+ field_name + "\n"
|
|
2197
|
+
if self._latex_name is None:
|
|
2198
|
+
symbol = ""
|
|
2199
|
+
else:
|
|
2200
|
+
symbol = self._latex_name + ":"
|
|
2201
|
+
result._latex = r"\begin{array}{llcl} " + symbol + r"&" + \
|
|
2202
|
+
latex(self._domain) + r"& \longrightarrow & " + \
|
|
2203
|
+
field_latex_name + r" \\"
|
|
2204
|
+
if chart is None:
|
|
2205
|
+
for ch in self._domain._top_charts:
|
|
2206
|
+
###
|
|
2207
|
+
# Get the greatest domain of top chart restrictions where the
|
|
2208
|
+
# expression is known:
|
|
2209
|
+
max_dom = None
|
|
2210
|
+
for sch in ch._subcharts:
|
|
2211
|
+
if max_dom is None:
|
|
2212
|
+
try:
|
|
2213
|
+
self.coord_function(sch)
|
|
2214
|
+
max_dom = sch.domain()
|
|
2215
|
+
except (TypeError, ValueError):
|
|
2216
|
+
pass
|
|
2217
|
+
elif max_dom.is_subset(sch.domain()):
|
|
2218
|
+
try:
|
|
2219
|
+
self.coord_function(sch)
|
|
2220
|
+
max_dom = sch.domain()
|
|
2221
|
+
except (TypeError, ValueError):
|
|
2222
|
+
pass
|
|
2223
|
+
if max_dom is not None:
|
|
2224
|
+
_display_expression(self, ch.restrict(max_dom), result)
|
|
2225
|
+
else:
|
|
2226
|
+
_display_expression(self, chart, result)
|
|
2227
|
+
result._txt = result._txt[:-1]
|
|
2228
|
+
result._latex = result._latex[:-2] + r"\end{array}"
|
|
2229
|
+
return result
|
|
2230
|
+
|
|
2231
|
+
disp = display
|
|
2232
|
+
|
|
2233
|
+
def restrict(self, subdomain):
|
|
2234
|
+
r"""
|
|
2235
|
+
Restriction of the scalar field to an open subset of its domain of
|
|
2236
|
+
definition.
|
|
2237
|
+
|
|
2238
|
+
INPUT:
|
|
2239
|
+
|
|
2240
|
+
- ``subdomain`` -- an open subset of the scalar field's domain
|
|
2241
|
+
|
|
2242
|
+
OUTPUT:
|
|
2243
|
+
|
|
2244
|
+
- instance of :class:`ScalarField` representing the restriction of
|
|
2245
|
+
the scalar field to ``subdomain``
|
|
2246
|
+
|
|
2247
|
+
EXAMPLES:
|
|
2248
|
+
|
|
2249
|
+
Restriction of a scalar field defined on `\RR^2` to the
|
|
2250
|
+
unit open disc::
|
|
2251
|
+
|
|
2252
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2253
|
+
sage: X.<x,y> = M.chart() # Cartesian coordinates
|
|
2254
|
+
sage: U = M.open_subset('U', coord_def={X: x^2+y^2 < 1}) # U unit open disc
|
|
2255
|
+
sage: f = M.scalar_field(cos(x*y), name='f')
|
|
2256
|
+
sage: f_U = f.restrict(U) ; f_U
|
|
2257
|
+
Scalar field f on the Open subset U of the 2-dimensional
|
|
2258
|
+
topological manifold M
|
|
2259
|
+
sage: f_U.display()
|
|
2260
|
+
f: U → ℝ
|
|
2261
|
+
(x, y) ↦ cos(x*y)
|
|
2262
|
+
sage: f.parent()
|
|
2263
|
+
Algebra of scalar fields on the 2-dimensional topological
|
|
2264
|
+
manifold M
|
|
2265
|
+
sage: f_U.parent()
|
|
2266
|
+
Algebra of scalar fields on the Open subset U of the 2-dimensional
|
|
2267
|
+
topological manifold M
|
|
2268
|
+
|
|
2269
|
+
The restriction to the whole domain is the identity::
|
|
2270
|
+
|
|
2271
|
+
sage: f.restrict(M) is f
|
|
2272
|
+
True
|
|
2273
|
+
sage: f_U.restrict(U) is f_U
|
|
2274
|
+
True
|
|
2275
|
+
|
|
2276
|
+
Restriction of the zero scalar field::
|
|
2277
|
+
|
|
2278
|
+
sage: M.zero_scalar_field().restrict(U)
|
|
2279
|
+
Scalar field zero on the Open subset U of the 2-dimensional
|
|
2280
|
+
topological manifold M
|
|
2281
|
+
sage: M.zero_scalar_field().restrict(U) is U.zero_scalar_field()
|
|
2282
|
+
True
|
|
2283
|
+
"""
|
|
2284
|
+
if subdomain == self._domain:
|
|
2285
|
+
return self
|
|
2286
|
+
if subdomain not in self._restrictions:
|
|
2287
|
+
if not subdomain.is_subset(self._domain):
|
|
2288
|
+
raise ValueError("the specified domain is not a subset of " +
|
|
2289
|
+
"the domain of definition of the scalar field")
|
|
2290
|
+
# Special case of the zero scalar field:
|
|
2291
|
+
if self._is_zero:
|
|
2292
|
+
return subdomain._zero_scalar_field
|
|
2293
|
+
# First one tries to get the restriction from a tighter domain:
|
|
2294
|
+
for dom, rst in self._restrictions.items():
|
|
2295
|
+
if subdomain.is_subset(dom):
|
|
2296
|
+
self._restrictions[subdomain] = rst.restrict(subdomain)
|
|
2297
|
+
break
|
|
2298
|
+
else:
|
|
2299
|
+
# If this fails, the restriction must be created from scratch:
|
|
2300
|
+
sexpress = {}
|
|
2301
|
+
for chart, funct in self._express.items():
|
|
2302
|
+
for schart in subdomain.atlas():
|
|
2303
|
+
if schart in chart._subcharts:
|
|
2304
|
+
sexpress[schart] = funct.expr()
|
|
2305
|
+
resu = type(self)(subdomain.scalar_field_algebra(),
|
|
2306
|
+
coord_expression=sexpress, name=self._name,
|
|
2307
|
+
latex_name=self._latex_name)
|
|
2308
|
+
if self.is_immutable():
|
|
2309
|
+
resu.set_immutable() # restriction must be immutable, too
|
|
2310
|
+
self._restrictions[subdomain] = resu
|
|
2311
|
+
return self._restrictions[subdomain]
|
|
2312
|
+
|
|
2313
|
+
def common_charts(self, other):
|
|
2314
|
+
r"""
|
|
2315
|
+
Find common charts for the expressions of the scalar field and
|
|
2316
|
+
``other``.
|
|
2317
|
+
|
|
2318
|
+
INPUT:
|
|
2319
|
+
|
|
2320
|
+
- ``other`` -- a scalar field
|
|
2321
|
+
|
|
2322
|
+
OUTPUT:
|
|
2323
|
+
|
|
2324
|
+
- list of common charts; if no common chart is found, ``None`` is
|
|
2325
|
+
returned (instead of an empty list)
|
|
2326
|
+
|
|
2327
|
+
EXAMPLES:
|
|
2328
|
+
|
|
2329
|
+
Search for common charts on a 2-dimensional manifold with 2
|
|
2330
|
+
overlapping domains::
|
|
2331
|
+
|
|
2332
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2333
|
+
sage: U = M.open_subset('U')
|
|
2334
|
+
sage: c_xy.<x,y> = U.chart()
|
|
2335
|
+
sage: V = M.open_subset('V')
|
|
2336
|
+
sage: c_uv.<u,v> = V.chart()
|
|
2337
|
+
sage: M.declare_union(U,V) # M is the union of U and V
|
|
2338
|
+
sage: f = U.scalar_field(x^2)
|
|
2339
|
+
sage: g = M.scalar_field(x+y)
|
|
2340
|
+
sage: f.common_charts(g)
|
|
2341
|
+
[Chart (U, (x, y))]
|
|
2342
|
+
sage: g.add_expr(u, c_uv)
|
|
2343
|
+
sage: f._express
|
|
2344
|
+
{Chart (U, (x, y)): x^2}
|
|
2345
|
+
sage: g._express # random (dictionary output)
|
|
2346
|
+
{Chart (U, (x, y)): x + y, Chart (V, (u, v)): u}
|
|
2347
|
+
sage: f.common_charts(g)
|
|
2348
|
+
[Chart (U, (x, y))]
|
|
2349
|
+
|
|
2350
|
+
Common charts found as subcharts: the subcharts are introduced via
|
|
2351
|
+
a transition map between charts c_xy and c_uv on the intersecting
|
|
2352
|
+
subdomain `W = U\cap V`::
|
|
2353
|
+
|
|
2354
|
+
sage: trans = c_xy.transition_map(c_uv, (x+y, x-y), 'W', x<0, u+v<0)
|
|
2355
|
+
sage: M.atlas()
|
|
2356
|
+
[Chart (U, (x, y)), Chart (V, (u, v)), Chart (W, (x, y)),
|
|
2357
|
+
Chart (W, (u, v))]
|
|
2358
|
+
sage: c_xy_W = M.atlas()[2]
|
|
2359
|
+
sage: c_uv_W = M.atlas()[3]
|
|
2360
|
+
sage: trans.inverse()
|
|
2361
|
+
Change of coordinates from Chart (W, (u, v)) to Chart (W, (x, y))
|
|
2362
|
+
sage: f.common_charts(g)
|
|
2363
|
+
[Chart (U, (x, y))]
|
|
2364
|
+
sage: f.expr(c_xy_W)
|
|
2365
|
+
x^2
|
|
2366
|
+
sage: f._express # random (dictionary output)
|
|
2367
|
+
{Chart (U, (x, y)): x^2, Chart (W, (x, y)): x^2}
|
|
2368
|
+
sage: g._express # random (dictionary output)
|
|
2369
|
+
{Chart (U, (x, y)): x + y, Chart (V, (u, v)): u}
|
|
2370
|
+
sage: g.common_charts(f) # c_xy_W is not returned because it is subchart of 'xy'
|
|
2371
|
+
[Chart (U, (x, y))]
|
|
2372
|
+
sage: f.expr(c_uv_W)
|
|
2373
|
+
1/4*u^2 + 1/2*u*v + 1/4*v^2
|
|
2374
|
+
sage: f._express # random (dictionary output)
|
|
2375
|
+
{Chart (U, (x, y)): x^2, Chart (W, (x, y)): x^2,
|
|
2376
|
+
Chart (W, (u, v)): 1/4*u^2 + 1/2*u*v + 1/4*v^2}
|
|
2377
|
+
sage: g._express # random (dictionary output)
|
|
2378
|
+
{Chart (U, (x, y)): x + y, Chart (V, (u, v)): u}
|
|
2379
|
+
sage: f.common_charts(g)
|
|
2380
|
+
[Chart (U, (x, y)), Chart (W, (u, v))]
|
|
2381
|
+
sage: # the expressions have been updated on the subcharts
|
|
2382
|
+
sage: g._express # random (dictionary output)
|
|
2383
|
+
{Chart (U, (x, y)): x + y, Chart (V, (u, v)): u,
|
|
2384
|
+
Chart (W, (u, v)): u}
|
|
2385
|
+
|
|
2386
|
+
Common charts found by computing some coordinate changes::
|
|
2387
|
+
|
|
2388
|
+
sage: W = U.intersection(V)
|
|
2389
|
+
sage: f = W.scalar_field(x^2, c_xy_W)
|
|
2390
|
+
sage: g = W.scalar_field(u+1, c_uv_W)
|
|
2391
|
+
sage: f._express
|
|
2392
|
+
{Chart (W, (x, y)): x^2}
|
|
2393
|
+
sage: g._express
|
|
2394
|
+
{Chart (W, (u, v)): u + 1}
|
|
2395
|
+
sage: f.common_charts(g)
|
|
2396
|
+
[Chart (W, (x, y)), Chart (W, (u, v))]
|
|
2397
|
+
sage: f._express # random (dictionary output)
|
|
2398
|
+
{Chart (W, (u, v)): 1/4*u^2 + 1/2*u*v + 1/4*v^2,
|
|
2399
|
+
Chart (W, (x, y)): x^2}
|
|
2400
|
+
sage: g._express # random (dictionary output)
|
|
2401
|
+
{Chart (W, (u, v)): u + 1, Chart (W, (x, y)): x + y + 1}
|
|
2402
|
+
|
|
2403
|
+
TESTS:
|
|
2404
|
+
|
|
2405
|
+
Check that :issue:`28072` has been fixed::
|
|
2406
|
+
|
|
2407
|
+
sage: c_ab.<a,b> = W.chart()
|
|
2408
|
+
sage: xy_to_ab = c_xy_W.transition_map(c_ab, (3*y, x-y))
|
|
2409
|
+
sage: h = W.scalar_field(a+b, chart=c_ab)
|
|
2410
|
+
sage: f.common_charts(h)
|
|
2411
|
+
[Chart (W, (x, y))]
|
|
2412
|
+
sage: h.expr(c_xy_W)
|
|
2413
|
+
x + 2*y
|
|
2414
|
+
"""
|
|
2415
|
+
if not isinstance(other, ScalarField):
|
|
2416
|
+
raise TypeError("the second argument must be a scalar field")
|
|
2417
|
+
coord_changes = self._manifold._coord_changes
|
|
2418
|
+
resu = []
|
|
2419
|
+
#
|
|
2420
|
+
# 1/ Search for common charts among the existing expressions, i.e.
|
|
2421
|
+
# without performing any expression transformation.
|
|
2422
|
+
# -------------------------------------------------------------
|
|
2423
|
+
for chart1 in self._express:
|
|
2424
|
+
if chart1 in other._express:
|
|
2425
|
+
resu.append(chart1)
|
|
2426
|
+
# Search for a subchart:
|
|
2427
|
+
known_expr1 = self._express.copy()
|
|
2428
|
+
known_expr2 = other._express.copy()
|
|
2429
|
+
for chart1 in known_expr1:
|
|
2430
|
+
if chart1 not in resu:
|
|
2431
|
+
for chart2 in known_expr2:
|
|
2432
|
+
if chart2 not in resu:
|
|
2433
|
+
if chart2 in chart1._subcharts:
|
|
2434
|
+
self.expr(chart2)
|
|
2435
|
+
resu.append(chart2)
|
|
2436
|
+
if chart1 in chart2._subcharts:
|
|
2437
|
+
other.expr(chart1)
|
|
2438
|
+
resu.append(chart1)
|
|
2439
|
+
#
|
|
2440
|
+
# 2/ Search for common charts via one expression transformation
|
|
2441
|
+
# ----------------------------------------------------------
|
|
2442
|
+
for chart1 in known_expr1:
|
|
2443
|
+
if chart1 not in resu:
|
|
2444
|
+
for chart2 in known_expr2:
|
|
2445
|
+
if chart2 not in resu:
|
|
2446
|
+
if (chart1, chart2) in coord_changes:
|
|
2447
|
+
other.coord_function(chart1, from_chart=chart2)
|
|
2448
|
+
resu.append(chart1)
|
|
2449
|
+
if (chart2, chart1) in coord_changes:
|
|
2450
|
+
self.coord_function(chart2, from_chart=chart1)
|
|
2451
|
+
resu.append(chart2)
|
|
2452
|
+
if not resu:
|
|
2453
|
+
return None
|
|
2454
|
+
else:
|
|
2455
|
+
return resu
|
|
2456
|
+
|
|
2457
|
+
def __call__(self, p, chart=None):
|
|
2458
|
+
r"""
|
|
2459
|
+
Compute the value of the scalar field at a given point.
|
|
2460
|
+
|
|
2461
|
+
INPUT:
|
|
2462
|
+
|
|
2463
|
+
- ``p`` -- point in the scalar field's domain
|
|
2464
|
+
- ``chart`` -- (default: ``None``) chart in which the coordinates
|
|
2465
|
+
of ``p`` are to be considered; if ``None``, a chart in which
|
|
2466
|
+
both ``p``'s coordinates and the expression of the scalar field
|
|
2467
|
+
are known is searched, starting from the default chart
|
|
2468
|
+
of ``self._domain``
|
|
2469
|
+
|
|
2470
|
+
OUTPUT: value at ``p``
|
|
2471
|
+
|
|
2472
|
+
EXAMPLES::
|
|
2473
|
+
|
|
2474
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2475
|
+
sage: X.<x,y> = M.chart()
|
|
2476
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2477
|
+
sage: p = M((2,-5), name='p'); p
|
|
2478
|
+
Point p on the 2-dimensional topological manifold M
|
|
2479
|
+
sage: f.__call__(p)
|
|
2480
|
+
-3
|
|
2481
|
+
sage: f(p)
|
|
2482
|
+
-3
|
|
2483
|
+
sage: M.zero_scalar_field()(p)
|
|
2484
|
+
0
|
|
2485
|
+
sage: M.one_scalar_field()(p)
|
|
2486
|
+
1
|
|
2487
|
+
|
|
2488
|
+
Example with a change of chart::
|
|
2489
|
+
|
|
2490
|
+
sage: Y.<u,v> = M.chart()
|
|
2491
|
+
sage: X_to_Y = X.transition_map(Y, [x+y, x-y])
|
|
2492
|
+
sage: Y_to_X = X_to_Y.inverse()
|
|
2493
|
+
sage: g = M.scalar_field({Y: u*v}, name='g')
|
|
2494
|
+
sage: g(p)
|
|
2495
|
+
-21
|
|
2496
|
+
sage: p.coord(Y)
|
|
2497
|
+
(-3, 7)
|
|
2498
|
+
"""
|
|
2499
|
+
# ! # it should be "if p not in self_domain:" instead, but this test is
|
|
2500
|
+
# skipped for efficiency
|
|
2501
|
+
if p not in self._manifold:
|
|
2502
|
+
raise ValueError("the {} ".format(p) + "does not belong " +
|
|
2503
|
+
"to the {}".format(self._manifold))
|
|
2504
|
+
if self._is_zero:
|
|
2505
|
+
return 0
|
|
2506
|
+
if chart is None:
|
|
2507
|
+
# A common chart is searched:
|
|
2508
|
+
def_chart = self._domain._def_chart
|
|
2509
|
+
if def_chart in p._coordinates and def_chart in self._express:
|
|
2510
|
+
chart = def_chart
|
|
2511
|
+
else:
|
|
2512
|
+
for chart_p in p._coordinates:
|
|
2513
|
+
if chart_p in self._express:
|
|
2514
|
+
chart = chart_p
|
|
2515
|
+
break
|
|
2516
|
+
if chart is None:
|
|
2517
|
+
# A change of coordinates is attempted for p:
|
|
2518
|
+
for chart_s in self._express:
|
|
2519
|
+
try:
|
|
2520
|
+
p.coord(chart_s)
|
|
2521
|
+
chart = chart_s
|
|
2522
|
+
break
|
|
2523
|
+
except ValueError:
|
|
2524
|
+
pass
|
|
2525
|
+
else:
|
|
2526
|
+
# A change of coordinates is attempted on the scalar field
|
|
2527
|
+
# expressions:
|
|
2528
|
+
for chart_p in p._coordinates:
|
|
2529
|
+
try:
|
|
2530
|
+
self.coord_function(chart_p)
|
|
2531
|
+
chart = chart_p
|
|
2532
|
+
break
|
|
2533
|
+
except (TypeError, ValueError):
|
|
2534
|
+
pass
|
|
2535
|
+
if chart is None:
|
|
2536
|
+
raise ValueError("no common chart has been found to evaluate " +
|
|
2537
|
+
"the action of {} on the {}".format(self, p))
|
|
2538
|
+
return self._express[chart](*(p._coordinates[chart]))
|
|
2539
|
+
|
|
2540
|
+
def preimage(self, codomain_subset, name=None, latex_name=None):
|
|
2541
|
+
r"""
|
|
2542
|
+
Return the preimage of ``codomain_subset``.
|
|
2543
|
+
|
|
2544
|
+
An alias is :meth:`pullback`.
|
|
2545
|
+
|
|
2546
|
+
INPUT:
|
|
2547
|
+
|
|
2548
|
+
- ``codomain_subset`` -- an instance of
|
|
2549
|
+
:class:`~sage.sets.real_set.RealSet`
|
|
2550
|
+
- ``name`` -- string; name (symbol) given to the subset
|
|
2551
|
+
- ``latex_name`` -- string (default: ``None``); LaTeX symbol to
|
|
2552
|
+
denote the subset; if none are provided, it is set to ``name``
|
|
2553
|
+
|
|
2554
|
+
OUTPUT:
|
|
2555
|
+
|
|
2556
|
+
- either a :class:`~sage.manifolds.manifold.TopologicalManifold` or
|
|
2557
|
+
a :class:`~sage.manifolds.subsets.pullback.ManifoldSubsetPullback`
|
|
2558
|
+
|
|
2559
|
+
EXAMPLES::
|
|
2560
|
+
|
|
2561
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2562
|
+
sage: X.<x,y> = M.chart()
|
|
2563
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2564
|
+
sage: L = f.pullback(RealSet.point(1)); latex(L)
|
|
2565
|
+
f^{-1}(\{1\})
|
|
2566
|
+
sage: M((-1, 1)) in L
|
|
2567
|
+
False
|
|
2568
|
+
sage: M((0, 1)) in L
|
|
2569
|
+
True
|
|
2570
|
+
|
|
2571
|
+
sage: M.zero_scalar_field().preimage(RealSet.point(0)) is M
|
|
2572
|
+
True
|
|
2573
|
+
"""
|
|
2574
|
+
if self.is_trivial_zero() and 0 in codomain_subset:
|
|
2575
|
+
return self.domain()
|
|
2576
|
+
from sage.manifolds.subsets.pullback import ManifoldSubsetPullback
|
|
2577
|
+
return ManifoldSubsetPullback(self, codomain_subset,
|
|
2578
|
+
name=name, latex_name=latex_name)
|
|
2579
|
+
|
|
2580
|
+
pullback = preimage
|
|
2581
|
+
|
|
2582
|
+
def __pos__(self):
|
|
2583
|
+
r"""
|
|
2584
|
+
Unary plus operator.
|
|
2585
|
+
|
|
2586
|
+
OUTPUT: an exact copy of the scalar field
|
|
2587
|
+
|
|
2588
|
+
TESTS::
|
|
2589
|
+
|
|
2590
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2591
|
+
sage: X.<x,y> = M.chart()
|
|
2592
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2593
|
+
sage: g = f.__pos__(); g
|
|
2594
|
+
Scalar field +f on the 2-dimensional topological manifold M
|
|
2595
|
+
sage: g == f
|
|
2596
|
+
True
|
|
2597
|
+
"""
|
|
2598
|
+
result = type(self)(self.parent())
|
|
2599
|
+
for chart in self._express:
|
|
2600
|
+
result._express[chart] = + self._express[chart]
|
|
2601
|
+
if self._name is not None:
|
|
2602
|
+
result._name = '+' + self._name
|
|
2603
|
+
if self._latex_name is not None:
|
|
2604
|
+
result._latex_name = '+' + self._latex_name
|
|
2605
|
+
return result
|
|
2606
|
+
|
|
2607
|
+
def __neg__(self):
|
|
2608
|
+
r"""
|
|
2609
|
+
Unary minus operator.
|
|
2610
|
+
|
|
2611
|
+
OUTPUT: the negative of the scalar field
|
|
2612
|
+
|
|
2613
|
+
TESTS::
|
|
2614
|
+
|
|
2615
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2616
|
+
sage: X.<x,y> = M.chart()
|
|
2617
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2618
|
+
sage: g = f.__neg__(); g
|
|
2619
|
+
Scalar field -f on the 2-dimensional topological manifold M
|
|
2620
|
+
sage: g.display()
|
|
2621
|
+
-f: M → ℝ
|
|
2622
|
+
(x, y) ↦ -x - y
|
|
2623
|
+
sage: g.__neg__() == f
|
|
2624
|
+
True
|
|
2625
|
+
"""
|
|
2626
|
+
result = type(self)(self.parent())
|
|
2627
|
+
for chart in self._express:
|
|
2628
|
+
result._express[chart] = - self._express[chart]
|
|
2629
|
+
if self._name is not None:
|
|
2630
|
+
result._name = '-' + self._name
|
|
2631
|
+
if self._latex_name is not None:
|
|
2632
|
+
result._latex_name = '-' + self._latex_name
|
|
2633
|
+
return result
|
|
2634
|
+
|
|
2635
|
+
# ### CommutativeAlgebraElement arithmetic operators ###
|
|
2636
|
+
|
|
2637
|
+
def _add_(self, other):
|
|
2638
|
+
r"""
|
|
2639
|
+
Scalar field addition.
|
|
2640
|
+
|
|
2641
|
+
INPUT:
|
|
2642
|
+
|
|
2643
|
+
- ``other`` -- a scalar field (in the same algebra as ``self``)
|
|
2644
|
+
|
|
2645
|
+
OUTPUT:
|
|
2646
|
+
|
|
2647
|
+
- the scalar field resulting from the addition of ``self`` and
|
|
2648
|
+
``other``
|
|
2649
|
+
|
|
2650
|
+
TESTS::
|
|
2651
|
+
|
|
2652
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2653
|
+
sage: X.<x,y> = M.chart()
|
|
2654
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2655
|
+
sage: g = M.scalar_field({X: x*y}, name='g')
|
|
2656
|
+
sage: s = f._add_(g); s
|
|
2657
|
+
Scalar field f+g on the 2-dimensional topological manifold M
|
|
2658
|
+
sage: s.display()
|
|
2659
|
+
f+g: M → ℝ
|
|
2660
|
+
(x, y) ↦ (x + 1)*y + x
|
|
2661
|
+
sage: s == f+g
|
|
2662
|
+
True
|
|
2663
|
+
sage: f._add_(M.zero_scalar_field()) == f
|
|
2664
|
+
True
|
|
2665
|
+
"""
|
|
2666
|
+
# Trivial cases:
|
|
2667
|
+
if self.is_trivial_zero():
|
|
2668
|
+
return other
|
|
2669
|
+
if other.is_trivial_zero():
|
|
2670
|
+
return self
|
|
2671
|
+
# Generic case:
|
|
2672
|
+
com_charts = self.common_charts(other)
|
|
2673
|
+
if com_charts is None:
|
|
2674
|
+
raise ValueError("no common chart for the addition")
|
|
2675
|
+
result = type(self)(self.parent())
|
|
2676
|
+
for chart in com_charts:
|
|
2677
|
+
# ChartFunction addition:
|
|
2678
|
+
result._express[chart] = self._express[chart] + other._express[chart]
|
|
2679
|
+
if self._name is not None and other._name is not None:
|
|
2680
|
+
result._name = self._name + '+' + other._name
|
|
2681
|
+
if self._latex_name is not None and other._latex_name is not None:
|
|
2682
|
+
result._latex_name = self._latex_name + '+' + other._latex_name
|
|
2683
|
+
return result
|
|
2684
|
+
|
|
2685
|
+
def _sub_(self, other):
|
|
2686
|
+
r"""
|
|
2687
|
+
Scalar field subtraction.
|
|
2688
|
+
|
|
2689
|
+
INPUT:
|
|
2690
|
+
|
|
2691
|
+
- ``other`` -- a scalar field (in the same algebra as ``self``)
|
|
2692
|
+
|
|
2693
|
+
OUTPUT:
|
|
2694
|
+
|
|
2695
|
+
- the scalar field resulting from the subtraction of ``other`` from
|
|
2696
|
+
``self``
|
|
2697
|
+
|
|
2698
|
+
TESTS::
|
|
2699
|
+
|
|
2700
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2701
|
+
sage: X.<x,y> = M.chart()
|
|
2702
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2703
|
+
sage: g = M.scalar_field({X: x*y}, name='g')
|
|
2704
|
+
sage: s = f._sub_(g); s
|
|
2705
|
+
Scalar field f-g on the 2-dimensional topological manifold M
|
|
2706
|
+
sage: s.display()
|
|
2707
|
+
f-g: M → ℝ
|
|
2708
|
+
(x, y) ↦ -(x - 1)*y + x
|
|
2709
|
+
sage: s == f-g
|
|
2710
|
+
True
|
|
2711
|
+
sage: f._sub_(M.zero_scalar_field()) == f
|
|
2712
|
+
True
|
|
2713
|
+
"""
|
|
2714
|
+
# Trivial cases:
|
|
2715
|
+
if self.is_trivial_zero():
|
|
2716
|
+
return -other
|
|
2717
|
+
if other.is_trivial_zero():
|
|
2718
|
+
return self
|
|
2719
|
+
if self is other:
|
|
2720
|
+
return self.parent().zero()
|
|
2721
|
+
# Generic case:
|
|
2722
|
+
com_charts = self.common_charts(other)
|
|
2723
|
+
if com_charts is None:
|
|
2724
|
+
raise ValueError("no common chart for the subtraction")
|
|
2725
|
+
result = type(self)(self.parent())
|
|
2726
|
+
for chart in com_charts:
|
|
2727
|
+
# ChartFunction subtraction:
|
|
2728
|
+
result._express[chart] = self._express[chart] - other._express[chart]
|
|
2729
|
+
if self._name is not None and other._name is not None:
|
|
2730
|
+
result._name = self._name + '-' + other._name
|
|
2731
|
+
if self._latex_name is not None and other._latex_name is not None:
|
|
2732
|
+
result._latex_name = self._latex_name + '-' + other._latex_name
|
|
2733
|
+
return result
|
|
2734
|
+
|
|
2735
|
+
def _mul_(self, other):
|
|
2736
|
+
r"""
|
|
2737
|
+
Scalar field multiplication.
|
|
2738
|
+
|
|
2739
|
+
INPUT:
|
|
2740
|
+
|
|
2741
|
+
- ``other`` -- a scalar field (in the same algebra as ``self``)
|
|
2742
|
+
|
|
2743
|
+
OUTPUT:
|
|
2744
|
+
|
|
2745
|
+
- the scalar field resulting from the multiplication of ``self`` by
|
|
2746
|
+
``other``
|
|
2747
|
+
|
|
2748
|
+
TESTS::
|
|
2749
|
+
|
|
2750
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2751
|
+
sage: X.<x,y> = M.chart()
|
|
2752
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2753
|
+
sage: g = M.scalar_field({X: x*y}, name='g')
|
|
2754
|
+
sage: s = f._mul_(g); s
|
|
2755
|
+
Scalar field f*g on the 2-dimensional topological manifold M
|
|
2756
|
+
sage: s.display()
|
|
2757
|
+
f*g: M → ℝ
|
|
2758
|
+
(x, y) ↦ x^2*y + x*y^2
|
|
2759
|
+
sage: s == f*g
|
|
2760
|
+
True
|
|
2761
|
+
sage: f._mul_(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
2762
|
+
True
|
|
2763
|
+
sage: f._mul_(M.one_scalar_field()) == f
|
|
2764
|
+
True
|
|
2765
|
+
"""
|
|
2766
|
+
# Trivial cases:
|
|
2767
|
+
if self.is_trivial_zero() or other.is_trivial_zero():
|
|
2768
|
+
return self._domain.zero_scalar_field()
|
|
2769
|
+
if self.is_trivial_one():
|
|
2770
|
+
return other
|
|
2771
|
+
if other.is_trivial_one():
|
|
2772
|
+
return self
|
|
2773
|
+
# Generic case:
|
|
2774
|
+
from sage.tensor.modules.format_utilities import (
|
|
2775
|
+
format_mul_latex,
|
|
2776
|
+
format_mul_txt,
|
|
2777
|
+
)
|
|
2778
|
+
com_charts = self.common_charts(other)
|
|
2779
|
+
if com_charts is None:
|
|
2780
|
+
raise ValueError("no common chart for the multiplication")
|
|
2781
|
+
result = type(self)(self.parent())
|
|
2782
|
+
for chart in com_charts:
|
|
2783
|
+
# ChartFunction multiplication:
|
|
2784
|
+
result._express[chart] = self._express[chart] * other._express[chart]
|
|
2785
|
+
result._name = format_mul_txt(self._name, '*', other._name)
|
|
2786
|
+
result._latex_name = format_mul_latex(self._latex_name, r' \cdot ',
|
|
2787
|
+
other._latex_name)
|
|
2788
|
+
return result
|
|
2789
|
+
|
|
2790
|
+
def _div_(self, other):
|
|
2791
|
+
r"""
|
|
2792
|
+
Scalar field division.
|
|
2793
|
+
|
|
2794
|
+
INPUT:
|
|
2795
|
+
|
|
2796
|
+
- ``other`` -- a scalar field (in the same algebra as self)
|
|
2797
|
+
|
|
2798
|
+
OUTPUT:
|
|
2799
|
+
|
|
2800
|
+
- the scalar field resulting from the division of ``self`` by
|
|
2801
|
+
``other``
|
|
2802
|
+
|
|
2803
|
+
TESTS::
|
|
2804
|
+
|
|
2805
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2806
|
+
sage: X.<x,y> = M.chart()
|
|
2807
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2808
|
+
sage: g = M.scalar_field({X: x*y}, name='g')
|
|
2809
|
+
sage: s = f._div_(g); s
|
|
2810
|
+
Scalar field f/g on the 2-dimensional topological manifold M
|
|
2811
|
+
sage: s.display()
|
|
2812
|
+
f/g: M → ℝ
|
|
2813
|
+
(x, y) ↦ (x + y)/(x*y)
|
|
2814
|
+
sage: s == f/g
|
|
2815
|
+
True
|
|
2816
|
+
sage: f._div_(M.zero_scalar_field())
|
|
2817
|
+
Traceback (most recent call last):
|
|
2818
|
+
...
|
|
2819
|
+
ZeroDivisionError: division of a scalar field by zero
|
|
2820
|
+
"""
|
|
2821
|
+
from sage.tensor.modules.format_utilities import (
|
|
2822
|
+
format_mul_latex,
|
|
2823
|
+
format_mul_txt,
|
|
2824
|
+
)
|
|
2825
|
+
# Trivial cases:
|
|
2826
|
+
if other.is_trivial_zero():
|
|
2827
|
+
raise ZeroDivisionError("division of a scalar field by zero")
|
|
2828
|
+
if self.is_trivial_zero():
|
|
2829
|
+
return self._domain.zero_scalar_field()
|
|
2830
|
+
# Generic case:
|
|
2831
|
+
com_charts = self.common_charts(other)
|
|
2832
|
+
if com_charts is None:
|
|
2833
|
+
raise ValueError("no common chart for the division")
|
|
2834
|
+
result = type(self)(self.parent())
|
|
2835
|
+
for chart in com_charts:
|
|
2836
|
+
# ChartFunction division:
|
|
2837
|
+
result._express[chart] = self._express[chart] / other._express[chart]
|
|
2838
|
+
result._name = format_mul_txt(self._name, '/', other._name)
|
|
2839
|
+
result._latex_name = format_mul_latex(self._latex_name, '/',
|
|
2840
|
+
other._latex_name)
|
|
2841
|
+
return result
|
|
2842
|
+
|
|
2843
|
+
def _lmul_(self, number):
|
|
2844
|
+
r"""
|
|
2845
|
+
Scalar multiplication operator: return ``number * self`` or
|
|
2846
|
+
``self * number``.
|
|
2847
|
+
|
|
2848
|
+
This differs from ``_mul_(self, other)`` by the fact that ``number``
|
|
2849
|
+
is not assumed to be a scalar field defined on the same domain as
|
|
2850
|
+
``self``, contrary to ``other`` in ``_mul_(self, other)``. In
|
|
2851
|
+
practice, ``number`` is an element of the field on which the
|
|
2852
|
+
scalar field algebra is defined.
|
|
2853
|
+
|
|
2854
|
+
INPUT:
|
|
2855
|
+
|
|
2856
|
+
- ``number`` -- an element of the ring on which the scalar field
|
|
2857
|
+
algebra is defined; this should be an element of the topological
|
|
2858
|
+
field on which the manifold is constructed (possibly represented
|
|
2859
|
+
by a symbolic expression)
|
|
2860
|
+
|
|
2861
|
+
OUTPUT: the scalar field ``number * self``
|
|
2862
|
+
|
|
2863
|
+
TESTS::
|
|
2864
|
+
|
|
2865
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2866
|
+
sage: X.<x,y> = M.chart()
|
|
2867
|
+
sage: f = M.scalar_field({X: x+y}, name='f')
|
|
2868
|
+
sage: s = f._lmul_(2); s
|
|
2869
|
+
Scalar field on the 2-dimensional topological manifold M
|
|
2870
|
+
sage: s.display()
|
|
2871
|
+
M → ℝ
|
|
2872
|
+
(x, y) ↦ 2*x + 2*y
|
|
2873
|
+
sage: s == 2 * f
|
|
2874
|
+
True
|
|
2875
|
+
sage: s == f * 2
|
|
2876
|
+
True
|
|
2877
|
+
sage: f._lmul_(pi).display()
|
|
2878
|
+
M → ℝ
|
|
2879
|
+
(x, y) ↦ pi*(x + y)
|
|
2880
|
+
sage: f._lmul_(pi) == pi*f
|
|
2881
|
+
True
|
|
2882
|
+
sage: f._lmul_(0) == M.zero_scalar_field()
|
|
2883
|
+
True
|
|
2884
|
+
sage: f._lmul_(1) == f
|
|
2885
|
+
True
|
|
2886
|
+
"""
|
|
2887
|
+
# Trivial cases:
|
|
2888
|
+
try:
|
|
2889
|
+
if number.is_trivial_zero():
|
|
2890
|
+
return self.parent().zero()
|
|
2891
|
+
if (number - 1).is_trivial_zero():
|
|
2892
|
+
return self
|
|
2893
|
+
except AttributeError:
|
|
2894
|
+
# in case base ring is not SR:
|
|
2895
|
+
if number == 0:
|
|
2896
|
+
return self.parent().zero()
|
|
2897
|
+
if number == 1:
|
|
2898
|
+
return self
|
|
2899
|
+
# Generic case:
|
|
2900
|
+
result = type(self)(self.parent())
|
|
2901
|
+
if isinstance(number, Expression):
|
|
2902
|
+
var = number.variables() # possible symbolic variables in number
|
|
2903
|
+
if var:
|
|
2904
|
+
# There are symbolic variables in number
|
|
2905
|
+
# Are any of them a chart coordinate ?
|
|
2906
|
+
chart_var = False
|
|
2907
|
+
for chart in self._express:
|
|
2908
|
+
if any(s in chart[:] for s in var):
|
|
2909
|
+
chart_var = True
|
|
2910
|
+
break
|
|
2911
|
+
if chart_var:
|
|
2912
|
+
# Some symbolic variables in number are chart coordinates
|
|
2913
|
+
for chart, expr in self._express.items():
|
|
2914
|
+
# The multiplication is performed only if
|
|
2915
|
+
# either
|
|
2916
|
+
# (i) all the symbolic variables in number are
|
|
2917
|
+
# coordinates of this chart
|
|
2918
|
+
# or (ii) no symbolic variable in number belongs to a
|
|
2919
|
+
# different chart
|
|
2920
|
+
chart_coords = chart[:]
|
|
2921
|
+
var_not_in_chart = [s for s in var
|
|
2922
|
+
if s not in chart_coords]
|
|
2923
|
+
any_in_other_chart = False
|
|
2924
|
+
if var_not_in_chart:
|
|
2925
|
+
for other_chart in self._domain.atlas():
|
|
2926
|
+
other_chart_coords = other_chart[:]
|
|
2927
|
+
for s in var_not_in_chart:
|
|
2928
|
+
if s in other_chart_coords:
|
|
2929
|
+
any_in_other_chart = True
|
|
2930
|
+
break
|
|
2931
|
+
if any_in_other_chart:
|
|
2932
|
+
break
|
|
2933
|
+
if not any_in_other_chart:
|
|
2934
|
+
result._express[chart] = number * expr
|
|
2935
|
+
return result
|
|
2936
|
+
# General case: the multiplication is performed on all charts:
|
|
2937
|
+
for chart, expr in self._express.items():
|
|
2938
|
+
result._express[chart] = number * expr
|
|
2939
|
+
return result
|
|
2940
|
+
|
|
2941
|
+
# ### End of CommutativeAlgebraElement arithmetic operators ###
|
|
2942
|
+
|
|
2943
|
+
def _function_name(self, func, func_latex, parentheses=True):
|
|
2944
|
+
r"""
|
|
2945
|
+
Helper function to set the symbol of a function applied to the
|
|
2946
|
+
scalar field.
|
|
2947
|
+
|
|
2948
|
+
TESTS::
|
|
2949
|
+
|
|
2950
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2951
|
+
sage: X.<x,y> = M.chart()
|
|
2952
|
+
sage: f = M.scalar_field({X: x+y}, name='f', latex_name=r"\Phi")
|
|
2953
|
+
sage: f._function_name("cos", r"\cos")
|
|
2954
|
+
('cos(f)', '\\cos\\left(\\Phi\\right)')
|
|
2955
|
+
sage: f._function_name("sqrt", r"\sqrt", parentheses=False)
|
|
2956
|
+
('sqrt(f)', '\\sqrt{\\Phi}')
|
|
2957
|
+
sage: f = M.scalar_field({X: x+y}) # no name given to f
|
|
2958
|
+
sage: f._function_name("cos", r"\cos")
|
|
2959
|
+
(None, None)
|
|
2960
|
+
"""
|
|
2961
|
+
if self._name is None:
|
|
2962
|
+
name = None
|
|
2963
|
+
else:
|
|
2964
|
+
name = func + "(" + self._name + ")"
|
|
2965
|
+
if self._latex_name is None:
|
|
2966
|
+
latex_name = None
|
|
2967
|
+
elif parentheses:
|
|
2968
|
+
latex_name = func_latex + r"\left(" + self._latex_name + r"\right)"
|
|
2969
|
+
else:
|
|
2970
|
+
latex_name = func_latex + r"{" + self._latex_name + r"}"
|
|
2971
|
+
return name, latex_name
|
|
2972
|
+
|
|
2973
|
+
def exp(self):
|
|
2974
|
+
r"""
|
|
2975
|
+
Exponential of the scalar field.
|
|
2976
|
+
|
|
2977
|
+
OUTPUT: the scalar field `\exp f`, where `f` is the current scalar field
|
|
2978
|
+
|
|
2979
|
+
EXAMPLES::
|
|
2980
|
+
|
|
2981
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
2982
|
+
sage: X.<x,y> = M.chart()
|
|
2983
|
+
sage: f = M.scalar_field({X: x+y}, name='f', latex_name=r"\Phi")
|
|
2984
|
+
sage: g = exp(f) ; g
|
|
2985
|
+
Scalar field exp(f) on the 2-dimensional topological manifold M
|
|
2986
|
+
sage: g.display()
|
|
2987
|
+
exp(f): M → ℝ
|
|
2988
|
+
(x, y) ↦ e^(x + y)
|
|
2989
|
+
sage: latex(g)
|
|
2990
|
+
\exp\left(\Phi\right)
|
|
2991
|
+
|
|
2992
|
+
Automatic simplifications occur::
|
|
2993
|
+
|
|
2994
|
+
sage: f = M.scalar_field({X: 2*ln(1+x^2)}, name='f')
|
|
2995
|
+
sage: exp(f).display()
|
|
2996
|
+
exp(f): M → ℝ
|
|
2997
|
+
(x, y) ↦ x^4 + 2*x^2 + 1
|
|
2998
|
+
|
|
2999
|
+
The inverse function is :meth:`log`::
|
|
3000
|
+
|
|
3001
|
+
sage: log(exp(f)) == f
|
|
3002
|
+
True
|
|
3003
|
+
|
|
3004
|
+
Some tests::
|
|
3005
|
+
|
|
3006
|
+
sage: exp(M.zero_scalar_field()) == M.constant_scalar_field(1)
|
|
3007
|
+
True
|
|
3008
|
+
sage: exp(M.constant_scalar_field(1)) == M.constant_scalar_field(e)
|
|
3009
|
+
True
|
|
3010
|
+
"""
|
|
3011
|
+
name, latex_name = self._function_name("exp", r"\exp")
|
|
3012
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3013
|
+
for chart, func in self._express.items():
|
|
3014
|
+
resu._express[chart] = func.exp()
|
|
3015
|
+
return resu
|
|
3016
|
+
|
|
3017
|
+
def log(self):
|
|
3018
|
+
r"""
|
|
3019
|
+
Natural logarithm of the scalar field.
|
|
3020
|
+
|
|
3021
|
+
OUTPUT: the scalar field `\ln f`, where `f` is the current scalar field
|
|
3022
|
+
|
|
3023
|
+
EXAMPLES::
|
|
3024
|
+
|
|
3025
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3026
|
+
sage: X.<x,y> = M.chart()
|
|
3027
|
+
sage: f = M.scalar_field({X: x+y}, name='f', latex_name=r"\Phi")
|
|
3028
|
+
sage: g = log(f) ; g
|
|
3029
|
+
Scalar field ln(f) on the 2-dimensional topological manifold M
|
|
3030
|
+
sage: g.display()
|
|
3031
|
+
ln(f): M → ℝ
|
|
3032
|
+
(x, y) ↦ log(x + y)
|
|
3033
|
+
sage: latex(g)
|
|
3034
|
+
\ln\left(\Phi\right)
|
|
3035
|
+
|
|
3036
|
+
The inverse function is :meth:`exp`::
|
|
3037
|
+
|
|
3038
|
+
sage: exp(log(f)) == f
|
|
3039
|
+
True
|
|
3040
|
+
"""
|
|
3041
|
+
name, latex_name = self._function_name("ln", r"\ln")
|
|
3042
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3043
|
+
for chart, func in self._express.items():
|
|
3044
|
+
resu._express[chart] = func.log()
|
|
3045
|
+
return resu
|
|
3046
|
+
|
|
3047
|
+
def __pow__(self, exponent):
|
|
3048
|
+
r"""
|
|
3049
|
+
The scalar field to a given power.
|
|
3050
|
+
|
|
3051
|
+
INPUT:
|
|
3052
|
+
|
|
3053
|
+
- ``exponent`` -- the exponent
|
|
3054
|
+
|
|
3055
|
+
OUTPUT:
|
|
3056
|
+
|
|
3057
|
+
- the scalar field `f^a`, where `f` is the current scalar field and
|
|
3058
|
+
`a` the exponent
|
|
3059
|
+
|
|
3060
|
+
EXAMPLES::
|
|
3061
|
+
|
|
3062
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3063
|
+
sage: X.<x,y> = M.chart()
|
|
3064
|
+
sage: f = M.scalar_field({X: x+y}, name='f', latex_name=r'\Phi')
|
|
3065
|
+
sage: g = f.__pow__(pi) ; g
|
|
3066
|
+
Scalar field f^pi on the 2-dimensional topological manifold M
|
|
3067
|
+
sage: latex(g)
|
|
3068
|
+
{\Phi}^{ \pi }
|
|
3069
|
+
sage: g.display()
|
|
3070
|
+
f^pi: M → ℝ
|
|
3071
|
+
(x, y) ↦ (x + y)^pi
|
|
3072
|
+
|
|
3073
|
+
The global function ``pow`` can be used::
|
|
3074
|
+
|
|
3075
|
+
sage: pow(f, pi) == f.__pow__(pi)
|
|
3076
|
+
True
|
|
3077
|
+
|
|
3078
|
+
as well as the exponent notation::
|
|
3079
|
+
|
|
3080
|
+
sage: f^pi == f.__pow__(pi)
|
|
3081
|
+
True
|
|
3082
|
+
|
|
3083
|
+
Some checks::
|
|
3084
|
+
|
|
3085
|
+
sage: pow(f, 2) == f*f
|
|
3086
|
+
True
|
|
3087
|
+
sage: pow(pow(f, 1/2), 2) == f
|
|
3088
|
+
True
|
|
3089
|
+
"""
|
|
3090
|
+
from sage.misc.latex import latex
|
|
3091
|
+
if self._name is None:
|
|
3092
|
+
name = None
|
|
3093
|
+
else:
|
|
3094
|
+
name = self._name + "^{}".format(exponent)
|
|
3095
|
+
if self._latex_name is None:
|
|
3096
|
+
latex_name = None
|
|
3097
|
+
else:
|
|
3098
|
+
latex_name = r"{" + self._latex_name + r"}^{" + \
|
|
3099
|
+
latex(exponent) + r"}"
|
|
3100
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3101
|
+
for chart, func in self._express.items():
|
|
3102
|
+
resu._express[chart] = func.__pow__(exponent)
|
|
3103
|
+
return resu
|
|
3104
|
+
|
|
3105
|
+
def sqrt(self):
|
|
3106
|
+
r"""
|
|
3107
|
+
Square root of the scalar field.
|
|
3108
|
+
|
|
3109
|
+
OUTPUT: the scalar field `\sqrt f`, where `f` is the current scalar field
|
|
3110
|
+
|
|
3111
|
+
EXAMPLES::
|
|
3112
|
+
|
|
3113
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3114
|
+
sage: X.<x,y> = M.chart()
|
|
3115
|
+
sage: f = M.scalar_field({X: 1+x^2+y^2}, name='f',
|
|
3116
|
+
....: latex_name=r"\Phi")
|
|
3117
|
+
sage: g = sqrt(f) ; g
|
|
3118
|
+
Scalar field sqrt(f) on the 2-dimensional topological manifold M
|
|
3119
|
+
sage: latex(g)
|
|
3120
|
+
\sqrt{\Phi}
|
|
3121
|
+
sage: g.display()
|
|
3122
|
+
sqrt(f): M → ℝ
|
|
3123
|
+
(x, y) ↦ sqrt(x^2 + y^2 + 1)
|
|
3124
|
+
|
|
3125
|
+
Some tests::
|
|
3126
|
+
|
|
3127
|
+
sage: g^2 == f
|
|
3128
|
+
True
|
|
3129
|
+
sage: sqrt(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3130
|
+
True
|
|
3131
|
+
"""
|
|
3132
|
+
name, latex_name = self._function_name("sqrt", r"\sqrt",
|
|
3133
|
+
parentheses=False)
|
|
3134
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3135
|
+
for chart, func in self._express.items():
|
|
3136
|
+
resu._express[chart] = func.sqrt()
|
|
3137
|
+
return resu
|
|
3138
|
+
|
|
3139
|
+
def cos(self):
|
|
3140
|
+
r"""
|
|
3141
|
+
Cosine of the scalar field.
|
|
3142
|
+
|
|
3143
|
+
OUTPUT: the scalar field `\cos f`, where `f` is the current scalar field
|
|
3144
|
+
|
|
3145
|
+
EXAMPLES::
|
|
3146
|
+
|
|
3147
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3148
|
+
sage: X.<x,y> = M.chart()
|
|
3149
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3150
|
+
sage: g = cos(f) ; g
|
|
3151
|
+
Scalar field cos(f) on the 2-dimensional topological manifold M
|
|
3152
|
+
sage: latex(g)
|
|
3153
|
+
\cos\left(\Phi\right)
|
|
3154
|
+
sage: g.display()
|
|
3155
|
+
cos(f): M → ℝ
|
|
3156
|
+
(x, y) ↦ cos(x*y)
|
|
3157
|
+
|
|
3158
|
+
Some tests::
|
|
3159
|
+
|
|
3160
|
+
sage: cos(M.zero_scalar_field()) == M.constant_scalar_field(1)
|
|
3161
|
+
True
|
|
3162
|
+
sage: cos(M.constant_scalar_field(pi/2)) == M.zero_scalar_field()
|
|
3163
|
+
True
|
|
3164
|
+
"""
|
|
3165
|
+
name, latex_name = self._function_name("cos", r"\cos")
|
|
3166
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3167
|
+
for chart, func in self._express.items():
|
|
3168
|
+
resu._express[chart] = func.cos()
|
|
3169
|
+
return resu
|
|
3170
|
+
|
|
3171
|
+
def sin(self):
|
|
3172
|
+
r"""
|
|
3173
|
+
Sine of the scalar field.
|
|
3174
|
+
|
|
3175
|
+
OUTPUT: the scalar field `\sin f`, where `f` is the current scalar field
|
|
3176
|
+
|
|
3177
|
+
EXAMPLES::
|
|
3178
|
+
|
|
3179
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3180
|
+
sage: X.<x,y> = M.chart()
|
|
3181
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3182
|
+
sage: g = sin(f) ; g
|
|
3183
|
+
Scalar field sin(f) on the 2-dimensional topological manifold M
|
|
3184
|
+
sage: latex(g)
|
|
3185
|
+
\sin\left(\Phi\right)
|
|
3186
|
+
sage: g.display()
|
|
3187
|
+
sin(f): M → ℝ
|
|
3188
|
+
(x, y) ↦ sin(x*y)
|
|
3189
|
+
|
|
3190
|
+
Some tests::
|
|
3191
|
+
|
|
3192
|
+
sage: sin(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3193
|
+
True
|
|
3194
|
+
sage: sin(M.constant_scalar_field(pi/2)) == M.constant_scalar_field(1)
|
|
3195
|
+
True
|
|
3196
|
+
"""
|
|
3197
|
+
name, latex_name = self._function_name("sin", r"\sin")
|
|
3198
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3199
|
+
for chart, func in self._express.items():
|
|
3200
|
+
resu._express[chart] = func.sin()
|
|
3201
|
+
return resu
|
|
3202
|
+
|
|
3203
|
+
def tan(self):
|
|
3204
|
+
r"""
|
|
3205
|
+
Tangent of the scalar field.
|
|
3206
|
+
|
|
3207
|
+
OUTPUT: the scalar field `\tan f`, where `f` is the current scalar field
|
|
3208
|
+
|
|
3209
|
+
EXAMPLES::
|
|
3210
|
+
|
|
3211
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3212
|
+
sage: X.<x,y> = M.chart()
|
|
3213
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3214
|
+
sage: g = tan(f) ; g
|
|
3215
|
+
Scalar field tan(f) on the 2-dimensional topological manifold M
|
|
3216
|
+
sage: latex(g)
|
|
3217
|
+
\tan\left(\Phi\right)
|
|
3218
|
+
sage: g.display()
|
|
3219
|
+
tan(f): M → ℝ
|
|
3220
|
+
(x, y) ↦ sin(x*y)/cos(x*y)
|
|
3221
|
+
|
|
3222
|
+
Some tests::
|
|
3223
|
+
|
|
3224
|
+
sage: tan(f) == sin(f) / cos(f)
|
|
3225
|
+
True
|
|
3226
|
+
sage: tan(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3227
|
+
True
|
|
3228
|
+
sage: tan(M.constant_scalar_field(pi/4)) == M.constant_scalar_field(1)
|
|
3229
|
+
True
|
|
3230
|
+
"""
|
|
3231
|
+
name, latex_name = self._function_name("tan", r"\tan")
|
|
3232
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3233
|
+
for chart, func in self._express.items():
|
|
3234
|
+
resu._express[chart] = func.tan()
|
|
3235
|
+
return resu
|
|
3236
|
+
|
|
3237
|
+
def arccos(self):
|
|
3238
|
+
r"""
|
|
3239
|
+
Arc cosine of the scalar field.
|
|
3240
|
+
|
|
3241
|
+
OUTPUT: the scalar field `\arccos f`, where `f` is the current scalar field
|
|
3242
|
+
|
|
3243
|
+
EXAMPLES::
|
|
3244
|
+
|
|
3245
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3246
|
+
sage: X.<x,y> = M.chart()
|
|
3247
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3248
|
+
sage: g = arccos(f) ; g
|
|
3249
|
+
Scalar field arccos(f) on the 2-dimensional topological manifold M
|
|
3250
|
+
sage: latex(g)
|
|
3251
|
+
\arccos\left(\Phi\right)
|
|
3252
|
+
sage: g.display()
|
|
3253
|
+
arccos(f): M → ℝ
|
|
3254
|
+
(x, y) ↦ arccos(x*y)
|
|
3255
|
+
|
|
3256
|
+
The notation ``acos`` can be used as well::
|
|
3257
|
+
|
|
3258
|
+
sage: acos(f)
|
|
3259
|
+
Scalar field arccos(f) on the 2-dimensional topological manifold M
|
|
3260
|
+
sage: acos(f) == g
|
|
3261
|
+
True
|
|
3262
|
+
|
|
3263
|
+
Some tests::
|
|
3264
|
+
|
|
3265
|
+
sage: cos(g) == f
|
|
3266
|
+
True
|
|
3267
|
+
sage: arccos(M.constant_scalar_field(1)) == M.zero_scalar_field()
|
|
3268
|
+
True
|
|
3269
|
+
sage: arccos(M.zero_scalar_field()) == M.constant_scalar_field(pi/2)
|
|
3270
|
+
True
|
|
3271
|
+
"""
|
|
3272
|
+
name, latex_name = self._function_name("arccos", r"\arccos")
|
|
3273
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3274
|
+
for chart, func in self._express.items():
|
|
3275
|
+
resu._express[chart] = func.arccos()
|
|
3276
|
+
return resu
|
|
3277
|
+
|
|
3278
|
+
def arcsin(self):
|
|
3279
|
+
r"""
|
|
3280
|
+
Arc sine of the scalar field.
|
|
3281
|
+
|
|
3282
|
+
OUTPUT: the scalar field `\arcsin f`, where `f` is the current scalar field
|
|
3283
|
+
|
|
3284
|
+
EXAMPLES::
|
|
3285
|
+
|
|
3286
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3287
|
+
sage: X.<x,y> = M.chart()
|
|
3288
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3289
|
+
sage: g = arcsin(f) ; g
|
|
3290
|
+
Scalar field arcsin(f) on the 2-dimensional topological manifold M
|
|
3291
|
+
sage: latex(g)
|
|
3292
|
+
\arcsin\left(\Phi\right)
|
|
3293
|
+
sage: g.display()
|
|
3294
|
+
arcsin(f): M → ℝ
|
|
3295
|
+
(x, y) ↦ arcsin(x*y)
|
|
3296
|
+
|
|
3297
|
+
The notation ``asin`` can be used as well::
|
|
3298
|
+
|
|
3299
|
+
sage: asin(f)
|
|
3300
|
+
Scalar field arcsin(f) on the 2-dimensional topological manifold M
|
|
3301
|
+
sage: asin(f) == g
|
|
3302
|
+
True
|
|
3303
|
+
|
|
3304
|
+
Some tests::
|
|
3305
|
+
|
|
3306
|
+
sage: sin(g) == f
|
|
3307
|
+
True
|
|
3308
|
+
sage: arcsin(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3309
|
+
True
|
|
3310
|
+
sage: arcsin(M.constant_scalar_field(1)) == M.constant_scalar_field(pi/2)
|
|
3311
|
+
True
|
|
3312
|
+
"""
|
|
3313
|
+
name, latex_name = self._function_name("arcsin", r"\arcsin")
|
|
3314
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3315
|
+
for chart, func in self._express.items():
|
|
3316
|
+
resu._express[chart] = func.arcsin()
|
|
3317
|
+
return resu
|
|
3318
|
+
|
|
3319
|
+
def arctan(self):
|
|
3320
|
+
r"""
|
|
3321
|
+
Arc tangent of the scalar field.
|
|
3322
|
+
|
|
3323
|
+
OUTPUT: the scalar field `\arctan f`, where `f` is the current scalar field
|
|
3324
|
+
|
|
3325
|
+
EXAMPLES::
|
|
3326
|
+
|
|
3327
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3328
|
+
sage: X.<x,y> = M.chart()
|
|
3329
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3330
|
+
sage: g = arctan(f) ; g
|
|
3331
|
+
Scalar field arctan(f) on the 2-dimensional topological manifold M
|
|
3332
|
+
sage: latex(g)
|
|
3333
|
+
\arctan\left(\Phi\right)
|
|
3334
|
+
sage: g.display()
|
|
3335
|
+
arctan(f): M → ℝ
|
|
3336
|
+
(x, y) ↦ arctan(x*y)
|
|
3337
|
+
|
|
3338
|
+
The notation ``atan`` can be used as well::
|
|
3339
|
+
|
|
3340
|
+
sage: atan(f)
|
|
3341
|
+
Scalar field arctan(f) on the 2-dimensional topological manifold M
|
|
3342
|
+
sage: atan(f) == g
|
|
3343
|
+
True
|
|
3344
|
+
|
|
3345
|
+
Some tests::
|
|
3346
|
+
|
|
3347
|
+
sage: tan(g) == f
|
|
3348
|
+
True
|
|
3349
|
+
sage: arctan(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3350
|
+
True
|
|
3351
|
+
sage: arctan(M.constant_scalar_field(1)) == M.constant_scalar_field(pi/4)
|
|
3352
|
+
True
|
|
3353
|
+
"""
|
|
3354
|
+
name, latex_name = self._function_name("arctan", r"\arctan")
|
|
3355
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3356
|
+
for chart, func in self._express.items():
|
|
3357
|
+
resu._express[chart] = func.arctan()
|
|
3358
|
+
return resu
|
|
3359
|
+
|
|
3360
|
+
def cosh(self):
|
|
3361
|
+
r"""
|
|
3362
|
+
Hyperbolic cosine of the scalar field.
|
|
3363
|
+
|
|
3364
|
+
OUTPUT: the scalar field `\cosh f`, where `f` is the current scalar field
|
|
3365
|
+
|
|
3366
|
+
EXAMPLES::
|
|
3367
|
+
|
|
3368
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3369
|
+
sage: X.<x,y> = M.chart()
|
|
3370
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3371
|
+
sage: g = cosh(f) ; g
|
|
3372
|
+
Scalar field cosh(f) on the 2-dimensional topological manifold M
|
|
3373
|
+
sage: latex(g)
|
|
3374
|
+
\cosh\left(\Phi\right)
|
|
3375
|
+
sage: g.display()
|
|
3376
|
+
cosh(f): M → ℝ
|
|
3377
|
+
(x, y) ↦ cosh(x*y)
|
|
3378
|
+
|
|
3379
|
+
Some test::
|
|
3380
|
+
|
|
3381
|
+
sage: cosh(M.zero_scalar_field()) == M.constant_scalar_field(1)
|
|
3382
|
+
True
|
|
3383
|
+
"""
|
|
3384
|
+
name, latex_name = self._function_name("cosh", r"\cosh")
|
|
3385
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3386
|
+
for chart, func in self._express.items():
|
|
3387
|
+
resu._express[chart] = func.cosh()
|
|
3388
|
+
return resu
|
|
3389
|
+
|
|
3390
|
+
def sinh(self):
|
|
3391
|
+
r"""
|
|
3392
|
+
Hyperbolic sine of the scalar field.
|
|
3393
|
+
|
|
3394
|
+
OUTPUT: the scalar field `\sinh f`, where `f` is the current scalar field
|
|
3395
|
+
|
|
3396
|
+
EXAMPLES::
|
|
3397
|
+
|
|
3398
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3399
|
+
sage: X.<x,y> = M.chart()
|
|
3400
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3401
|
+
sage: g = sinh(f) ; g
|
|
3402
|
+
Scalar field sinh(f) on the 2-dimensional topological manifold M
|
|
3403
|
+
sage: latex(g)
|
|
3404
|
+
\sinh\left(\Phi\right)
|
|
3405
|
+
sage: g.display()
|
|
3406
|
+
sinh(f): M → ℝ
|
|
3407
|
+
(x, y) ↦ sinh(x*y)
|
|
3408
|
+
|
|
3409
|
+
Some test::
|
|
3410
|
+
|
|
3411
|
+
sage: sinh(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3412
|
+
True
|
|
3413
|
+
"""
|
|
3414
|
+
name, latex_name = self._function_name("sinh", r"\sinh")
|
|
3415
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3416
|
+
for chart, func in self._express.items():
|
|
3417
|
+
resu._express[chart] = func.sinh()
|
|
3418
|
+
return resu
|
|
3419
|
+
|
|
3420
|
+
def tanh(self):
|
|
3421
|
+
r"""
|
|
3422
|
+
Hyperbolic tangent of the scalar field.
|
|
3423
|
+
|
|
3424
|
+
OUTPUT: the scalar field `\tanh f`, where `f` is the current scalar field
|
|
3425
|
+
|
|
3426
|
+
EXAMPLES::
|
|
3427
|
+
|
|
3428
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3429
|
+
sage: X.<x,y> = M.chart()
|
|
3430
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3431
|
+
sage: g = tanh(f) ; g
|
|
3432
|
+
Scalar field tanh(f) on the 2-dimensional topological manifold M
|
|
3433
|
+
sage: latex(g)
|
|
3434
|
+
\tanh\left(\Phi\right)
|
|
3435
|
+
sage: g.display()
|
|
3436
|
+
tanh(f): M → ℝ
|
|
3437
|
+
(x, y) ↦ sinh(x*y)/cosh(x*y)
|
|
3438
|
+
|
|
3439
|
+
Some tests::
|
|
3440
|
+
|
|
3441
|
+
sage: tanh(f) == sinh(f) / cosh(f)
|
|
3442
|
+
True
|
|
3443
|
+
sage: tanh(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3444
|
+
True
|
|
3445
|
+
"""
|
|
3446
|
+
name, latex_name = self._function_name("tanh", r"\tanh")
|
|
3447
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3448
|
+
for chart, func in self._express.items():
|
|
3449
|
+
resu._express[chart] = func.tanh()
|
|
3450
|
+
return resu
|
|
3451
|
+
|
|
3452
|
+
def arccosh(self):
|
|
3453
|
+
r"""
|
|
3454
|
+
Inverse hyperbolic cosine of the scalar field.
|
|
3455
|
+
|
|
3456
|
+
OUTPUT:
|
|
3457
|
+
|
|
3458
|
+
- the scalar field `\mathrm{arccosh}\, f`, where `f` is the current
|
|
3459
|
+
scalar field
|
|
3460
|
+
|
|
3461
|
+
EXAMPLES::
|
|
3462
|
+
|
|
3463
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3464
|
+
sage: X.<x,y> = M.chart()
|
|
3465
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3466
|
+
sage: g = arccosh(f) ; g
|
|
3467
|
+
Scalar field arccosh(f) on the 2-dimensional topological manifold M
|
|
3468
|
+
sage: latex(g)
|
|
3469
|
+
\,\mathrm{arccosh}\left(\Phi\right)
|
|
3470
|
+
sage: g.display()
|
|
3471
|
+
arccosh(f): M → ℝ
|
|
3472
|
+
(x, y) ↦ arccosh(x*y)
|
|
3473
|
+
|
|
3474
|
+
The notation ``acosh`` can be used as well::
|
|
3475
|
+
|
|
3476
|
+
sage: acosh(f)
|
|
3477
|
+
Scalar field arccosh(f) on the 2-dimensional topological manifold M
|
|
3478
|
+
sage: acosh(f) == g
|
|
3479
|
+
True
|
|
3480
|
+
|
|
3481
|
+
Some tests::
|
|
3482
|
+
|
|
3483
|
+
sage: cosh(g) == f
|
|
3484
|
+
True
|
|
3485
|
+
sage: arccosh(M.constant_scalar_field(1)) == M.zero_scalar_field()
|
|
3486
|
+
True
|
|
3487
|
+
"""
|
|
3488
|
+
name, latex_name = self._function_name("arccosh", r"\,\mathrm{arccosh}")
|
|
3489
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3490
|
+
for chart, func in self._express.items():
|
|
3491
|
+
resu._express[chart] = func.arccosh()
|
|
3492
|
+
return resu
|
|
3493
|
+
|
|
3494
|
+
def arcsinh(self):
|
|
3495
|
+
r"""
|
|
3496
|
+
Inverse hyperbolic sine of the scalar field.
|
|
3497
|
+
|
|
3498
|
+
OUTPUT:
|
|
3499
|
+
|
|
3500
|
+
- the scalar field `\mathrm{arcsinh}\, f`, where `f` is the current
|
|
3501
|
+
scalar field
|
|
3502
|
+
|
|
3503
|
+
EXAMPLES::
|
|
3504
|
+
|
|
3505
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3506
|
+
sage: X.<x,y> = M.chart()
|
|
3507
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3508
|
+
sage: g = arcsinh(f) ; g
|
|
3509
|
+
Scalar field arcsinh(f) on the 2-dimensional topological manifold M
|
|
3510
|
+
sage: latex(g)
|
|
3511
|
+
\,\mathrm{arcsinh}\left(\Phi\right)
|
|
3512
|
+
sage: g.display()
|
|
3513
|
+
arcsinh(f): M → ℝ
|
|
3514
|
+
(x, y) ↦ arcsinh(x*y)
|
|
3515
|
+
|
|
3516
|
+
The notation ``asinh`` can be used as well::
|
|
3517
|
+
|
|
3518
|
+
sage: asinh(f)
|
|
3519
|
+
Scalar field arcsinh(f) on the 2-dimensional topological manifold M
|
|
3520
|
+
sage: asinh(f) == g
|
|
3521
|
+
True
|
|
3522
|
+
|
|
3523
|
+
Some tests::
|
|
3524
|
+
|
|
3525
|
+
sage: sinh(g) == f
|
|
3526
|
+
True
|
|
3527
|
+
sage: arcsinh(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3528
|
+
True
|
|
3529
|
+
"""
|
|
3530
|
+
name, latex_name = self._function_name("arcsinh", r"\,\mathrm{arcsinh}")
|
|
3531
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3532
|
+
for chart, func in self._express.items():
|
|
3533
|
+
resu._express[chart] = func.arcsinh()
|
|
3534
|
+
return resu
|
|
3535
|
+
|
|
3536
|
+
def arctanh(self):
|
|
3537
|
+
r"""
|
|
3538
|
+
Inverse hyperbolic tangent of the scalar field.
|
|
3539
|
+
|
|
3540
|
+
OUTPUT:
|
|
3541
|
+
|
|
3542
|
+
- the scalar field `\mathrm{arctanh}\, f`, where `f` is the current
|
|
3543
|
+
scalar field
|
|
3544
|
+
|
|
3545
|
+
EXAMPLES::
|
|
3546
|
+
|
|
3547
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3548
|
+
sage: X.<x,y> = M.chart()
|
|
3549
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3550
|
+
sage: g = arctanh(f) ; g
|
|
3551
|
+
Scalar field arctanh(f) on the 2-dimensional topological manifold M
|
|
3552
|
+
sage: latex(g)
|
|
3553
|
+
\,\mathrm{arctanh}\left(\Phi\right)
|
|
3554
|
+
sage: g.display()
|
|
3555
|
+
arctanh(f): M → ℝ
|
|
3556
|
+
(x, y) ↦ arctanh(x*y)
|
|
3557
|
+
|
|
3558
|
+
The notation ``atanh`` can be used as well::
|
|
3559
|
+
|
|
3560
|
+
sage: atanh(f)
|
|
3561
|
+
Scalar field arctanh(f) on the 2-dimensional topological manifold M
|
|
3562
|
+
sage: atanh(f) == g
|
|
3563
|
+
True
|
|
3564
|
+
|
|
3565
|
+
Some tests::
|
|
3566
|
+
|
|
3567
|
+
sage: tanh(g) == f
|
|
3568
|
+
True
|
|
3569
|
+
sage: arctanh(M.zero_scalar_field()) == M.zero_scalar_field()
|
|
3570
|
+
True
|
|
3571
|
+
sage: arctanh(M.constant_scalar_field(1/2)) == M.constant_scalar_field(log(3)/2)
|
|
3572
|
+
True
|
|
3573
|
+
"""
|
|
3574
|
+
name, latex_name = self._function_name("arctanh", r"\,\mathrm{arctanh}")
|
|
3575
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3576
|
+
for chart, func in self._express.items():
|
|
3577
|
+
resu._express[chart] = func.arctanh()
|
|
3578
|
+
return resu
|
|
3579
|
+
|
|
3580
|
+
def __abs__(self):
|
|
3581
|
+
r"""
|
|
3582
|
+
Absolute value of the scalar field.
|
|
3583
|
+
|
|
3584
|
+
OUTPUT:
|
|
3585
|
+
|
|
3586
|
+
- the scalar field `\mathrm{Abs}\, f`, where `f` is the current
|
|
3587
|
+
scalar field
|
|
3588
|
+
|
|
3589
|
+
EXAMPLES::
|
|
3590
|
+
|
|
3591
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3592
|
+
sage: X.<x,y> = M.chart()
|
|
3593
|
+
sage: f = M.scalar_field({X: x*y}, name='f', latex_name=r"\Phi")
|
|
3594
|
+
sage: g = abs(f) ; g
|
|
3595
|
+
Scalar field abs(f) on the 2-dimensional topological manifold M
|
|
3596
|
+
sage: latex(g)
|
|
3597
|
+
\,\mathrm{abs}\left(\Phi\right)
|
|
3598
|
+
sage: g.display()
|
|
3599
|
+
abs(f): M → ℝ
|
|
3600
|
+
(x, y) ↦ abs(x)*abs(y)
|
|
3601
|
+
"""
|
|
3602
|
+
name, latex_name = self._function_name("abs", r"\,\mathrm{abs}")
|
|
3603
|
+
resu = type(self)(self.parent(), name=name, latex_name=latex_name)
|
|
3604
|
+
for chart, func in self._express.items():
|
|
3605
|
+
resu._express[chart] = func.abs()
|
|
3606
|
+
return resu
|
|
3607
|
+
|
|
3608
|
+
def set_calc_order(self, symbol, order, truncate=False):
|
|
3609
|
+
r"""
|
|
3610
|
+
Trigger a power series expansion with respect to a small parameter in
|
|
3611
|
+
computations involving the scalar field.
|
|
3612
|
+
|
|
3613
|
+
This property is propagated by usual operations. The internal
|
|
3614
|
+
representation must be ``SR`` for this to take effect.
|
|
3615
|
+
|
|
3616
|
+
If the small parameter is `\epsilon` and `f` is ``self``, the
|
|
3617
|
+
power series expansion to order `n` is
|
|
3618
|
+
|
|
3619
|
+
.. MATH::
|
|
3620
|
+
|
|
3621
|
+
f = f_0 + \epsilon f_1 + \epsilon^2 f_2 + \cdots + \epsilon^n f_n
|
|
3622
|
+
+ O(\epsilon^{n+1}),
|
|
3623
|
+
|
|
3624
|
+
where `f_0, f_1, \ldots, f_n` are `n+1` scalar fields that do not
|
|
3625
|
+
depend upon `\epsilon`.
|
|
3626
|
+
|
|
3627
|
+
INPUT:
|
|
3628
|
+
|
|
3629
|
+
- ``symbol`` -- symbolic variable (the "small parameter" `\epsilon`)
|
|
3630
|
+
with respect to which the coordinate expressions of ``self`` in
|
|
3631
|
+
various charts are expanded in power series (around the zero value of
|
|
3632
|
+
this variable)
|
|
3633
|
+
- ``order`` -- integer; the order `n` of the expansion, defined as the
|
|
3634
|
+
degree of the polynomial representing the truncated power series in
|
|
3635
|
+
``symbol``
|
|
3636
|
+
|
|
3637
|
+
.. WARNING::
|
|
3638
|
+
|
|
3639
|
+
The order of the big `O` in the power series expansion is `n+1`,
|
|
3640
|
+
where `n` is ``order``.
|
|
3641
|
+
|
|
3642
|
+
- ``truncate`` -- boolean (default: ``False``); determines whether the
|
|
3643
|
+
coordinate expressions of ``self`` are replaced by their expansions
|
|
3644
|
+
to the given order
|
|
3645
|
+
|
|
3646
|
+
EXAMPLES::
|
|
3647
|
+
|
|
3648
|
+
sage: M = Manifold(2, 'M', structure='topological')
|
|
3649
|
+
sage: X.<x,y> = M.chart()
|
|
3650
|
+
sage: t = var('t') # the small parameter
|
|
3651
|
+
sage: f = M.scalar_field(exp(-t*x))
|
|
3652
|
+
sage: f.expr()
|
|
3653
|
+
e^(-t*x)
|
|
3654
|
+
sage: f.set_calc_order(t, 2, truncate=True)
|
|
3655
|
+
sage: f.expr()
|
|
3656
|
+
1/2*t^2*x^2 - t*x + 1
|
|
3657
|
+
"""
|
|
3658
|
+
for expr in self._express.values():
|
|
3659
|
+
expr._expansion_symbol = symbol
|
|
3660
|
+
expr._order = order
|
|
3661
|
+
if truncate:
|
|
3662
|
+
expr.simplify()
|
|
3663
|
+
self._del_derived()
|
|
3664
|
+
|
|
3665
|
+
def set_immutable(self):
|
|
3666
|
+
r"""
|
|
3667
|
+
Set ``self`` and all restrictions of ``self`` immutable.
|
|
3668
|
+
|
|
3669
|
+
EXAMPLES::
|
|
3670
|
+
|
|
3671
|
+
sage: M = Manifold(2, 'M')
|
|
3672
|
+
sage: X.<x,y> = M.chart()
|
|
3673
|
+
sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1}) # disk
|
|
3674
|
+
sage: V = M.open_subset('U', coord_def={X: x>0}) # half plane
|
|
3675
|
+
sage: f = M.scalar_field(x^2, name='f')
|
|
3676
|
+
sage: fU = f.restrict(U)
|
|
3677
|
+
sage: f.set_immutable()
|
|
3678
|
+
sage: fU.is_immutable()
|
|
3679
|
+
True
|
|
3680
|
+
sage: f.restrict(V).is_immutable()
|
|
3681
|
+
True
|
|
3682
|
+
"""
|
|
3683
|
+
for rst in self._restrictions.values():
|
|
3684
|
+
rst.set_immutable()
|
|
3685
|
+
for func in self._express.values():
|
|
3686
|
+
func.set_immutable()
|
|
3687
|
+
super().set_immutable()
|
|
3688
|
+
|
|
3689
|
+
@cached_method
|
|
3690
|
+
def __hash__(self):
|
|
3691
|
+
r"""
|
|
3692
|
+
Hash function.
|
|
3693
|
+
|
|
3694
|
+
TESTS::
|
|
3695
|
+
|
|
3696
|
+
sage: M = Manifold(2, 'M')
|
|
3697
|
+
sage: X.<x,y> = M.chart()
|
|
3698
|
+
sage: f = M.scalar_field(x^2, name='f')
|
|
3699
|
+
sage: f.set_immutable()
|
|
3700
|
+
sage: g = M.scalar_field(x^2, name='g')
|
|
3701
|
+
sage: g.set_immutable()
|
|
3702
|
+
|
|
3703
|
+
Check whether equality implies equality of hash::
|
|
3704
|
+
|
|
3705
|
+
sage: f == g
|
|
3706
|
+
True
|
|
3707
|
+
sage: hash(f) == hash(g)
|
|
3708
|
+
True
|
|
3709
|
+
|
|
3710
|
+
Let us check that ``f`` can be used as a dictionary key::
|
|
3711
|
+
|
|
3712
|
+
sage: {f: 1}[f]
|
|
3713
|
+
1
|
|
3714
|
+
"""
|
|
3715
|
+
if self.is_mutable():
|
|
3716
|
+
raise ValueError('element must be immutable in order to be '
|
|
3717
|
+
'hashable')
|
|
3718
|
+
return hash((type(self).__name__, self._domain))
|