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.
Files changed (182) hide show
  1. passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
  2. passagemath_symbolics/__init__.py +3 -0
  3. passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
  4. passagemath_symbolics-10.8.1a1.dist-info/RECORD +182 -0
  5. passagemath_symbolics-10.8.1a1.dist-info/WHEEL +6 -0
  6. passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
  7. sage/all__sagemath_symbolics.py +17 -0
  8. sage/calculus/all.py +14 -0
  9. sage/calculus/calculus.py +2838 -0
  10. sage/calculus/desolvers.py +1864 -0
  11. sage/calculus/predefined.py +51 -0
  12. sage/calculus/tests.py +225 -0
  13. sage/calculus/var.cpython-311-darwin.so +0 -0
  14. sage/calculus/var.pyx +401 -0
  15. sage/dynamics/all__sagemath_symbolics.py +6 -0
  16. sage/dynamics/complex_dynamics/all.py +5 -0
  17. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-311-darwin.so +0 -0
  19. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
  20. sage/ext/all__sagemath_symbolics.py +1 -0
  21. sage/ext_data/kenzo/CP2.txt +45 -0
  22. sage/ext_data/kenzo/CP3.txt +349 -0
  23. sage/ext_data/kenzo/CP4.txt +4774 -0
  24. sage/ext_data/kenzo/README.txt +49 -0
  25. sage/ext_data/kenzo/S4.txt +20 -0
  26. sage/ext_data/magma/latex/latex.m +1021 -0
  27. sage/ext_data/magma/latex/latex.spec +1 -0
  28. sage/ext_data/magma/sage/basic.m +356 -0
  29. sage/ext_data/magma/sage/sage.spec +1 -0
  30. sage/ext_data/magma/spec +9 -0
  31. sage/geometry/all__sagemath_symbolics.py +8 -0
  32. sage/geometry/hyperbolic_space/all.py +5 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  39. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  40. sage/geometry/riemannian_manifolds/all.py +7 -0
  41. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  42. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  43. sage/interfaces/all__sagemath_symbolics.py +1 -0
  44. sage/interfaces/magma.py +2991 -0
  45. sage/interfaces/magma_free.py +90 -0
  46. sage/interfaces/maple.py +1402 -0
  47. sage/interfaces/mathematica.py +1345 -0
  48. sage/interfaces/mathics.py +1312 -0
  49. sage/interfaces/sympy.py +1398 -0
  50. sage/interfaces/sympy_wrapper.py +197 -0
  51. sage/interfaces/tides.py +938 -0
  52. sage/libs/all__sagemath_symbolics.py +6 -0
  53. sage/manifolds/all.py +7 -0
  54. sage/manifolds/calculus_method.py +553 -0
  55. sage/manifolds/catalog.py +437 -0
  56. sage/manifolds/chart.py +4010 -0
  57. sage/manifolds/chart_func.py +3416 -0
  58. sage/manifolds/continuous_map.py +2183 -0
  59. sage/manifolds/continuous_map_image.py +155 -0
  60. sage/manifolds/differentiable/affine_connection.py +2475 -0
  61. sage/manifolds/differentiable/all.py +1 -0
  62. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  63. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  64. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  65. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  66. sage/manifolds/differentiable/chart.py +1241 -0
  67. sage/manifolds/differentiable/curve.py +1028 -0
  68. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  69. sage/manifolds/differentiable/degenerate.py +559 -0
  70. sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
  71. sage/manifolds/differentiable/diff_form.py +1660 -0
  72. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  73. sage/manifolds/differentiable/diff_map.py +1315 -0
  74. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  75. sage/manifolds/differentiable/examples/all.py +1 -0
  76. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  77. sage/manifolds/differentiable/examples/real_line.py +897 -0
  78. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  79. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  80. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  81. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  82. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  83. sage/manifolds/differentiable/manifold.py +4254 -0
  84. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  85. sage/manifolds/differentiable/metric.py +3032 -0
  86. sage/manifolds/differentiable/mixed_form.py +1507 -0
  87. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  88. sage/manifolds/differentiable/multivector_module.py +800 -0
  89. sage/manifolds/differentiable/multivectorfield.py +1522 -0
  90. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  91. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  92. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  93. sage/manifolds/differentiable/scalarfield.py +1343 -0
  94. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  95. sage/manifolds/differentiable/symplectic_form.py +912 -0
  96. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  97. sage/manifolds/differentiable/tangent_space.py +412 -0
  98. sage/manifolds/differentiable/tangent_vector.py +616 -0
  99. sage/manifolds/differentiable/tensorfield.py +4665 -0
  100. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  101. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  102. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  103. sage/manifolds/differentiable/vector_bundle.py +1725 -0
  104. sage/manifolds/differentiable/vectorfield.py +1717 -0
  105. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  106. sage/manifolds/differentiable/vectorframe.py +1832 -0
  107. sage/manifolds/family.py +270 -0
  108. sage/manifolds/local_frame.py +1490 -0
  109. sage/manifolds/manifold.py +3090 -0
  110. sage/manifolds/manifold_homset.py +452 -0
  111. sage/manifolds/operators.py +359 -0
  112. sage/manifolds/point.py +994 -0
  113. sage/manifolds/scalarfield.py +3718 -0
  114. sage/manifolds/scalarfield_algebra.py +629 -0
  115. sage/manifolds/section.py +3111 -0
  116. sage/manifolds/section_module.py +831 -0
  117. sage/manifolds/structure.py +229 -0
  118. sage/manifolds/subset.py +2721 -0
  119. sage/manifolds/subsets/all.py +1 -0
  120. sage/manifolds/subsets/closure.py +131 -0
  121. sage/manifolds/subsets/pullback.py +883 -0
  122. sage/manifolds/topological_submanifold.py +891 -0
  123. sage/manifolds/trivialization.py +733 -0
  124. sage/manifolds/utilities.py +1348 -0
  125. sage/manifolds/vector_bundle.py +1347 -0
  126. sage/manifolds/vector_bundle_fiber.py +332 -0
  127. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  128. sage/matrix/all__sagemath_symbolics.py +1 -0
  129. sage/matrix/matrix_symbolic_dense.cpython-311-darwin.so +0 -0
  130. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  131. sage/matrix/matrix_symbolic_dense.pyx +1030 -0
  132. sage/matrix/matrix_symbolic_sparse.cpython-311-darwin.so +0 -0
  133. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  134. sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
  135. sage/modules/all__sagemath_symbolics.py +1 -0
  136. sage/modules/vector_callable_symbolic_dense.py +105 -0
  137. sage/modules/vector_symbolic_dense.py +116 -0
  138. sage/modules/vector_symbolic_sparse.py +118 -0
  139. sage/rings/all__sagemath_symbolics.py +4 -0
  140. sage/rings/asymptotic/all.py +6 -0
  141. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  142. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  143. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
  144. sage/rings/asymptotic/growth_group.py +5373 -0
  145. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  146. sage/rings/asymptotic/term_monoid.py +5205 -0
  147. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  148. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  149. sage/symbolic/all.py +15 -0
  150. sage/symbolic/assumptions.py +987 -0
  151. sage/symbolic/benchmark.py +93 -0
  152. sage/symbolic/callable.py +456 -0
  153. sage/symbolic/callable.pyi +66 -0
  154. sage/symbolic/comparison_impl.pyi +38 -0
  155. sage/symbolic/complexity_measures.py +35 -0
  156. sage/symbolic/constants.py +1286 -0
  157. sage/symbolic/constants_c_impl.pyi +10 -0
  158. sage/symbolic/expression_conversion_algebraic.py +310 -0
  159. sage/symbolic/expression_conversion_sympy.py +317 -0
  160. sage/symbolic/expression_conversions.py +1727 -0
  161. sage/symbolic/function_factory.py +355 -0
  162. sage/symbolic/function_factory.pyi +41 -0
  163. sage/symbolic/getitem_impl.pyi +24 -0
  164. sage/symbolic/integration/all.py +1 -0
  165. sage/symbolic/integration/external.py +271 -0
  166. sage/symbolic/integration/integral.py +1075 -0
  167. sage/symbolic/maxima_wrapper.py +162 -0
  168. sage/symbolic/operators.py +267 -0
  169. sage/symbolic/operators.pyi +61 -0
  170. sage/symbolic/pynac_constant_impl.pyi +13 -0
  171. sage/symbolic/pynac_function_impl.pyi +8 -0
  172. sage/symbolic/random_tests.py +461 -0
  173. sage/symbolic/relation.py +2062 -0
  174. sage/symbolic/ring.cpython-311-darwin.so +0 -0
  175. sage/symbolic/ring.pxd +5 -0
  176. sage/symbolic/ring.pyi +110 -0
  177. sage/symbolic/ring.pyx +1393 -0
  178. sage/symbolic/series_impl.pyi +10 -0
  179. sage/symbolic/subring.py +1025 -0
  180. sage/symbolic/symengine.py +19 -0
  181. sage/symbolic/tests.py +40 -0
  182. sage/symbolic/units.py +1468 -0
@@ -0,0 +1,4665 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Tensor Fields
4
+
5
+ The class :class:`TensorField` implements tensor fields on differentiable
6
+ manifolds. The derived class
7
+ :class:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal`
8
+ is devoted to tensor fields with values on parallelizable manifolds.
9
+
10
+ Various derived classes of :class:`TensorField` are devoted to specific tensor
11
+ fields:
12
+
13
+ * :class:`~sage.manifolds.differentiable.vectorfield.VectorField` for vector
14
+ fields (rank-1 contravariant tensor fields)
15
+
16
+ * :class:`~sage.manifolds.differentiable.automorphismfield.AutomorphismField`
17
+ for fields of tangent-space automorphisms
18
+
19
+ * :class:`~sage.manifolds.differentiable.diff_form.DiffForm` for differential
20
+ forms (fully antisymmetric covariant tensor fields)
21
+
22
+ * :class:`~sage.manifolds.differentiable.multivectorfield.MultivectorField`
23
+ for multivector fields (fully antisymmetric contravariant tensor fields)
24
+
25
+ AUTHORS:
26
+
27
+ - Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
28
+ - Travis Scrimshaw (2016): review tweaks
29
+ - Eric Gourgoulhon (2018): operators divergence, Laplacian and d'Alembertian;
30
+ method :meth:`TensorField.along`
31
+ - Florentin Jaffredo (2018) : series expansion with respect to a given
32
+ parameter
33
+ - Michael Jung (2019): improve treatment of the zero element; add method
34
+ :meth:`TensorField.copy_from`
35
+ - Eric Gourgoulhon (2020): add method :meth:`TensorField.apply_map`
36
+
37
+ REFERENCES:
38
+
39
+ - [KN1963]_
40
+ - [Lee2013]_
41
+ - [ONe1983]_
42
+ """
43
+
44
+ # *****************************************************************************
45
+ # Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
46
+ # Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
47
+ # Copyright (C) 2016 Travis Scrimshaw <tscrimsh@umn.edu>
48
+ #
49
+ # Distributed under the terms of the GNU General Public License (GPL)
50
+ # as published by the Free Software Foundation; either version 2 of
51
+ # the License, or (at your option) any later version.
52
+ # https://www.gnu.org/licenses/
53
+ # *****************************************************************************
54
+ from __future__ import annotations
55
+
56
+ from typing import TYPE_CHECKING, Optional, TypeVar, Union
57
+
58
+ from sage.rings.integer import Integer
59
+ from sage.rings.integer_ring import ZZ
60
+ from sage.structure.element import ModuleElementWithMutability
61
+ from sage.tensor.modules.comp import CompWithSym
62
+ from sage.tensor.modules.free_module_tensor import FreeModuleTensor
63
+ from sage.tensor.modules.tensor_with_indices import TensorWithIndices
64
+
65
+ if TYPE_CHECKING:
66
+ from sage.manifolds.differentiable.diff_map import DiffMap
67
+ from sage.manifolds.differentiable.manifold import DifferentiableManifold
68
+ from sage.manifolds.differentiable.metric import PseudoRiemannianMetric
69
+ from sage.manifolds.differentiable.poisson_tensor import PoissonTensorField
70
+ from sage.manifolds.differentiable.symplectic_form import SymplecticForm
71
+ from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule
72
+ from sage.manifolds.point import ManifoldPoint
73
+ from sage.tensor.modules.comp import Components
74
+
75
+
76
+ TensorType = tuple[int, int]
77
+ T = TypeVar("T", bound='TensorField')
78
+
79
+
80
+ class TensorField(ModuleElementWithMutability):
81
+ r"""
82
+ Tensor field along a differentiable manifold.
83
+
84
+ An instance of this class is a tensor field along a differentiable
85
+ manifold `U` with values on a differentiable manifold `M`, via a
86
+ differentiable map `\Phi: U \rightarrow M`. More precisely, given two
87
+ nonnegative integers `k` and `l` and a differentiable map
88
+
89
+ .. MATH::
90
+
91
+ \Phi:\ U \longrightarrow M,
92
+
93
+ a *tensor field of type* `(k,l)` *along* `U` *with values on* `M` is
94
+ a differentiable map
95
+
96
+ .. MATH::
97
+
98
+ t:\ U \longrightarrow T^{(k,l)}M
99
+
100
+ (where `T^{(k,l)}M` is the tensor bundle of type `(k,l)` over `M`) such
101
+ that
102
+
103
+ .. MATH::
104
+
105
+ \forall p \in U,\ t(p) \in T^{(k,l)}(T_q M)
106
+
107
+ i.e. `t(p)` is a tensor of type `(k,l)` on the tangent space `T_q M` at
108
+ the point `q = \Phi(p)`, that is to say a multilinear map
109
+
110
+ .. MATH::
111
+
112
+ t(p):\ \underbrace{T_q^*M\times\cdots\times T_q^*M}_{k\ \; \text{times}}
113
+ \times \underbrace{T_q M\times\cdots\times T_q M}_{l\ \; \text{times}}
114
+ \longrightarrow K,
115
+
116
+ where `T_q^* M` is the dual vector space to `T_q M` and `K` is the
117
+ topological field over which the manifold `M` is defined. The integer `k+l`
118
+ is called the *tensor rank*.
119
+
120
+ The standard case of a tensor
121
+ field *on* a differentiable manifold corresponds to `U=M` and
122
+ `\Phi = \mathrm{Id}_M`. Other common cases are `\Phi` being an
123
+ immersion and `\Phi` being a curve in `M` (`U` is then an open interval
124
+ of `\RR`).
125
+
126
+ If `M` is parallelizable, the class
127
+ :class:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal`
128
+ should be used instead.
129
+
130
+ This is a Sage *element* class, the corresponding *parent* class being
131
+ :class:`~sage.manifolds.differentiable.tensorfield_module.TensorFieldModule`.
132
+
133
+ INPUT:
134
+
135
+ - ``vector_field_module`` -- module `\mathfrak{X}(U,\Phi)` of vector
136
+ fields along `U` associated with the map `\Phi: U \rightarrow M` (cf.
137
+ :class:`~sage.manifolds.differentiable.vectorfield_module.VectorFieldModule`)
138
+ - ``tensor_type`` -- pair `(k,l)` with `k` being the contravariant rank
139
+ and `l` the covariant rank
140
+ - ``name`` -- (default: ``None``) name given to the tensor field
141
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the tensor
142
+ field; if none is provided, the LaTeX symbol is set to ``name``
143
+ - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries among
144
+ the tensor arguments: each symmetry is described by a tuple containing
145
+ the positions of the involved arguments, with the convention
146
+ ``position = 0`` for the first argument; for instance:
147
+
148
+ * ``sym = (0,1)`` for a symmetry between the 1st and 2nd arguments
149
+ * ``sym = [(0,2), (1,3,4)]`` for a symmetry between the 1st and 3rd
150
+ arguments and a symmetry between the 2nd, 4th and 5th arguments.
151
+
152
+ - ``antisym`` -- (default: ``None``) antisymmetry or list of antisymmetries
153
+ among the arguments, with the same convention as for ``sym``
154
+ - ``parent`` -- (default: ``None``) some specific parent (e.g. exterior
155
+ power for differential forms); if ``None``,
156
+ ``vector_field_module.tensor_module(k,l)`` is used
157
+
158
+ EXAMPLES:
159
+
160
+ Tensor field of type (0,2) on the sphere `S^2`::
161
+
162
+ sage: M = Manifold(2, 'S^2') # the 2-dimensional sphere S^2
163
+ sage: U = M.open_subset('U') # complement of the North pole
164
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
165
+ sage: V = M.open_subset('V') # complement of the South pole
166
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
167
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
168
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
169
+ ....: intersection_name='W', restrictions1= x^2+y^2!=0,
170
+ ....: restrictions2= u^2+v^2!=0)
171
+ sage: uv_to_xy = xy_to_uv.inverse()
172
+ sage: W = U.intersection(V)
173
+ sage: t = M.tensor_field(0,2, name='t') ; t
174
+ Tensor field t of type (0,2) on the 2-dimensional differentiable
175
+ manifold S^2
176
+ sage: t.parent()
177
+ Module T^(0,2)(S^2) of type-(0,2) tensors fields on the 2-dimensional
178
+ differentiable manifold S^2
179
+ sage: t.parent().category()
180
+ Category of tensor products of modules over Algebra of differentiable scalar fields
181
+ on the 2-dimensional differentiable manifold S^2
182
+
183
+ The parent of `t` is not a free module, for the sphere `S^2` is not
184
+ parallelizable::
185
+
186
+ sage: isinstance(t.parent(), FiniteRankFreeModule)
187
+ False
188
+
189
+ To fully define `t`, we have to specify its components in some vector
190
+ frames defined on subsets of `S^2`; let us start by the open subset `U`::
191
+
192
+ sage: eU = c_xy.frame()
193
+ sage: t[eU,:] = [[1,0], [-2,3]]
194
+ sage: t.display(eU)
195
+ t = dx⊗dx - 2 dy⊗dx + 3 dy⊗dy
196
+
197
+ To set the components of `t` on `V` consistently, we copy the expressions
198
+ of the components in the common subset `W`::
199
+
200
+ sage: eV = c_uv.frame()
201
+ sage: eVW = eV.restrict(W)
202
+ sage: c_uvW = c_uv.restrict(W)
203
+ sage: t[eV,0,0] = t[eVW,0,0,c_uvW].expr() # long time
204
+ sage: t[eV,0,1] = t[eVW,0,1,c_uvW].expr() # long time
205
+ sage: t[eV,1,0] = t[eVW,1,0,c_uvW].expr() # long time
206
+ sage: t[eV,1,1] = t[eVW,1,1,c_uvW].expr() # long time
207
+
208
+ Actually, the above operation can be performed in a single line by means
209
+ of the method
210
+ :meth:`~sage.manifolds.differentiable.tensorfield.TensorField.add_comp_by_continuation`::
211
+
212
+ sage: t.add_comp_by_continuation(eV, W, chart=c_uv) # long time
213
+
214
+ At this stage, `t` is fully defined, having components in frames eU and eV
215
+ and the union of the domains of eU and eV being the whole manifold::
216
+
217
+ sage: t.display(eV) # long time
218
+ t = (u^4 - 4*u^3*v + 10*u^2*v^2 + 4*u*v^3 + v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du⊗du
219
+ - 4*(u^3*v + 2*u^2*v^2 - u*v^3)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du⊗dv
220
+ + 2*(u^4 - 2*u^3*v - 2*u^2*v^2 + 2*u*v^3 + v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) dv⊗du
221
+ + (3*u^4 + 4*u^3*v - 2*u^2*v^2 - 4*u*v^3 + 3*v^4)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) dv⊗dv
222
+
223
+ Let us consider two vector fields, `a` and `b`, on `S^2`::
224
+
225
+ sage: a = M.vector_field({eU: [-y, x]}, name='a')
226
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
227
+ sage: a.display(eV)
228
+ a = -v ∂/∂u + u ∂/∂v
229
+ sage: b = M.vector_field({eU: [y, -1]}, name='b')
230
+ sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
231
+ sage: b.display(eV)
232
+ b = ((2*u + 1)*v^3 + (2*u^3 - u^2)*v)/(u^2 + v^2) ∂/∂u
233
+ - (u^4 - v^4 + 2*u*v^2)/(u^2 + v^2) ∂/∂v
234
+
235
+ As a tensor field of type `(0,2)`, `t` acts on the pair `(a,b)`,
236
+ resulting in a scalar field::
237
+
238
+ sage: f = t(a,b); f
239
+ Scalar field t(a,b) on the 2-dimensional differentiable manifold S^2
240
+ sage: f.display() # long time
241
+ t(a,b): S^2 → ℝ
242
+ on U: (x, y) ↦ -2*x*y - y^2 - 3*x
243
+ on V: (u, v) ↦ -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)
244
+
245
+ The vectors can be defined only on subsets of `S^2`, the domain of the
246
+ result is then the common subset::
247
+
248
+ sage: # long time
249
+ sage: s = t(a.restrict(U), b) ; s
250
+ Scalar field t(a,b) on the Open subset U of the 2-dimensional
251
+ differentiable manifold S^2
252
+ sage: s.display()
253
+ t(a,b): U → ℝ
254
+ (x, y) ↦ -2*x*y - y^2 - 3*x
255
+ on W: (u, v) ↦ -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)
256
+ sage: s = t(a.restrict(U), b.restrict(W)) ; s
257
+ Scalar field t(a,b) on the Open subset W of the 2-dimensional
258
+ differentiable manifold S^2
259
+ sage: s.display()
260
+ t(a,b): W → ℝ
261
+ (x, y) ↦ -2*x*y - y^2 - 3*x
262
+ (u, v) ↦ -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)
263
+
264
+ The tensor itself can be defined only on some open subset of `S^2`,
265
+ yielding a result whose domain is this subset::
266
+
267
+ sage: s = t.restrict(V)(a,b); s # long time
268
+ Scalar field t(a,b) on the Open subset V of the 2-dimensional
269
+ differentiable manifold S^2
270
+ sage: s.display() # long time
271
+ t(a,b): V → ℝ
272
+ (u, v) ↦ -(3*u^3 + (3*u + 1)*v^2 + 2*u*v)/(u^4 + 2*u^2*v^2 + v^4)
273
+ on W: (x, y) ↦ -2*x*y - y^2 - 3*x
274
+
275
+ Tests regarding the multiplication by a scalar field::
276
+
277
+ sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2),
278
+ ....: c_uv: (u^2 + v^2)/(u^2 + v^2 + 1)}, name='f')
279
+ sage: t.parent().base_ring() is f.parent()
280
+ True
281
+ sage: s = f*t; s # long time
282
+ Tensor field f*t of type (0,2) on the 2-dimensional differentiable
283
+ manifold S^2
284
+ sage: s[[0,0]] == f*t[[0,0]] # long time
285
+ True
286
+ sage: s.restrict(U) == f.restrict(U) * t.restrict(U) # long time
287
+ True
288
+ sage: s = f*t.restrict(U); s
289
+ Tensor field f*t of type (0,2) on the Open subset U of the 2-dimensional
290
+ differentiable manifold S^2
291
+ sage: s.restrict(U) == f.restrict(U) * t.restrict(U)
292
+ True
293
+
294
+ .. RUBRIC:: Same examples with SymPy as the symbolic engine
295
+
296
+ From now on, we ask that all symbolic calculus on manifold `M` are
297
+ performed by SymPy::
298
+
299
+ sage: M.set_calculus_method('sympy')
300
+
301
+ We define the tensor `t` as above::
302
+
303
+ sage: t = M.tensor_field(0, 2, {eU: [[1,0], [-2,3]]}, name='t')
304
+ sage: t.display(eU)
305
+ t = dx⊗dx - 2 dy⊗dx + 3 dy⊗dy
306
+ sage: t.add_comp_by_continuation(eV, W, chart=c_uv) # long time
307
+ sage: t.display(eV) # long time
308
+ t = (u**4 - 4*u**3*v + 10*u**2*v**2 + 4*u*v**3 + v**4)/(u**8 +
309
+ 4*u**6*v**2 + 6*u**4*v**4 + 4*u**2*v**6 + v**8) du⊗du +
310
+ 4*u*v*(-u**2 - 2*u*v + v**2)/(u**8 + 4*u**6*v**2 + 6*u**4*v**4
311
+ + 4*u**2*v**6 + v**8) du⊗dv + 2*(u**4 - 2*u**3*v - 2*u**2*v**2
312
+ + 2*u*v**3 + v**4)/(u**8 + 4*u**6*v**2 + 6*u**4*v**4 +
313
+ 4*u**2*v**6 + v**8) dv⊗du + (3*u**4 + 4*u**3*v - 2*u**2*v**2 -
314
+ 4*u*v**3 + 3*v**4)/(u**8 + 4*u**6*v**2 + 6*u**4*v**4 +
315
+ 4*u**2*v**6 + v**8) dv⊗dv
316
+
317
+ The default coordinate representations of tensor components are now
318
+ SymPy objects::
319
+
320
+ sage: t[eV,1,1,c_uv].expr() # long time
321
+ (3*u**4 + 4*u**3*v - 2*u**2*v**2 - 4*u*v**3 + 3*v**4)/(u**8 +
322
+ 4*u**6*v**2 + 6*u**4*v**4 + 4*u**2*v**6 + v**8)
323
+ sage: type(t[eV,1,1,c_uv].expr()) # long time
324
+ <class 'sympy.core.mul.Mul'>
325
+
326
+ Let us consider two vector fields, `a` and `b`, on `S^2`::
327
+
328
+ sage: a = M.vector_field({eU: [-y, x]}, name='a')
329
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
330
+ sage: a.display(eV)
331
+ a = -v ∂/∂u + u ∂/∂v
332
+ sage: b = M.vector_field({eU: [y, -1]}, name='b')
333
+ sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
334
+ sage: b.display(eV)
335
+ b = v*(2*u**3 - u**2 + 2*u*v**2 + v**2)/(u**2 + v**2) ∂/∂u
336
+ + (-u**4 - 2*u*v**2 + v**4)/(u**2 + v**2) ∂/∂v
337
+
338
+ As a tensor field of type `(0,2)`, `t` acts on the pair `(a,b)`,
339
+ resulting in a scalar field::
340
+
341
+ sage: f = t(a,b)
342
+ sage: f.display() # long time
343
+ t(a,b): S^2 → ℝ
344
+ on U: (x, y) ↦ -2*x*y - 3*x - y**2
345
+ on V: (u, v) ↦ (-3*u**3 - 3*u*v**2 - 2*u*v - v**2)/(u**4 + 2*u**2*v**2 + v**4)
346
+
347
+ The vectors can be defined only on subsets of `S^2`, the domain of the
348
+ result is then the common subset::
349
+
350
+ sage: s = t(a.restrict(U), b)
351
+ sage: s.display() # long time
352
+ t(a,b): U → ℝ
353
+ (x, y) ↦ -2*x*y - 3*x - y**2
354
+ on W: (u, v) ↦ (-3*u**3 - 3*u*v**2 - 2*u*v - v**2)/(u**4 + 2*u**2*v**2 + v**4)
355
+ sage: s = t(a.restrict(U), b.restrict(W)) # long time
356
+ sage: s.display() # long time
357
+ t(a,b): W → ℝ
358
+ (x, y) ↦ -2*x*y - 3*x - y**2
359
+ (u, v) ↦ (-3*u**3 - 3*u*v**2 - 2*u*v - v**2)/(u**4 + 2*u**2*v**2 + v**4)
360
+
361
+ The tensor itself can be defined only on some open subset of `S^2`,
362
+ yielding a result whose domain is this subset::
363
+
364
+ sage: s = t.restrict(V)(a,b) # long time
365
+ sage: s.display() # long time
366
+ t(a,b): V → ℝ
367
+ (u, v) ↦ (-3*u**3 - 3*u*v**2 - 2*u*v - v**2)/(u**4 + 2*u**2*v**2 + v**4)
368
+ on W: (x, y) ↦ -2*x*y - 3*x - y**2
369
+
370
+ Tests regarding the multiplication by a scalar field::
371
+
372
+ sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2),
373
+ ....: c_uv: (u^2 + v^2)/(u^2 + v^2 + 1)}, name='f')
374
+ sage: s = f*t # long time
375
+ sage: s[[0,0]] == f*t[[0,0]] # long time
376
+ True
377
+ sage: s.restrict(U) == f.restrict(U) * t.restrict(U) # long time
378
+ True
379
+ sage: s = f*t.restrict(U)
380
+ sage: s.restrict(U) == f.restrict(U) * t.restrict(U)
381
+ True
382
+
383
+ Notice that the zero tensor field is immutable, and therefore its
384
+ components cannot be changed::
385
+
386
+ sage: zer = M.tensor_field_module((1, 1)).zero()
387
+ sage: zer.is_immutable()
388
+ True
389
+ sage: zer.set_comp()
390
+ Traceback (most recent call last):
391
+ ...
392
+ ValueError: the components of an immutable element cannot be
393
+ changed
394
+
395
+ Other tensor fields can be declared immutable, too::
396
+
397
+ sage: t.is_immutable()
398
+ False
399
+ sage: t.set_immutable()
400
+ sage: t.is_immutable()
401
+ True
402
+ sage: t.set_comp()
403
+ Traceback (most recent call last):
404
+ ...
405
+ ValueError: the components of an immutable element cannot be
406
+ changed
407
+ sage: t.set_name('b')
408
+ Traceback (most recent call last):
409
+ ...
410
+ ValueError: the name of an immutable element cannot be changed
411
+ """
412
+
413
+ _name: Optional[str]
414
+ _latex_name: Optional[str]
415
+ _vmodule: VectorFieldModule
416
+ _domain: DifferentiableManifold
417
+ _ambient_domain: DifferentiableManifold
418
+
419
+ def __init__(
420
+ self,
421
+ vector_field_module: VectorFieldModule,
422
+ tensor_type: TensorType,
423
+ name: Optional[str] = None,
424
+ latex_name: Optional[str] = None,
425
+ sym=None,
426
+ antisym=None,
427
+ parent=None,
428
+ ):
429
+ r"""
430
+ Construct a tensor field.
431
+
432
+ TESTS:
433
+
434
+ Construction via ``parent.element_class``, and not via a direct call
435
+ to ``TensorField``, to fit with the category framework::
436
+
437
+ sage: M = Manifold(2, 'M')
438
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
439
+ sage: M.declare_union(U,V) # M is the union of U and V
440
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
441
+ sage: transf = c_xy.transition_map(c_uv, (x+y, x-y),
442
+ ....: intersection_name='W', restrictions1= x>0,
443
+ ....: restrictions2= u+v>0)
444
+ sage: inv = transf.inverse()
445
+ sage: W = U.intersection(V)
446
+ sage: e_xy = c_xy.frame() ; e_uv = c_uv.frame()
447
+ sage: XM = M.vector_field_module()
448
+ sage: T02 = M.tensor_field_module((0,2))
449
+ sage: t = T02.element_class(XM, (0,2), name='t'); t
450
+ Tensor field t of type (0,2) on the 2-dimensional differentiable
451
+ manifold M
452
+ sage: t[e_xy,:] = [[1+x^2, x*y], [0, 1+y^2]]
453
+ sage: t.add_comp_by_continuation(e_uv, W, c_uv)
454
+ sage: t.display(e_xy)
455
+ t = (x^2 + 1) dx⊗dx + x*y dx⊗dy + (y^2 + 1) dy⊗dy
456
+ sage: t.display(e_uv)
457
+ t = (3/16*u^2 + 1/16*v^2 + 1/2) du⊗du
458
+ + (-1/16*u^2 + 1/4*u*v + 1/16*v^2) du⊗dv
459
+ + (1/16*u^2 + 1/4*u*v - 1/16*v^2) dv⊗du
460
+ + (1/16*u^2 + 3/16*v^2 + 1/2) dv⊗dv
461
+ sage: TestSuite(t).run(skip='_test_pickling')
462
+
463
+ Construction with ``DifferentiableManifold.tensor_field``::
464
+
465
+ sage: t1 = M.tensor_field(0, 2, name='t'); t1
466
+ Tensor field t of type (0,2) on the 2-dimensional differentiable
467
+ manifold M
468
+ sage: type(t1) == type(t)
469
+ True
470
+ """
471
+ if parent is None:
472
+ parent = vector_field_module.tensor_module(*tensor_type)
473
+ ModuleElementWithMutability.__init__(self, parent)
474
+ self._vmodule = vector_field_module
475
+ self._tensor_type = tuple(tensor_type)
476
+ self._tensor_rank = self._tensor_type[0] + self._tensor_type[1]
477
+ self._is_zero = False
478
+ # a priori, may be changed below or via method __bool__()
479
+
480
+ self._name = name
481
+ if latex_name is None:
482
+ self._latex_name = self._name
483
+ else:
484
+ self._latex_name = latex_name
485
+ self._domain = vector_field_module._domain
486
+ self._ambient_domain = vector_field_module._ambient_domain
487
+
488
+ self._extensions_graph = {self._domain: self}
489
+ # dict. of known extensions of self on bigger domains,
490
+ # including self, with domains as keys. Its elements can be
491
+ # seen as incoming edges on a graph.
492
+ self._restrictions_graph = {self._domain: self}
493
+ # dict. of known restrictions of self on smaller domains,
494
+ # including self, with domains as keys. Its elements can be
495
+ # seen as outgoing edges on a graph.
496
+
497
+ self._restrictions = {} # dict. of restrictions of self on subdomains
498
+ # of self._domain, with the subdomains as keys
499
+ # Treatment of symmetry declarations:
500
+ self._sym, self._antisym = CompWithSym._canonicalize_sym_antisym(
501
+ self._tensor_rank, sym, antisym)
502
+ # Initialization of derived quantities:
503
+ self._init_derived()
504
+
505
+ # ###### Required methods for ModuleElement (beside arithmetic) #######
506
+
507
+ def __bool__(self):
508
+ r"""
509
+ Return ``True`` if ``self`` is nonzero and ``False`` otherwise.
510
+
511
+ This method is called by :meth:`is_zero`.
512
+
513
+ EXAMPLES:
514
+
515
+ Tensor field defined by parts on a 2-dimensional manifold::
516
+
517
+ sage: M = Manifold(2, 'M')
518
+ sage: U = M.open_subset('U')
519
+ sage: c_xy.<x, y> = U.chart()
520
+ sage: V = M.open_subset('V')
521
+ sage: c_uv.<u, v> = V.chart()
522
+ sage: M.declare_union(U,V) # M is the union of U and V
523
+ sage: t = M.tensor_field(1, 2, name='t')
524
+ sage: tu = U.tensor_field(1, 2, name='t')
525
+ sage: tv = V.tensor_field(1, 2, name='t')
526
+ sage: tu[0,0,0] = 0
527
+ sage: tv[0,0,0] = 0
528
+ sage: t.set_restriction(tv)
529
+ sage: t.set_restriction(tu)
530
+ sage: bool(t)
531
+ False
532
+ sage: t.is_zero() # indirect doctest
533
+ True
534
+ sage: tv[0,0,0] = 1
535
+ sage: t.set_restriction(tv)
536
+ sage: bool(t)
537
+ True
538
+ sage: t.is_zero() # indirect doctest
539
+ False
540
+ """
541
+ if self._is_zero:
542
+ return False
543
+ if any(bool(rst) for rst in self._restrictions.values()):
544
+ self._is_zero = False
545
+ return True
546
+ self._is_zero = True
547
+ return False
548
+
549
+ # #### End of required methods for ModuleElement (beside arithmetic) #####
550
+
551
+ def _repr_(self) -> str:
552
+ r"""
553
+ String representation of ``self``.
554
+
555
+ TESTS::
556
+
557
+ sage: M = Manifold(2, 'M')
558
+ sage: t = M.tensor_field(1, 3, name='t')
559
+ sage: t
560
+ Tensor field t of type (1,3) on the 2-dimensional differentiable manifold M
561
+ """
562
+ # Special cases
563
+ if self._tensor_type == (0,2) and self._sym == ((0,1),):
564
+ description = "Field of symmetric bilinear forms "
565
+ if self._name is not None:
566
+ description += self._name + " "
567
+ else:
568
+ # Generic case
569
+ description = "Tensor field "
570
+ if self._name is not None:
571
+ description += self._name + " "
572
+ description += "of type ({},{}) ".format(
573
+ self._tensor_type[0], self._tensor_type[1])
574
+ return self._final_repr(description)
575
+
576
+ def _latex_(self):
577
+ r"""
578
+ LaTeX representation of ``self``.
579
+
580
+ TESTS::
581
+
582
+ sage: M = Manifold(2, 'M')
583
+ sage: t = M.tensor_field(1, 3, name='t')
584
+ sage: t._latex_()
585
+ 't'
586
+ sage: t = M.tensor_field(1, 3, name='t', latex_name=r'\tau')
587
+ sage: latex(t)
588
+ \tau
589
+ """
590
+ if self._latex_name is None:
591
+ return r'\text{' + str(self) + r'}'
592
+ else:
593
+ return self._latex_name
594
+
595
+ def set_name(self, name: Optional[str] = None, latex_name: Optional[str] = None):
596
+ r"""
597
+ Set (or change) the text name and LaTeX name of ``self``.
598
+
599
+ INPUT:
600
+
601
+ - ``name`` -- string (default: ``None``); name given to the tensor
602
+ field
603
+ - ``latex_name`` -- string (default: ``None``); LaTeX symbol to denote
604
+ the tensor field; if ``None`` while ``name`` is provided, the LaTeX
605
+ symbol is set to ``name``
606
+
607
+ EXAMPLES::
608
+
609
+ sage: M = Manifold(2, 'M')
610
+ sage: t = M.tensor_field(1, 3); t
611
+ Tensor field of type (1,3) on the 2-dimensional differentiable
612
+ manifold M
613
+ sage: t.set_name(name='t')
614
+ sage: t
615
+ Tensor field t of type (1,3) on the 2-dimensional differentiable
616
+ manifold M
617
+ sage: latex(t)
618
+ t
619
+ sage: t.set_name(latex_name=r'\tau')
620
+ sage: latex(t)
621
+ \tau
622
+ sage: t.set_name(name='a')
623
+ sage: t
624
+ Tensor field a of type (1,3) on the 2-dimensional differentiable
625
+ manifold M
626
+ sage: latex(t)
627
+ a
628
+ """
629
+ if self.is_immutable():
630
+ raise ValueError("the name of an immutable element "
631
+ "cannot be changed")
632
+ if name is not None:
633
+ self._name = name
634
+ if latex_name is None:
635
+ self._latex_name = self._name
636
+ if latex_name is not None:
637
+ self._latex_name = latex_name
638
+ for rst in self._restrictions.values():
639
+ rst.set_name(name=name, latex_name=latex_name)
640
+
641
+ def _new_instance(self):
642
+ r"""
643
+ Create an instance of the same class as ``self`` on the same
644
+ vector field module, with the same tensor type and same symmetries
645
+
646
+ TESTS::
647
+
648
+ sage: M = Manifold(2, 'M')
649
+ sage: t = M.tensor_field(1, 3, name='t')
650
+ sage: t1 = t._new_instance(); t1
651
+ Tensor field of type (1,3) on the 2-dimensional differentiable
652
+ manifold M
653
+ sage: type(t1) == type(t)
654
+ True
655
+ sage: t1.parent() is t.parent()
656
+ True
657
+ """
658
+ return type(self)(self._vmodule, self._tensor_type, sym=self._sym,
659
+ antisym=self._antisym, parent=self.parent())
660
+
661
+ def _final_repr(self, description: str) -> str:
662
+ r"""
663
+ Part of string representation common to all derived classes of
664
+ :class:`TensorField`.
665
+
666
+ TESTS::
667
+
668
+ sage: M = Manifold(2, 'M')
669
+ sage: t = M.tensor_field(1, 3, name='t')
670
+ sage: t._final_repr('Tensor field t ')
671
+ 'Tensor field t on the 2-dimensional differentiable manifold M'
672
+ """
673
+ if self._domain == self._ambient_domain:
674
+ description += "on the {}".format(self._domain)
675
+ else:
676
+ description += "along the {} ".format(self._domain) + \
677
+ "with values on the {}".format(self._ambient_domain)
678
+ return description
679
+
680
+ def _init_derived(self):
681
+ r"""
682
+ Initialize the derived quantities.
683
+
684
+ TESTS::
685
+
686
+ sage: M = Manifold(2, 'M')
687
+ sage: t = M.tensor_field(1, 3, name='t')
688
+ sage: t._init_derived()
689
+ """
690
+ self._lie_derivatives = {} # dict. of Lie derivatives of self (keys: id(vector))
691
+
692
+ def _del_derived(self):
693
+ r"""
694
+ Delete the derived quantities.
695
+
696
+ TESTS::
697
+
698
+ sage: M = Manifold(2, 'M')
699
+ sage: t = M.tensor_field(1, 3, name='t')
700
+ sage: t._del_derived()
701
+ """
702
+ # First deletes any reference to self in the vectors' dictionaries:
703
+ for val in self._lie_derivatives.values():
704
+ del val[0]._lie_der_along_self[id(self)]
705
+ # Then clears the dictionary of Lie derivatives
706
+ self._lie_derivatives.clear()
707
+
708
+ def _del_restrictions(self):
709
+ r"""
710
+ Delete the restrictions defined on ``self``.
711
+
712
+ TESTS::
713
+
714
+ sage: M = Manifold(2, 'M')
715
+ sage: c_xy.<x,y> = M.chart()
716
+ sage: t = M.tensor_field(1,2)
717
+ sage: U = M.open_subset('U', coord_def={c_xy: x<0})
718
+ sage: h = t.restrict(U)
719
+ sage: t._restrictions
720
+ {Open subset U of the 2-dimensional differentiable manifold M:
721
+ Tensor field of type (1,2) on the Open subset U of the
722
+ 2-dimensional differentiable manifold M}
723
+ sage: t._del_restrictions()
724
+ sage: t._restrictions
725
+ {}
726
+ """
727
+ self._restrictions.clear()
728
+ self._extensions_graph = {self._domain: self}
729
+ self._restrictions_graph = {self._domain: self}
730
+
731
+ def _init_components(self, *comp, **kwargs):
732
+ r"""
733
+ Initialize the tensor field components in some given vector frames.
734
+
735
+ INPUT:
736
+
737
+ - ``comp`` -- either the components of the tensor field with respect
738
+ to the vector frame specified by the argument ``frame`` or a
739
+ dictionary of components, the keys of which are vector frames or
740
+ pairs ``(f,c)`` where ``f`` is a vector frame and ``c`` a chart
741
+ - ``frame`` -- (default: ``None``; unused if ``comp`` is a dictionary)
742
+ vector frame in which the components are given; if ``None``, the
743
+ default vector frame on the domain of ``self`` is assumed
744
+ - ``chart`` -- (default: ``None``; unused if ``comp`` is a dictionary)
745
+ coordinate chart in which the components are expressed; if ``None``,
746
+ the default chart on the domain of ``frame`` is assumed
747
+
748
+ EXAMPLES::
749
+
750
+ sage: M = Manifold(2, 'M')
751
+ sage: X.<x,y> = M.chart()
752
+ sage: t = M.tensor_field(1, 1, name='t')
753
+ sage: t._init_components([[1+x, x*y], [-2, y^2]])
754
+ sage: t.display()
755
+ t = (x + 1) ∂/∂x⊗dx + x*y ∂/∂x⊗dy - 2 ∂/∂y⊗dx + y^2 ∂/∂y⊗dy
756
+ sage: Y.<u,v> = M.chart()
757
+ sage: t._init_components([[2*u, 3*v], [u+v, -u]], frame=Y.frame(),
758
+ ....: chart=Y)
759
+ sage: t.display(Y)
760
+ t = 2*u ∂/∂u⊗du + 3*v ∂/∂u⊗dv + (u + v) ∂/∂v⊗du - u ∂/∂v⊗dv
761
+ sage: t._init_components({X.frame(): [[2*x, 1-y],[0, x]]})
762
+ sage: t.display()
763
+ t = 2*x ∂/∂x⊗dx + (-y + 1) ∂/∂x⊗dy + x ∂/∂y⊗dy
764
+ sage: t._init_components({(Y.frame(), Y): [[2*u, 0],[v^3, u+v]]})
765
+ sage: t.display(Y)
766
+ t = 2*u ∂/∂u⊗du + v^3 ∂/∂v⊗du + (u + v) ∂/∂v⊗dv
767
+
768
+ TESTS:
769
+
770
+ Check that :issue:`29639` is fixed::
771
+
772
+ sage: v = M.vector_field()
773
+ sage: v._init_components(1/2, -1)
774
+ sage: v.display()
775
+ 1/2 ∂/∂x - ∂/∂y
776
+ """
777
+ comp0 = comp[0]
778
+ self._is_zero = False # a priori
779
+ if isinstance(comp0, dict):
780
+ for frame, components in comp0.items():
781
+ chart = None
782
+ if isinstance(frame, tuple):
783
+ # frame is actually a pair (frame, chart):
784
+ frame, chart = frame
785
+ self.add_comp(frame)[:, chart] = components
786
+ elif isinstance(comp0, str):
787
+ # For compatibility with previous use of tensor_field():
788
+ self.set_name(comp0)
789
+ else:
790
+ if hasattr(comp0, '__len__') and hasattr(comp0, '__getitem__'):
791
+ # comp0 is a list/vector of components
792
+ # otherwise comp is the tuple of components in a specific frame
793
+ comp = comp0
794
+ frame = kwargs.get('frame')
795
+ chart = kwargs.get('chart')
796
+ self.add_comp(frame)[:, chart] = comp
797
+
798
+ #### Simple accessors ####
799
+
800
+ def domain(self) -> DifferentiableManifold:
801
+ r"""
802
+ Return the manifold on which ``self`` is defined.
803
+
804
+ OUTPUT:
805
+
806
+ - instance of class
807
+ :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`
808
+
809
+ EXAMPLES::
810
+
811
+ sage: M = Manifold(2, 'M')
812
+ sage: c_xy.<x,y> = M.chart()
813
+ sage: t = M.tensor_field(1,2)
814
+ sage: t.domain()
815
+ 2-dimensional differentiable manifold M
816
+ sage: U = M.open_subset('U', coord_def={c_xy: x<0})
817
+ sage: h = t.restrict(U)
818
+ sage: h.domain()
819
+ Open subset U of the 2-dimensional differentiable manifold M
820
+ """
821
+ return self._domain
822
+
823
+ def base_module(self) -> VectorFieldModule:
824
+ r"""
825
+ Return the vector field module on which ``self`` acts as a tensor.
826
+
827
+ OUTPUT:
828
+
829
+ - instance of
830
+ :class:`~sage.manifolds.differentiable.vectorfield_module.VectorFieldModule`
831
+
832
+ EXAMPLES:
833
+
834
+ The module of vector fields on the 2-sphere as a "base module"::
835
+
836
+ sage: M = Manifold(2, 'S^2')
837
+ sage: t = M.tensor_field(0,2)
838
+ sage: t.base_module()
839
+ Module X(S^2) of vector fields on the 2-dimensional differentiable
840
+ manifold S^2
841
+ sage: t.base_module() is M.vector_field_module()
842
+ True
843
+ sage: XM = M.vector_field_module()
844
+ sage: XM.an_element().base_module() is XM
845
+ True
846
+ """
847
+ return self._vmodule
848
+
849
+ def tensor_type(self) -> TensorType:
850
+ r"""
851
+ Return the tensor type of ``self``.
852
+
853
+ OUTPUT:
854
+
855
+ - pair `(k,l)`, where `k` is the contravariant rank and `l` is
856
+ the covariant rank
857
+
858
+ EXAMPLES::
859
+
860
+ sage: M = Manifold(2, 'S^2')
861
+ sage: t = M.tensor_field(1,2)
862
+ sage: t.tensor_type()
863
+ (1, 2)
864
+ sage: v = M.vector_field()
865
+ sage: v.tensor_type()
866
+ (1, 0)
867
+ """
868
+ return self._tensor_type
869
+
870
+ def tensor_rank(self):
871
+ r"""
872
+ Return the tensor rank of ``self``.
873
+
874
+ OUTPUT:
875
+
876
+ - integer `k+l`, where `k` is the contravariant rank and `l` is
877
+ the covariant rank
878
+
879
+ EXAMPLES::
880
+
881
+ sage: M = Manifold(2, 'S^2')
882
+ sage: t = M.tensor_field(1,2)
883
+ sage: t.tensor_rank()
884
+ 3
885
+ sage: v = M.vector_field()
886
+ sage: v.tensor_rank()
887
+ 1
888
+ """
889
+ return self._tensor_rank
890
+
891
+ def symmetries(self):
892
+ r"""
893
+ Print the list of symmetries and antisymmetries.
894
+
895
+ EXAMPLES::
896
+
897
+ sage: M = Manifold(2, 'S^2')
898
+ sage: t = M.tensor_field(1,2)
899
+ sage: t.symmetries()
900
+ no symmetry; no antisymmetry
901
+ sage: t = M.tensor_field(1,2, sym=(1,2))
902
+ sage: t.symmetries()
903
+ symmetry: (1, 2); no antisymmetry
904
+ sage: t = M.tensor_field(2,2, sym=(0,1), antisym=(2,3))
905
+ sage: t.symmetries()
906
+ symmetry: (0, 1); antisymmetry: (2, 3)
907
+ sage: t = M.tensor_field(2,2, antisym=[(0,1),(2,3)])
908
+ sage: t.symmetries()
909
+ no symmetry; antisymmetries: [(0, 1), (2, 3)]
910
+ """
911
+ if not self._sym:
912
+ s = "no symmetry; "
913
+ elif len(self._sym) == 1:
914
+ s = "symmetry: {}; ".format(self._sym[0])
915
+ else:
916
+ s = "symmetries: {}; ".format(list(self._sym))
917
+ if not self._antisym:
918
+ a = "no antisymmetry"
919
+ elif len(self._antisym) == 1:
920
+ a = "antisymmetry: {}".format(self._antisym[0])
921
+ else:
922
+ a = "antisymmetries: {}".format(list(self._antisym))
923
+ print(s + a)
924
+
925
+ #### End of simple accessors #####
926
+
927
+ def set_immutable(self):
928
+ r"""
929
+ Set ``self`` and all restrictions of ``self`` immutable.
930
+
931
+ EXAMPLES::
932
+
933
+ sage: M = Manifold(2, 'M')
934
+ sage: X.<x,y> = M.chart()
935
+ sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1})
936
+ sage: a = M.tensor_field(1, 1, [[1+y,x], [0,x+y]], name='a')
937
+ sage: aU = a.restrict(U)
938
+ sage: a.set_immutable()
939
+ sage: aU.is_immutable()
940
+ True
941
+ """
942
+ for rst in self._restrictions.values():
943
+ rst.set_immutable()
944
+ super().set_immutable()
945
+
946
+ def set_restriction(self, rst: TensorField):
947
+ r"""
948
+ Define a restriction of ``self`` to some subdomain.
949
+
950
+ INPUT:
951
+
952
+ - ``rst`` -- :class:`TensorField` of the same type and symmetries
953
+ as the current tensor field ``self``, defined on a subdomain of
954
+ the domain of ``self``
955
+
956
+ EXAMPLES::
957
+
958
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
959
+ sage: U = M.open_subset('U') # complement of the North pole
960
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
961
+ sage: V = M.open_subset('V') # complement of the South pole
962
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
963
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
964
+ sage: t = M.tensor_field(1, 2, name='t')
965
+ sage: s = U.tensor_field(1, 2)
966
+ sage: s[0,0,1] = x+y
967
+ sage: t.set_restriction(s)
968
+ sage: t.display(c_xy.frame())
969
+ t = (x + y) ∂/∂x⊗dx⊗dy
970
+ sage: t.restrict(U) == s
971
+ True
972
+
973
+ If the restriction is defined on the very same domain, the tensor field
974
+ becomes a copy of it (see :meth:`copy_from`)::
975
+
976
+ sage: v = M.tensor_field(1, 2, name='v')
977
+ sage: v.set_restriction(t)
978
+ sage: v.restrict(U) == t.restrict(U)
979
+ True
980
+ """
981
+ if self.is_immutable():
982
+ raise ValueError("the restrictions of an immutable element "
983
+ "cannot be changed")
984
+ if not isinstance(rst, TensorField):
985
+ raise TypeError("the argument must be a tensor field")
986
+ if not rst._domain.is_subset(self._domain):
987
+ raise ValueError("the domain of the declared restriction is not " +
988
+ "a subset of the field's domain")
989
+ if not rst._ambient_domain.is_subset(self._ambient_domain):
990
+ raise ValueError("the ambient domain of the declared " +
991
+ "restriction is not a subset of the " +
992
+ "field's ambient domain")
993
+ if rst._tensor_type != self._tensor_type:
994
+ raise ValueError("the declared restriction has not the same " +
995
+ "tensor type as the current tensor field")
996
+ if rst._tensor_type != self._tensor_type:
997
+ raise ValueError("the declared restriction has not the same " +
998
+ "tensor type as the current tensor field")
999
+ if rst._sym != self._sym:
1000
+ raise ValueError("the declared restriction has not the same " +
1001
+ "symmetries as the current tensor field")
1002
+ if rst._antisym != self._antisym:
1003
+ raise ValueError("the declared restriction has not the same " +
1004
+ "antisymmetries as the current tensor field")
1005
+ if self._domain is rst._domain:
1006
+ self.copy_from(rst)
1007
+ else:
1008
+ self._restrictions[rst._domain] = rst.copy(name=self._name,
1009
+ latex_name=self._latex_name)
1010
+ self._is_zero = False # a priori
1011
+
1012
+ def restrict(
1013
+ self: T, subdomain: DifferentiableManifold, dest_map: Optional[DiffMap] = None
1014
+ ) -> T:
1015
+ r"""
1016
+ Return the restriction of ``self`` to some subdomain.
1017
+
1018
+ If the restriction has not been defined yet, it is constructed here.
1019
+
1020
+ INPUT:
1021
+
1022
+ - ``subdomain`` --
1023
+ :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`;
1024
+ open subset `U` of the tensor field domain `S`
1025
+ - ``dest_map`` --
1026
+ :class:`~sage.manifolds.differentiable.diff_map.DiffMap`
1027
+ (default: ``None``); destination map `\Psi:\ U \rightarrow V`,
1028
+ where `V` is an open subset of the manifold `M` where the tensor
1029
+ field takes it values; if ``None``, the restriction of `\Phi`
1030
+ to `U` is used, `\Phi` being the differentiable map
1031
+ `S \rightarrow M` associated with the tensor field
1032
+
1033
+ OUTPUT: :class:`TensorField` representing the restriction
1034
+
1035
+ EXAMPLES:
1036
+
1037
+ Restrictions of a vector field on the 2-sphere::
1038
+
1039
+ sage: M = Manifold(2, 'S^2', start_index=1)
1040
+ sage: U = M.open_subset('U') # the complement of the North pole
1041
+ sage: stereoN.<x,y> = U.chart() # stereographic coordinates from the North pole
1042
+ sage: eN = stereoN.frame() # the associated vector frame
1043
+ sage: V = M.open_subset('V') # the complement of the South pole
1044
+ sage: stereoS.<u,v> = V.chart() # stereographic coordinates from the South pole
1045
+ sage: eS = stereoS.frame() # the associated vector frame
1046
+ sage: transf = stereoN.transition_map(stereoS, (x/(x^2+y^2), y/(x^2+y^2)),
1047
+ ....: intersection_name='W', restrictions1= x^2+y^2!=0,
1048
+ ....: restrictions2= u^2+v^2!=0)
1049
+ sage: inv = transf.inverse() # transformation from stereoS to stereoN
1050
+ sage: W = U.intersection(V) # the complement of the North and South poles
1051
+ sage: stereoN_W = W.atlas()[0] # restriction of stereographic coord. from North pole to W
1052
+ sage: stereoS_W = W.atlas()[1] # restriction of stereographic coord. from South pole to W
1053
+ sage: eN_W = stereoN_W.frame() ; eS_W = stereoS_W.frame()
1054
+ sage: v = M.vector_field({eN: [1, 0]}, name='v')
1055
+ sage: v.display()
1056
+ v = ∂/∂x
1057
+ sage: vU = v.restrict(U) ; vU
1058
+ Vector field v on the Open subset U of the 2-dimensional
1059
+ differentiable manifold S^2
1060
+ sage: vU.display()
1061
+ v = ∂/∂x
1062
+ sage: vU == eN[1]
1063
+ True
1064
+ sage: vW = v.restrict(W) ; vW
1065
+ Vector field v on the Open subset W of the 2-dimensional
1066
+ differentiable manifold S^2
1067
+ sage: vW.display()
1068
+ v = ∂/∂x
1069
+ sage: vW.display(eS_W, stereoS_W)
1070
+ v = (-u^2 + v^2) ∂/∂u - 2*u*v ∂/∂v
1071
+ sage: vW == eN_W[1]
1072
+ True
1073
+
1074
+ At this stage, defining the restriction of ``v`` to the open
1075
+ subset ``V`` fully specifies ``v``::
1076
+
1077
+ sage: v.restrict(V)[1] = vW[eS_W, 1, stereoS_W].expr() # note that eS is the default frame on V
1078
+ sage: v.restrict(V)[2] = vW[eS_W, 2, stereoS_W].expr()
1079
+ sage: v.display(eS, stereoS)
1080
+ v = (-u^2 + v^2) ∂/∂u - 2*u*v ∂/∂v
1081
+ sage: v.restrict(U).display()
1082
+ v = ∂/∂x
1083
+ sage: v.restrict(V).display()
1084
+ v = (-u^2 + v^2) ∂/∂u - 2*u*v ∂/∂v
1085
+
1086
+ The restriction of the vector field to its own domain is of course
1087
+ itself::
1088
+
1089
+ sage: v.restrict(M) is v
1090
+ True
1091
+ sage: vU.restrict(U) is vU
1092
+ True
1093
+ """
1094
+ if (subdomain == self._domain
1095
+ and (dest_map is None or dest_map == self._vmodule._dest_map)):
1096
+ return self
1097
+ if subdomain not in self._restrictions:
1098
+ if not subdomain.is_subset(self._domain):
1099
+ raise ValueError("the provided domain is not a subset of " +
1100
+ "the field's domain")
1101
+ if dest_map is None:
1102
+ dest_map = self._vmodule._dest_map.restrict(subdomain)
1103
+ elif not dest_map._codomain.is_subset(self._ambient_domain):
1104
+ raise ValueError("the argument 'dest_map' is not compatible " +
1105
+ "with the ambient domain of " +
1106
+ "the {}".format(self))
1107
+ # First one tries to get the restriction from a tighter domain:
1108
+ for dom, rst in self._restrictions.items():
1109
+ if subdomain.is_subset(dom) and subdomain in rst._restrictions:
1110
+ res = rst._restrictions[subdomain]
1111
+ self._restrictions[subdomain] = res
1112
+ self._restrictions_graph[subdomain] = res
1113
+ res._extensions_graph.update(self._extensions_graph)
1114
+ for ext in self._extensions_graph.values():
1115
+ ext._restrictions[subdomain] = res
1116
+ ext._restrictions_graph[subdomain] = res
1117
+ return self._restrictions[subdomain]
1118
+
1119
+ for dom, rst in self._restrictions.items():
1120
+ if subdomain.is_subset(dom) and dom is not self._domain:
1121
+ self._restrictions[subdomain] = rst.restrict(subdomain)
1122
+ self._restrictions_graph[subdomain] = rst.restrict(subdomain)
1123
+ return self._restrictions[subdomain]
1124
+
1125
+ # Secondly one tries to get the restriction from one previously
1126
+ # defined on a larger domain:
1127
+ for dom, ext in self._extensions_graph.items():
1128
+ if subdomain in ext._restrictions:
1129
+ res = ext._restrictions_graph[subdomain]
1130
+ self._restrictions[subdomain] = res
1131
+ self._restrictions_graph[subdomain] = res
1132
+ res._extensions_graph.update(self._extensions_graph)
1133
+ for ext in self._extensions_graph.values():
1134
+ ext._restrictions[subdomain] = res
1135
+ ext._restrictions_graph[subdomain] = res
1136
+ return self._restrictions[subdomain]
1137
+
1138
+ # If this fails, the restriction is created from scratch:
1139
+ smodule = subdomain.vector_field_module(dest_map=dest_map)
1140
+ res = smodule.tensor(self._tensor_type, name=self._name,
1141
+ latex_name=self._latex_name, sym=self._sym,
1142
+ antisym=self._antisym,
1143
+ specific_type=type(self))
1144
+ res._extensions_graph.update(self._extensions_graph)
1145
+ for dom, ext in self._extensions_graph.items():
1146
+ ext._restrictions[subdomain] = res
1147
+ ext._restrictions_graph[subdomain] = res
1148
+
1149
+ for dom, rst in self._restrictions.items():
1150
+ if dom.is_subset(subdomain):
1151
+ if rst is not res:
1152
+ res._restrictions.update(rst._restrictions)
1153
+ res._restrictions_graph.update(rst._restrictions_graph)
1154
+ rst._extensions_graph.update(res._extensions_graph)
1155
+ if self.is_immutable():
1156
+ res.set_immutable() # restrictions must be immutable, too
1157
+ self._restrictions[subdomain] = res
1158
+ self._restrictions_graph[subdomain] = res
1159
+ res._extensions_graph.update(self._extensions_graph)
1160
+
1161
+ return self._restrictions[subdomain]
1162
+
1163
+ def _set_comp_unsafe(self, basis=None):
1164
+ r"""
1165
+ Return the components of ``self`` in a given vector frame
1166
+ for assignment. This private method invokes no security check. Use
1167
+ this method at your own risk.
1168
+
1169
+ The components with respect to other frames having the same domain
1170
+ as the provided vector frame are deleted, in order to avoid any
1171
+ inconsistency. To keep them, use the method :meth:`_add_comp_unsafe`
1172
+ instead.
1173
+
1174
+ INPUT:
1175
+
1176
+ - ``basis`` -- (default: ``None``) vector frame in which the
1177
+ components are defined; if none is provided, the components are
1178
+ assumed to refer to the tensor field domain's default frame
1179
+
1180
+ OUTPUT:
1181
+
1182
+ - components in the given frame, as a
1183
+ :class:`~sage.tensor.modules.comp.Components`; if such
1184
+ components did not exist previously, they are created
1185
+
1186
+ TESTS::
1187
+
1188
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
1189
+ sage: U = M.open_subset('U') # complement of the North pole
1190
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
1191
+ sage: V = M.open_subset('V') # complement of the South pole
1192
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
1193
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
1194
+ sage: e_uv = c_uv.frame()
1195
+ sage: t = M.tensor_field(1, 2, name='t')
1196
+ sage: t._set_comp_unsafe(e_uv)
1197
+ 3-indices components w.r.t. Coordinate frame (V, (∂/∂u,∂/∂v))
1198
+ sage: t._set_comp_unsafe(e_uv)[1,0,1] = u+v
1199
+ sage: t.display(e_uv)
1200
+ t = (u + v) ∂/∂v⊗du⊗dv
1201
+
1202
+ Setting the components in a new frame (``e``)::
1203
+
1204
+ sage: e = V.vector_frame('e')
1205
+ sage: t._set_comp_unsafe(e)
1206
+ 3-indices components w.r.t. Vector frame (V, (e_0,e_1))
1207
+ sage: t._set_comp_unsafe(e)[0,1,1] = u*v
1208
+ sage: t.display(e)
1209
+ t = u*v e_0⊗e^1⊗e^1
1210
+
1211
+ Since the frames ``e`` and ``e_uv`` are defined on the same domain, the
1212
+ components w.r.t. ``e_uv`` have been erased::
1213
+
1214
+ sage: t.display(c_uv.frame())
1215
+ Traceback (most recent call last):
1216
+ ...
1217
+ ValueError: no basis could be found for computing the components
1218
+ in the Coordinate frame (V, (∂/∂u,∂/∂v))
1219
+ """
1220
+ if basis is None:
1221
+ basis = self._domain._def_frame
1222
+ self._del_derived() # deletes the derived quantities
1223
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1224
+ return rst._set_comp_unsafe(basis)
1225
+
1226
+ def set_comp(self, basis=None):
1227
+ r"""
1228
+ Return the components of ``self`` in a given vector frame
1229
+ for assignment.
1230
+
1231
+ The components with respect to other frames having the same domain
1232
+ as the provided vector frame are deleted, in order to avoid any
1233
+ inconsistency. To keep them, use the method :meth:`add_comp` instead.
1234
+
1235
+ INPUT:
1236
+
1237
+ - ``basis`` -- (default: ``None``) vector frame in which the
1238
+ components are defined; if none is provided, the components are
1239
+ assumed to refer to the tensor field domain's default frame
1240
+
1241
+ OUTPUT:
1242
+
1243
+ - components in the given frame, as a
1244
+ :class:`~sage.tensor.modules.comp.Components`; if such
1245
+ components did not exist previously, they are created
1246
+
1247
+ EXAMPLES::
1248
+
1249
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
1250
+ sage: U = M.open_subset('U') # complement of the North pole
1251
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
1252
+ sage: V = M.open_subset('V') # complement of the South pole
1253
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
1254
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
1255
+ sage: e_uv = c_uv.frame()
1256
+ sage: t = M.tensor_field(1, 2, name='t')
1257
+ sage: t.set_comp(e_uv)
1258
+ 3-indices components w.r.t. Coordinate frame (V, (∂/∂u,∂/∂v))
1259
+ sage: t.set_comp(e_uv)[1,0,1] = u+v
1260
+ sage: t.display(e_uv)
1261
+ t = (u + v) ∂/∂v⊗du⊗dv
1262
+
1263
+ Setting the components in a new frame (``e``)::
1264
+
1265
+ sage: e = V.vector_frame('e')
1266
+ sage: t.set_comp(e)
1267
+ 3-indices components w.r.t. Vector frame (V, (e_0,e_1))
1268
+ sage: t.set_comp(e)[0,1,1] = u*v
1269
+ sage: t.display(e)
1270
+ t = u*v e_0⊗e^1⊗e^1
1271
+
1272
+ Since the frames ``e`` and ``e_uv`` are defined on the same domain, the
1273
+ components w.r.t. ``e_uv`` have been erased::
1274
+
1275
+ sage: t.display(c_uv.frame())
1276
+ Traceback (most recent call last):
1277
+ ...
1278
+ ValueError: no basis could be found for computing the components
1279
+ in the Coordinate frame (V, (∂/∂u,∂/∂v))
1280
+
1281
+ Since zero is an immutable, its components cannot be changed::
1282
+
1283
+ sage: z = M.tensor_field_module((1, 1)).zero()
1284
+ sage: z.set_comp(e)[0,1] = u*v
1285
+ Traceback (most recent call last):
1286
+ ...
1287
+ ValueError: the components of an immutable element cannot be
1288
+ changed
1289
+ """
1290
+ if self.is_immutable():
1291
+ raise ValueError("the components of an immutable element "
1292
+ "cannot be changed")
1293
+ self._is_zero = False # a priori
1294
+ if basis is None:
1295
+ basis = self._domain._def_frame
1296
+ self._del_derived() # deletes the derived quantities
1297
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1298
+ return rst.set_comp(basis=basis)
1299
+
1300
+ def _add_comp_unsafe(self, basis=None):
1301
+ r"""
1302
+ Return the components of ``self`` in a given vector frame
1303
+ for assignment. This private method invokes no security check. Use
1304
+ this method at your own risk.
1305
+
1306
+ The components with respect to other frames having the same domain
1307
+ as the provided vector frame are kept. To delete them, use the
1308
+ method :meth:`_set_comp_unsafe` instead.
1309
+
1310
+ INPUT:
1311
+
1312
+ - ``basis`` -- (default: ``None``) vector frame in which the
1313
+ components are defined; if ``None``, the components are assumed
1314
+ to refer to the tensor field domain's default frame
1315
+
1316
+ OUTPUT:
1317
+
1318
+ - components in the given frame, as a
1319
+ :class:`~sage.tensor.modules.comp.Components`; if such
1320
+ components did not exist previously, they are created
1321
+
1322
+ TESTS::
1323
+
1324
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
1325
+ sage: U = M.open_subset('U') # complement of the North pole
1326
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
1327
+ sage: V = M.open_subset('V') # complement of the South pole
1328
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
1329
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
1330
+ sage: e_uv = c_uv.frame()
1331
+ sage: t = M.tensor_field(1, 2, name='t')
1332
+ sage: t._add_comp_unsafe(e_uv)
1333
+ 3-indices components w.r.t. Coordinate frame (V, (∂/∂u,∂/∂v))
1334
+ sage: t._add_comp_unsafe(e_uv)[1,0,1] = u+v
1335
+ sage: t.display(e_uv)
1336
+ t = (u + v) ∂/∂v⊗du⊗dv
1337
+
1338
+ Setting the components in a new frame::
1339
+
1340
+ sage: e = V.vector_frame('e')
1341
+ sage: t._add_comp_unsafe(e)
1342
+ 3-indices components w.r.t. Vector frame (V, (e_0,e_1))
1343
+ sage: t._add_comp_unsafe(e)[0,1,1] = u*v
1344
+ sage: t.display(e)
1345
+ t = u*v e_0⊗e^1⊗e^1
1346
+
1347
+ The components with respect to ``e_uv`` are kept::
1348
+
1349
+ sage: t.display(e_uv)
1350
+ t = (u + v) ∂/∂v⊗du⊗dv
1351
+ """
1352
+ if basis is None:
1353
+ basis = self._domain._def_frame
1354
+ self._del_derived() # deletes the derived quantities
1355
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1356
+ return rst._add_comp_unsafe(basis)
1357
+
1358
+ def add_comp(self, basis=None) -> Components:
1359
+ r"""
1360
+ Return the components of ``self`` in a given vector frame
1361
+ for assignment.
1362
+
1363
+ The components with respect to other frames having the same domain
1364
+ as the provided vector frame are kept. To delete them, use the
1365
+ method :meth:`set_comp` instead.
1366
+
1367
+ INPUT:
1368
+
1369
+ - ``basis`` -- (default: ``None``) vector frame in which the
1370
+ components are defined; if ``None``, the components are assumed
1371
+ to refer to the tensor field domain's default frame
1372
+
1373
+ OUTPUT:
1374
+
1375
+ - components in the given frame, as a
1376
+ :class:`~sage.tensor.modules.comp.Components`; if such
1377
+ components did not exist previously, they are created
1378
+
1379
+ EXAMPLES::
1380
+
1381
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
1382
+ sage: U = M.open_subset('U') # complement of the North pole
1383
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
1384
+ sage: V = M.open_subset('V') # complement of the South pole
1385
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
1386
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
1387
+ sage: e_uv = c_uv.frame()
1388
+ sage: t = M.tensor_field(1, 2, name='t')
1389
+ sage: t.add_comp(e_uv)
1390
+ 3-indices components w.r.t. Coordinate frame (V, (∂/∂u,∂/∂v))
1391
+ sage: t.add_comp(e_uv)[1,0,1] = u+v
1392
+ sage: t.display(e_uv)
1393
+ t = (u + v) ∂/∂v⊗du⊗dv
1394
+
1395
+ Setting the components in a new frame::
1396
+
1397
+ sage: e = V.vector_frame('e')
1398
+ sage: t.add_comp(e)
1399
+ 3-indices components w.r.t. Vector frame (V, (e_0,e_1))
1400
+ sage: t.add_comp(e)[0,1,1] = u*v
1401
+ sage: t.display(e)
1402
+ t = u*v e_0⊗e^1⊗e^1
1403
+
1404
+ The components with respect to ``e_uv`` are kept::
1405
+
1406
+ sage: t.display(e_uv)
1407
+ t = (u + v) ∂/∂v⊗du⊗dv
1408
+
1409
+ Since zero is a special element, its components cannot be changed::
1410
+
1411
+ sage: z = M.tensor_field_module((1, 1)).zero()
1412
+ sage: z.add_comp(e_uv)[1, 1] = u^2
1413
+ Traceback (most recent call last):
1414
+ ...
1415
+ ValueError: the components of an immutable element cannot be
1416
+ changed
1417
+ """
1418
+ if self.is_immutable():
1419
+ raise ValueError("the components of an immutable element "
1420
+ "cannot be changed")
1421
+ self._is_zero = False # a priori
1422
+ if basis is None:
1423
+ basis = self._domain._def_frame
1424
+ self._del_derived() # deletes the derived quantities
1425
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1426
+ return rst.add_comp(basis=basis)
1427
+
1428
+ def add_comp_by_continuation(self, frame, subdomain, chart=None):
1429
+ r"""
1430
+ Set components with respect to a vector frame by continuation of the
1431
+ coordinate expression of the components in a subframe.
1432
+
1433
+ The continuation is performed by demanding that the components have
1434
+ the same coordinate expression as those on the restriction of the
1435
+ frame to a given subdomain.
1436
+
1437
+ INPUT:
1438
+
1439
+ - ``frame`` -- vector frame `e` in which the components are to be set
1440
+ - ``subdomain`` -- open subset of `e`'s domain in which the
1441
+ components are known or can be evaluated from other components
1442
+ - ``chart`` -- (default: ``None``) coordinate chart on `e`'s domain in
1443
+ which the extension of the expression of the components is to be
1444
+ performed; if ``None``, the default's chart of `e`'s domain is
1445
+ assumed
1446
+
1447
+ EXAMPLES:
1448
+
1449
+ Components of a vector field on the sphere `S^2`::
1450
+
1451
+ sage: M = Manifold(2, 'S^2', start_index=1)
1452
+
1453
+ The two open subsets covered by stereographic coordinates (North and South)::
1454
+
1455
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
1456
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
1457
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() # stereographic coordinates
1458
+ sage: transf = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
1459
+ ....: intersection_name='W', restrictions1= x^2+y^2!=0,
1460
+ ....: restrictions2= u^2+v^2!=0)
1461
+ sage: inv = transf.inverse()
1462
+ sage: W = U.intersection(V) # The complement of the two poles
1463
+ sage: eU = c_xy.frame() ; eV = c_uv.frame()
1464
+ sage: a = M.vector_field({eU: [x, 2+y]}, name='a')
1465
+
1466
+ At this stage, the vector field has been defined only on the open
1467
+ subset ``U`` (through its components in the frame ``eU``)::
1468
+
1469
+ sage: a.display(eU)
1470
+ a = x ∂/∂x + (y + 2) ∂/∂y
1471
+
1472
+ The components with respect to the restriction of ``eV`` to the common
1473
+ subdomain ``W``, in terms of the ``(u,v)`` coordinates, are obtained
1474
+ by a change-of-frame formula on ``W``::
1475
+
1476
+ sage: a.display(eV.restrict(W), c_uv.restrict(W))
1477
+ a = (-4*u*v - u) ∂/∂u + (2*u^2 - 2*v^2 - v) ∂/∂v
1478
+
1479
+ The continuation consists in extending the definition of the vector
1480
+ field to the whole open subset ``V`` by demanding that the components
1481
+ in the frame eV have the same coordinate expression as the above one::
1482
+
1483
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
1484
+
1485
+ We have then::
1486
+
1487
+ sage: a.display(eV)
1488
+ a = (-4*u*v - u) ∂/∂u + (2*u^2 - 2*v^2 - v) ∂/∂v
1489
+
1490
+ and `a` is defined on the entire manifold `S^2`.
1491
+ """
1492
+ if self.is_immutable():
1493
+ raise ValueError("the components of an immutable element "
1494
+ "cannot be changed")
1495
+ dom = frame._domain
1496
+ if not dom.is_subset(self._domain):
1497
+ raise ValueError("the vector frame is not defined on a subset " +
1498
+ "of the tensor field domain")
1499
+ if chart is None:
1500
+ chart = dom._def_chart
1501
+ sframe = frame.restrict(subdomain)
1502
+ schart = chart.restrict(subdomain)
1503
+ scomp = self.comp(sframe)
1504
+ resu = self._add_comp_unsafe(frame) # _del_derived is performed here
1505
+ for ind in resu.non_redundant_index_generator():
1506
+ resu[[ind]] = dom.scalar_field({chart: scomp[[ind]].expr(schart)})
1507
+
1508
+ def add_expr_from_subdomain(self, frame, subdomain):
1509
+ r"""
1510
+ Add an expression to an existing component from a subdomain.
1511
+
1512
+ INPUT:
1513
+
1514
+ - ``frame`` -- vector frame `e` in which the components are to be set
1515
+ - ``subdomain`` -- open subset of `e`'s domain in which the
1516
+ components have additional expressions
1517
+
1518
+ EXAMPLES:
1519
+
1520
+ We are going to consider a vector field in `\RR^3` along the 2-sphere::
1521
+
1522
+ sage: M = Manifold(3, 'M', structure='Riemannian')
1523
+ sage: S = Manifold(2, 'S', structure='Riemannian')
1524
+ sage: E.<X,Y,Z> = M.chart()
1525
+
1526
+ Let us define ``S`` in terms of stereographic charts::
1527
+
1528
+ sage: U = S.open_subset('U')
1529
+ sage: V = S.open_subset('V')
1530
+ sage: S.declare_union(U,V)
1531
+ sage: stereoN.<x,y> = U.chart()
1532
+ sage: stereoS.<xp,yp> = V.chart("xp:x' yp:y'")
1533
+ sage: stereoN_to_S = stereoN.transition_map(stereoS,
1534
+ ....: (x/(x^2+y^2), y/(x^2+y^2)),
1535
+ ....: intersection_name='W',
1536
+ ....: restrictions1= x^2+y^2!=0,
1537
+ ....: restrictions2= xp^2+yp^2!=0)
1538
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1539
+ sage: W = U.intersection(V)
1540
+ sage: stereoN_W = stereoN.restrict(W)
1541
+ sage: stereoS_W = stereoS.restrict(W)
1542
+
1543
+ The embedding of `S^2` in `\RR^3`::
1544
+
1545
+ sage: phi = S.diff_map(M, {(stereoN, E): [2*x/(1+x^2+y^2),
1546
+ ....: 2*y/(1+x^2+y^2),
1547
+ ....: (x^2+y^2-1)/(1+x^2+y^2)],
1548
+ ....: (stereoS, E): [2*xp/(1+xp^2+yp^2),
1549
+ ....: 2*yp/(1+xp^2+yp^2),
1550
+ ....: (1-xp^2-yp^2)/(1+xp^2+yp^2)]},
1551
+ ....: name='Phi', latex_name=r'\Phi')
1552
+
1553
+ To define a vector field ``v`` along ``S`` taking its values in ``M``,
1554
+ we first set the components on ``U``::
1555
+
1556
+ sage: v = M.vector_field(name='v').along(phi)
1557
+ sage: vU = v.restrict(U)
1558
+ sage: vU[:] = [x,y,x**2+y**2]
1559
+
1560
+ But because ``M`` is parallelizable, these components can be extended
1561
+ to ``S`` itself::
1562
+
1563
+ sage: v.add_comp_by_continuation(E.frame().along(phi), U)
1564
+
1565
+ One can see that ``v`` is not yet fully defined: the components
1566
+ (scalar fields) do not have values on the whole manifold::
1567
+
1568
+ sage: sorted(v._components.values())[0]._comp[(0,)].display()
1569
+ S → ℝ
1570
+ on U: (x, y) ↦ x
1571
+ on W: (xp, yp) ↦ xp/(xp^2 + yp^2)
1572
+
1573
+ To fix that, we first extend the components from ``W`` to ``V`` using
1574
+ :meth:`add_comp_by_continuation`::
1575
+
1576
+ sage: v.add_comp_by_continuation(E.frame().along(phi).restrict(V),
1577
+ ....: W, stereoS)
1578
+
1579
+ Then, the expression on the subdomain ``V`` is added to the
1580
+ already known components on ``S`` by::
1581
+
1582
+ sage: v.add_expr_from_subdomain(E.frame().along(phi), V)
1583
+
1584
+ The definition of ``v`` is now complete::
1585
+
1586
+ sage: sorted(v._components.values())[0]._comp[(2,)].display()
1587
+ S → ℝ
1588
+ on U: (x, y) ↦ x^2 + y^2
1589
+ on V: (xp, yp) ↦ 1/(xp^2 + yp^2)
1590
+ """
1591
+ if self.is_immutable():
1592
+ raise ValueError("the expressions of an immutable element "
1593
+ "cannot be changed")
1594
+ dom = frame._domain
1595
+ if not dom.is_subset(self._domain):
1596
+ raise ValueError("the vector frame is not defined on a subset " +
1597
+ "of the tensor field domain")
1598
+ if frame not in self.restrict(frame.domain())._components:
1599
+ raise ValueError("the tensor doesn't have an expression in "
1600
+ "the frame"+frame._repr_())
1601
+ comp = self._add_comp_unsafe(frame) # the components stay the same
1602
+ scomp = self.restrict(subdomain).comp(frame.restrict(subdomain))
1603
+ for ind in comp.non_redundant_index_generator():
1604
+ comp[[ind]]._express.update(scomp[[ind]]._express)
1605
+
1606
+ rst = self._restrictions.copy()
1607
+ self._del_derived() # may delete restrictions
1608
+ self._restrictions = rst
1609
+
1610
+ def comp(self, basis=None, from_basis=None):
1611
+ r"""
1612
+ Return the components in a given vector frame.
1613
+
1614
+ If the components are not known already, they are computed by the
1615
+ tensor change-of-basis formula from components in another vector frame.
1616
+
1617
+ INPUT:
1618
+
1619
+ - ``basis`` -- (default: ``None``) vector frame in which the components
1620
+ are required; if none is provided, the components are assumed to
1621
+ refer to the tensor field domain's default frame
1622
+ - ``from_basis`` -- (default: ``None``) vector frame from which the
1623
+ required components are computed, via the tensor change-of-basis
1624
+ formula, if they are not known already in the basis ``basis``
1625
+
1626
+ OUTPUT:
1627
+
1628
+ - components in the vector frame ``basis``, as a
1629
+ :class:`~sage.tensor.modules.comp.Components`
1630
+
1631
+ EXAMPLES:
1632
+
1633
+ Components of a type-`(1,1)` tensor field defined on two
1634
+ open subsets::
1635
+
1636
+ sage: M = Manifold(2, 'M')
1637
+ sage: U = M.open_subset('U')
1638
+ sage: c_xy.<x, y> = U.chart()
1639
+ sage: e = U.default_frame() ; e
1640
+ Coordinate frame (U, (∂/∂x,∂/∂y))
1641
+ sage: V = M.open_subset('V')
1642
+ sage: c_uv.<u, v> = V.chart()
1643
+ sage: f = V.default_frame() ; f
1644
+ Coordinate frame (V, (∂/∂u,∂/∂v))
1645
+ sage: M.declare_union(U,V) # M is the union of U and V
1646
+ sage: t = M.tensor_field(1,1, name='t')
1647
+ sage: t[e,0,0] = - x + y^3
1648
+ sage: t[e,0,1] = 2+x
1649
+ sage: t[f,1,1] = - u*v
1650
+ sage: t.comp(e)
1651
+ 2-indices components w.r.t. Coordinate frame (U, (∂/∂x,∂/∂y))
1652
+ sage: t.comp(e)[:]
1653
+ [y^3 - x x + 2]
1654
+ [ 0 0]
1655
+ sage: t.comp(f)
1656
+ 2-indices components w.r.t. Coordinate frame (V, (∂/∂u,∂/∂v))
1657
+ sage: t.comp(f)[:]
1658
+ [ 0 0]
1659
+ [ 0 -u*v]
1660
+
1661
+ Since ``e`` is ``M``'s default frame, the argument ``e`` can
1662
+ be omitted::
1663
+
1664
+ sage: e is M.default_frame()
1665
+ True
1666
+ sage: t.comp() is t.comp(e)
1667
+ True
1668
+
1669
+ Example of computation of the components via a change of frame::
1670
+
1671
+ sage: a = V.automorphism_field()
1672
+ sage: a[:] = [[1+v, -u^2], [0, 1-u]]
1673
+ sage: h = f.new_frame(a, 'h')
1674
+ sage: t.comp(h)
1675
+ 2-indices components w.r.t. Vector frame (V, (h_0,h_1))
1676
+ sage: t.comp(h)[:]
1677
+ [ 0 -u^3*v/(v + 1)]
1678
+ [ 0 -u*v]
1679
+ """
1680
+ if basis is None:
1681
+ basis = self._domain._def_frame
1682
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1683
+ return rst.comp(basis=basis, from_basis=from_basis)
1684
+
1685
+ def display(self, frame=None, chart=None):
1686
+ r"""
1687
+ Display the tensor field in terms of its expansion with respect
1688
+ to a given vector frame.
1689
+
1690
+ The output is either text-formatted (console mode) or LaTeX-formatted
1691
+ (notebook mode).
1692
+
1693
+ INPUT:
1694
+
1695
+ - ``frame`` -- (default: ``None``) vector frame with respect to
1696
+ which the tensor is expanded; if ``frame`` is ``None`` and ``chart``
1697
+ is not ``None``, the coordinate frame associated with ``chart`` is
1698
+ assumed; if both ``frame`` and ``chart`` are ``None``, the default
1699
+ frame of the domain of definition of the tensor field is assumed
1700
+ - ``chart`` -- (default: ``None``) chart with respect to which the
1701
+ components of the tensor field in the selected frame are expressed;
1702
+ if ``None``, the default chart of the vector frame domain is assumed
1703
+
1704
+ EXAMPLES:
1705
+
1706
+ Display of a type-`(1,1)` tensor field on a 2-dimensional manifold::
1707
+
1708
+ sage: M = Manifold(2, 'M')
1709
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
1710
+ sage: M.declare_union(U,V) # M is the union of U and V
1711
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
1712
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
1713
+ ....: intersection_name='W', restrictions1= x>0,
1714
+ ....: restrictions2= u+v>0)
1715
+ sage: uv_to_xy = xy_to_uv.inverse()
1716
+ sage: W = U.intersection(V)
1717
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
1718
+ sage: t = M.tensor_field(1,1, name='t')
1719
+ sage: t[e_xy,:] = [[x, 1], [y, 0]]
1720
+ sage: t.add_comp_by_continuation(e_uv, W, c_uv)
1721
+ sage: t.display(e_xy)
1722
+ t = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
1723
+ sage: t.display(e_uv)
1724
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1725
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1726
+
1727
+ Since ``e_xy`` is ``M``'s default frame, the argument ``e_xy`` can
1728
+ be omitted::
1729
+
1730
+ sage: e_xy is M.default_frame()
1731
+ True
1732
+ sage: t.display()
1733
+ t = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
1734
+
1735
+ Similarly, since ``e_uv`` is ``V``'s default frame, the argument ``e_uv``
1736
+ can be omitted when considering the restriction of ``t`` to ``V``::
1737
+
1738
+ sage: t.restrict(V).display()
1739
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1740
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1741
+
1742
+ If the coordinate expression of the components are to be displayed in
1743
+ a chart distinct from the default one on the considered domain, then
1744
+ the chart has to be passed as the second argument of ``display``.
1745
+ For instance, on `W = U \cap V`, two charts are available:
1746
+ ``c_xy.restrict(W)`` (the default one) and ``c_uv.restrict(W)``.
1747
+ Accordingly, one can have two views of the expansion of ``t`` in the
1748
+ *same* vector frame ``e_uv.restrict(W)``::
1749
+
1750
+ sage: t.display(e_uv.restrict(W)) # W's default chart assumed
1751
+ t = (1/2*x + 1/2*y + 1/2) ∂/∂u⊗du + (1/2*x + 1/2*y - 1/2) ∂/∂u⊗dv
1752
+ + (1/2*x - 1/2*y + 1/2) ∂/∂v⊗du + (1/2*x - 1/2*y - 1/2) ∂/∂v⊗dv
1753
+ sage: t.display(e_uv.restrict(W), c_uv.restrict(W))
1754
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1755
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1756
+
1757
+ As a shortcut, one can pass just a chart to ``display``. It is then
1758
+ understood that the expansion is to be performed with respect to the
1759
+ coordinate frame associated with this chart. Therefore the above
1760
+ command can be abridged to::
1761
+
1762
+ sage: t.display(c_uv.restrict(W))
1763
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1764
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1765
+
1766
+ and one has::
1767
+
1768
+ sage: t.display(c_xy)
1769
+ t = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
1770
+ sage: t.display(c_uv)
1771
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1772
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1773
+ sage: t.display(c_xy.restrict(W))
1774
+ t = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
1775
+ sage: t.restrict(W).display(c_uv.restrict(W))
1776
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1777
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1778
+
1779
+ One can ask for the display with respect to a frame in which ``t`` has
1780
+ not been initialized yet (this will automatically trigger the use of
1781
+ the change-of-frame formula for tensors)::
1782
+
1783
+ sage: a = V.automorphism_field()
1784
+ sage: a[:] = [[1+v, -u^2], [0, 1-u]]
1785
+ sage: f = e_uv.new_frame(a, 'f')
1786
+ sage: [f[i].display() for i in M.irange()]
1787
+ [f_0 = (v + 1) ∂/∂u, f_1 = -u^2 ∂/∂u + (-u + 1) ∂/∂v]
1788
+ sage: t.display(f)
1789
+ t = -1/2*(u^2*v + 1)/(u - 1) f_0⊗f^0
1790
+ - 1/2*(2*u^3 - 5*u^2 - (u^4 + u^3 - u^2)*v + 3*u - 1)/((u - 1)*v + u - 1) f_0⊗f^1
1791
+ - 1/2*(v^2 + 2*v + 1)/(u - 1) f_1⊗f^0
1792
+ + 1/2*(u^2 + (u^2 + u - 1)*v - u + 1)/(u - 1) f_1⊗f^1
1793
+
1794
+ A shortcut of ``display()`` is ``disp()``::
1795
+
1796
+ sage: t.disp(e_uv)
1797
+ t = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv
1798
+ + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
1799
+ """
1800
+ if frame is None:
1801
+ if chart is not None:
1802
+ frame = chart.frame()
1803
+ else:
1804
+ if self._vmodule._dest_map.is_identity():
1805
+ frame = self._domain._def_frame
1806
+ else:
1807
+ for rst in self._restrictions.values():
1808
+ try:
1809
+ return rst.display()
1810
+ except ValueError:
1811
+ pass
1812
+ if frame is None: # should be "is still None" ;-)
1813
+ raise ValueError("a frame must be provided for the display")
1814
+ else:
1815
+ try:
1816
+ frame0 = frame.frame()
1817
+ # if this succeeds, frame is actually not a vector frame, but
1818
+ # a coordinate chart
1819
+ if chart is None:
1820
+ chart = frame
1821
+ frame = frame0
1822
+ except AttributeError:
1823
+ # case of a genuine vector frame
1824
+ pass
1825
+ rst = self.restrict(frame._domain, dest_map=frame._dest_map)
1826
+ return rst.display(frame, chart)
1827
+
1828
+ disp = display
1829
+
1830
+ def display_comp(self, frame=None, chart=None, coordinate_labels=True,
1831
+ only_nonzero=True, only_nonredundant=False):
1832
+ r"""
1833
+ Display the tensor components with respect to a given frame,
1834
+ one per line.
1835
+
1836
+ The output is either text-formatted (console mode) or LaTeX-formatted
1837
+ (notebook mode).
1838
+
1839
+ INPUT:
1840
+
1841
+ - ``frame`` -- (default: ``None``) vector frame with respect to which
1842
+ the tensor field components are defined; if ``None``, then
1843
+
1844
+ * if ``chart`` is not ``None``, the coordinate frame associated to
1845
+ ``chart`` is used
1846
+ * otherwise, the default basis of the vector field module on which
1847
+ the tensor field is defined is used
1848
+
1849
+ - ``chart`` -- (default: ``None``) chart specifying the coordinate
1850
+ expression of the components; if ``None``, the default chart of the
1851
+ tensor field domain is used
1852
+ - ``coordinate_labels`` -- boolean (default: ``True``); if ``True``,
1853
+ coordinate symbols are used by default (instead of integers) as
1854
+ index labels whenever ``frame`` is a coordinate frame
1855
+ - ``only_nonzero`` -- boolean (default: ``True``); if ``True``, only
1856
+ nonzero components are displayed
1857
+ - ``only_nonredundant`` -- boolean (default: ``False``); if ``True``,
1858
+ only nonredundant components are displayed in case of symmetries
1859
+
1860
+ EXAMPLES:
1861
+
1862
+ Display of the components of a type-`(1,1)` tensor field defined
1863
+ on two open subsets::
1864
+
1865
+ sage: M = Manifold(2, 'M')
1866
+ sage: U = M.open_subset('U')
1867
+ sage: c_xy.<x, y> = U.chart()
1868
+ sage: e = U.default_frame()
1869
+ sage: V = M.open_subset('V')
1870
+ sage: c_uv.<u, v> = V.chart()
1871
+ sage: f = V.default_frame()
1872
+ sage: M.declare_union(U,V) # M is the union of U and V
1873
+ sage: t = M.tensor_field(1,1, name='t')
1874
+ sage: t[e,0,0] = - x + y^3
1875
+ sage: t[e,0,1] = 2+x
1876
+ sage: t[f,1,1] = - u*v
1877
+ sage: t.display_comp(e)
1878
+ t^x_x = y^3 - x
1879
+ t^x_y = x + 2
1880
+ sage: t.display_comp(f)
1881
+ t^v_v = -u*v
1882
+
1883
+ Components in a chart frame::
1884
+
1885
+ sage: t.display_comp(chart=c_xy)
1886
+ t^x_x = y^3 - x
1887
+ t^x_y = x + 2
1888
+ sage: t.display_comp(chart=c_uv)
1889
+ t^v_v = -u*v
1890
+
1891
+ See documentation of
1892
+ :meth:`sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.display_comp`
1893
+ for more options.
1894
+ """
1895
+ if frame is None:
1896
+ if chart is not None:
1897
+ frame = chart.frame()
1898
+ else:
1899
+ if self._vmodule._dest_map.is_identity():
1900
+ frame = self._domain.default_frame()
1901
+ else:
1902
+ for rst in self._restrictions.values():
1903
+ try:
1904
+ return rst.display_comp(chart=chart,
1905
+ coordinate_labels=coordinate_labels,
1906
+ only_nonzero=only_nonzero,
1907
+ only_nonredundant=only_nonredundant)
1908
+ except ValueError:
1909
+ pass
1910
+ if frame is None: # should be "is still None" ;-)
1911
+ raise ValueError("a frame must be provided for the display")
1912
+ rst = self.restrict(frame.domain(), dest_map=frame._dest_map)
1913
+ return rst.display_comp(frame=frame, chart=chart,
1914
+ coordinate_labels=coordinate_labels,
1915
+ only_nonzero=only_nonzero,
1916
+ only_nonredundant=only_nonredundant)
1917
+
1918
+ def __getitem__(self, args):
1919
+ r"""
1920
+ Return a component with respect to some frame.
1921
+
1922
+ INPUT:
1923
+
1924
+ - ``args`` -- list of indices defining the component; if ``[:]`` is
1925
+ provided, all the components are returned
1926
+
1927
+ The frame can be passed as the first item of ``args``. If not, the
1928
+ default frame of the tensor field's domain is assumed. If ``args``
1929
+ is a string, this method acts as a shortcut for tensor contractions
1930
+ and symmetrizations, the string containing abstract indices.
1931
+
1932
+ TESTS::
1933
+
1934
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
1935
+ sage: U = M.open_subset('U') # complement of the North pole
1936
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
1937
+ sage: V = M.open_subset('V') # complement of the South pole
1938
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
1939
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
1940
+ sage: e_xy = c_xy.frame()
1941
+ sage: t = M.tensor_field(1, 1, name='t')
1942
+ sage: t[e_xy, :] = [[x+y, -2], [3*y^2, x*y]]
1943
+ sage: t.__getitem__((1,0))
1944
+ 3*y^2
1945
+ sage: t.__getitem__((1,1))
1946
+ x*y
1947
+ sage: t.__getitem__((e_xy,1,0))
1948
+ 3*y^2
1949
+ sage: t.__getitem__(slice(None))
1950
+ [x + y -2]
1951
+ [3*y^2 x*y]
1952
+ sage: t.__getitem__((e_xy,slice(None)))
1953
+ [x + y -2]
1954
+ [3*y^2 x*y]
1955
+ sage: t.__getitem__('^a_a') # trace
1956
+ Scalar field on the 2-dimensional differentiable manifold M
1957
+ sage: t.__getitem__('^a_a').display()
1958
+ M → ℝ
1959
+ on U: (x, y) ↦ (x + 1)*y + x
1960
+ """
1961
+ if isinstance(args, str): # tensor with specified indices
1962
+ return TensorWithIndices(self, args).update()
1963
+ if isinstance(args, list): # case of [[...]] syntax
1964
+ if not isinstance(args[0], (int, Integer, slice)):
1965
+ frame = args[0]
1966
+ args = args[1:]
1967
+ else:
1968
+ frame = self._domain._def_frame
1969
+ else:
1970
+ if isinstance(args, (int, Integer, slice)):
1971
+ frame = self._domain._def_frame
1972
+ elif not isinstance(args[0], (int, Integer, slice)):
1973
+ frame = args[0]
1974
+ args = args[1:]
1975
+ else:
1976
+ frame = self._domain._def_frame
1977
+ return self.comp(frame)[args]
1978
+
1979
+ def __setitem__(self, args, value):
1980
+ r"""
1981
+ Set a component with respect to some vector frame.
1982
+
1983
+ INPUT:
1984
+
1985
+ - ``args`` -- list of indices; if ``[:]`` is provided, all the
1986
+ components are set; the frame can be passed as the first item
1987
+ of ``args``; if not, the default frame of the tensor field's
1988
+ domain is assumed
1989
+ - ``value`` -- the value to be set or a list of values if
1990
+ ``args = [:]``
1991
+
1992
+ TESTS::
1993
+
1994
+ sage: M = Manifold(2, 'M') # the 2-dimensional sphere S^2
1995
+ sage: U = M.open_subset('U') # complement of the North pole
1996
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
1997
+ sage: V = M.open_subset('V') # complement of the South pole
1998
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
1999
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
2000
+ sage: e_xy = c_xy.frame()
2001
+ sage: t = M.tensor_field(1, 1, name='t')
2002
+ sage: t.__setitem__((e_xy, 0, 1), x+y^2)
2003
+ sage: t.display(e_xy)
2004
+ t = (y^2 + x) ∂/∂x⊗dy
2005
+ sage: t.__setitem__((0, 1), x+y^2) # same as above since e_xy is the default frame on M
2006
+ sage: t.display()
2007
+ t = (y^2 + x) ∂/∂x⊗dy
2008
+ sage: t.__setitem__(slice(None), [[x+y, -2], [3*y^2, x*y]])
2009
+ sage: t.display()
2010
+ t = (x + y) ∂/∂x⊗dx - 2 ∂/∂x⊗dy + 3*y^2 ∂/∂y⊗dx + x*y ∂/∂y⊗dy
2011
+ """
2012
+ if isinstance(args, list): # case of [[...]] syntax
2013
+ if not isinstance(args[0], (int, Integer, slice)):
2014
+ frame = args[0]
2015
+ args = args[1:]
2016
+ else:
2017
+ frame = self._domain._def_frame
2018
+ else:
2019
+ if isinstance(args, (int, Integer, slice)):
2020
+ frame = self._domain._def_frame
2021
+ elif not isinstance(args[0], (int, Integer, slice)):
2022
+ frame = args[0]
2023
+ args = args[1:]
2024
+ else:
2025
+ frame = self._domain._def_frame
2026
+ self.set_comp(frame)[args] = value
2027
+
2028
+ def copy_from(self, other):
2029
+ r"""
2030
+ Make ``self`` a copy of ``other``.
2031
+
2032
+ INPUT:
2033
+
2034
+ - ``other`` -- other tensor field, in the same module as ``self``
2035
+
2036
+ .. NOTE::
2037
+
2038
+ While the derived quantities are not copied, the name is kept.
2039
+
2040
+ .. WARNING::
2041
+
2042
+ All previous defined components and restrictions will be deleted!
2043
+
2044
+ EXAMPLES::
2045
+
2046
+ sage: M = Manifold(2, 'M')
2047
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2048
+ sage: M.declare_union(U,V) # M is the union of U and V
2049
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2050
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2051
+ ....: intersection_name='W', restrictions1= x>0,
2052
+ ....: restrictions2= u+v>0)
2053
+ sage: uv_to_xy = xy_to_uv.inverse()
2054
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2055
+ sage: t = M.tensor_field(1, 1, name='t')
2056
+ sage: t[e_xy,:] = [[x+y, 0], [2, 1-y]]
2057
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2058
+ sage: s = M.tensor_field(1, 1, name='s')
2059
+ sage: s.copy_from(t)
2060
+ sage: s.display(e_xy)
2061
+ s = (x + y) ∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2062
+ sage: s == t
2063
+ True
2064
+
2065
+ While the original tensor field is modified, the copy is not::
2066
+
2067
+ sage: t[e_xy,0,0] = -1
2068
+ sage: t.display(e_xy)
2069
+ t = -∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2070
+ sage: s.display(e_xy)
2071
+ s = (x + y) ∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2072
+ sage: s == t
2073
+ False
2074
+ """
2075
+ if self.is_immutable():
2076
+ raise ValueError("the components of an immutable element "
2077
+ "cannot be changed")
2078
+ if other not in self.parent():
2079
+ raise TypeError("the original must be an element of "
2080
+ f"{self.parent()}")
2081
+ self._del_derived()
2082
+ self._del_restrictions() # delete restrictions
2083
+ for dom, rst in other._restrictions.items():
2084
+ self._restrictions[dom] = rst.copy(name=self._name,
2085
+ latex_name=self._latex_name)
2086
+ self._is_zero = other._is_zero
2087
+
2088
+ def copy(self, name=None, latex_name=None):
2089
+ r"""
2090
+ Return an exact copy of ``self``.
2091
+
2092
+ INPUT:
2093
+
2094
+ - ``name`` -- (default: ``None``) name given to the copy
2095
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
2096
+ copy; if none is provided, the LaTeX symbol is set to ``name``
2097
+
2098
+ .. NOTE::
2099
+
2100
+ The name and the derived quantities are not copied.
2101
+
2102
+ EXAMPLES:
2103
+
2104
+ Copy of a type-`(1,1)` tensor field on a 2-dimensional manifold::
2105
+
2106
+ sage: M = Manifold(2, 'M')
2107
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2108
+ sage: M.declare_union(U,V) # M is the union of U and V
2109
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2110
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2111
+ ....: intersection_name='W', restrictions1= x>0,
2112
+ ....: restrictions2= u+v>0)
2113
+ sage: uv_to_xy = xy_to_uv.inverse()
2114
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2115
+ sage: t = M.tensor_field(1, 1, name='t')
2116
+ sage: t[e_xy,:] = [[x+y, 0], [2, 1-y]]
2117
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2118
+ sage: s = t.copy(); s
2119
+ Tensor field of type (1,1) on the 2-dimensional differentiable
2120
+ manifold M
2121
+ sage: s.display(e_xy)
2122
+ (x + y) ∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2123
+ sage: s == t
2124
+ True
2125
+
2126
+ If the original tensor field is modified, the copy is not::
2127
+
2128
+ sage: t[e_xy,0,0] = -1
2129
+ sage: t.display(e_xy)
2130
+ t = -∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2131
+ sage: s.display(e_xy)
2132
+ (x + y) ∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2133
+ sage: s == t
2134
+ False
2135
+ """
2136
+ resu = self._new_instance()
2137
+ # set resu name
2138
+ if name is not None:
2139
+ resu._name = name
2140
+ if latex_name is None:
2141
+ resu._latex_name = name
2142
+ if latex_name is not None:
2143
+ resu._latex_name = latex_name
2144
+ # set restrictions
2145
+ for dom, rst in self._restrictions.items():
2146
+ resu._restrictions[dom] = rst.copy(name=name,
2147
+ latex_name=latex_name)
2148
+ resu._is_zero = self._is_zero
2149
+ return resu
2150
+
2151
+ def _common_subdomains(self, other):
2152
+ r"""
2153
+ Return the list of subdomains of ``self._domain`` on which
2154
+ both ``self`` and ``other`` have known restrictions.
2155
+
2156
+ TESTS::
2157
+
2158
+ sage: M = Manifold(2, 'M')
2159
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2160
+ sage: M.declare_union(U,V) # M is the union of U and V
2161
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2162
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2163
+ ....: intersection_name='W', restrictions1= x>0,
2164
+ ....: restrictions2= u+v>0)
2165
+ sage: uv_to_xy = xy_to_uv.inverse()
2166
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2167
+ sage: t = M.tensor_field(1, 1, name='t')
2168
+ sage: t[e_xy,:] = [[x+y, 0], [2, 0]]
2169
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2170
+ sage: sorted(t._common_subdomains(t), key=str)
2171
+ [Open subset U of the 2-dimensional differentiable manifold M,
2172
+ Open subset V of the 2-dimensional differentiable manifold M,
2173
+ Open subset W of the 2-dimensional differentiable manifold M]
2174
+ sage: a = M.tensor_field(1, 1, name='a')
2175
+ sage: t._common_subdomains(a)
2176
+ []
2177
+ sage: a[e_xy, 0, 1] = 0
2178
+ sage: t._common_subdomains(a)
2179
+ [Open subset U of the 2-dimensional differentiable manifold M]
2180
+ sage: a[e_uv, 0, 0] = 0
2181
+ sage: sorted(t._common_subdomains(a), key=str)
2182
+ [Open subset U of the 2-dimensional differentiable manifold M,
2183
+ Open subset V of the 2-dimensional differentiable manifold M]
2184
+ """
2185
+ resu = []
2186
+ for dom in self._restrictions:
2187
+ if dom in other._restrictions:
2188
+ resu.append(dom)
2189
+ return resu
2190
+
2191
+ def __eq__(self, other):
2192
+ r"""
2193
+ Comparison (equality) operator.
2194
+
2195
+ INPUT:
2196
+
2197
+ - ``other`` -- a tensor field or 0
2198
+
2199
+ OUTPUT: ``True`` if ``self`` is equal to ``other`` and ``False`` otherwise
2200
+
2201
+ TESTS::
2202
+
2203
+ sage: M = Manifold(2, 'M')
2204
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2205
+ sage: M.declare_union(U,V) # M is the union of U and V
2206
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2207
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2208
+ ....: intersection_name='W', restrictions1= x>0,
2209
+ ....: restrictions2= u+v>0)
2210
+ sage: uv_to_xy = xy_to_uv.inverse()
2211
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2212
+ sage: t = M.tensor_field(1, 1, name='t')
2213
+ sage: t[e_xy,:] = [[x+y, 0], [2, 1-y]]
2214
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2215
+ sage: t == t
2216
+ True
2217
+ sage: t == t.copy()
2218
+ True
2219
+ sage: a = M.tensor_field(1, 1, name='a')
2220
+ sage: a.set_restriction(t.restrict(U))
2221
+ sage: t == a # False since a has not been defined on V
2222
+ False
2223
+ sage: a.set_restriction(t.restrict(V))
2224
+ sage: t == a # True now
2225
+ True
2226
+ sage: a[e_xy, 0, 0] = -1
2227
+ sage: t == a # False since a has been reset on U (domain of e_xy)
2228
+ False
2229
+ sage: t.parent().zero() == 0
2230
+ True
2231
+ """
2232
+ from sage.manifolds.differentiable.mixed_form import MixedForm
2233
+
2234
+ if other is self:
2235
+ return True
2236
+ if other in ZZ: # to compare with 0
2237
+ if other == 0:
2238
+ return self.is_zero()
2239
+ return False
2240
+ elif isinstance(other, MixedForm):
2241
+ # use comparison of MixedForm:
2242
+ return other == self
2243
+ elif not isinstance(other, TensorField):
2244
+ return False
2245
+ else: # other is another tensor field
2246
+ if other._vmodule != self._vmodule:
2247
+ return False
2248
+ if other._tensor_type != self._tensor_type:
2249
+ return False
2250
+ # Non-trivial open covers of the domain:
2251
+ for oc in self._domain.open_covers(trivial=False):
2252
+ resu = True
2253
+ for dom in oc:
2254
+ try:
2255
+ resu = resu and \
2256
+ bool(self.restrict(dom) == other.restrict(dom))
2257
+ except ValueError:
2258
+ break
2259
+ else:
2260
+ # If this point is reached, no exception has occurred; hence
2261
+ # the result is valid and can be returned:
2262
+ return resu
2263
+ # If this point is reached, the comparison has not been possible
2264
+ # on any open cover; we then compare the restrictions to
2265
+ # subdomains:
2266
+ if not self._restrictions:
2267
+ return False # self is not initialized
2268
+ if len(self._restrictions) != len(other._restrictions):
2269
+ return False # the restrictions are not on the same subdomains
2270
+ resu = True
2271
+ for dom, rst in self._restrictions.items():
2272
+ if dom in other._restrictions:
2273
+ resu = resu and bool(rst == other._restrictions[dom])
2274
+ else:
2275
+ return False # the restrictions are not on the same
2276
+ # subdomains
2277
+ return resu
2278
+
2279
+ def __ne__(self, other):
2280
+ r"""
2281
+ Inequality operator.
2282
+
2283
+ INPUT:
2284
+
2285
+ - ``other`` -- a tensor field or 0
2286
+
2287
+ OUTPUT:
2288
+
2289
+ - ``True`` if ``self`` is different from ``other`` and ``False``
2290
+ otherwise
2291
+
2292
+ TESTS::
2293
+
2294
+ sage: M = Manifold(2, 'M')
2295
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2296
+ sage: M.declare_union(U,V) # M is the union of U and V
2297
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2298
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2299
+ ....: intersection_name='W', restrictions1= x>0,
2300
+ ....: restrictions2= u+v>0)
2301
+ sage: uv_to_xy = xy_to_uv.inverse()
2302
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2303
+ sage: t = M.tensor_field(1, 1, name='t')
2304
+ sage: t[e_xy,:] = [[x+y, 0], [2, 1-y]]
2305
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2306
+ sage: t != t
2307
+ False
2308
+ sage: t != t.copy()
2309
+ False
2310
+ sage: t != 0
2311
+ True
2312
+ """
2313
+ return not (self == other)
2314
+
2315
+ def __pos__(self):
2316
+ r"""
2317
+ Unary plus operator.
2318
+
2319
+ OUTPUT: an exact copy of ``self``
2320
+
2321
+ TESTS::
2322
+
2323
+ sage: M = Manifold(2, 'M')
2324
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2325
+ sage: M.declare_union(U,V) # M is the union of U and V
2326
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2327
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2328
+ ....: intersection_name='W', restrictions1= x>0,
2329
+ ....: restrictions2= u+v>0)
2330
+ sage: uv_to_xy = xy_to_uv.inverse()
2331
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2332
+ sage: t = M.tensor_field(1, 1, name='t')
2333
+ sage: t[e_xy,:] = [[x+y, 0], [2, 1-y]]
2334
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2335
+ sage: s = t.__pos__(); s
2336
+ Tensor field +t of type (1,1) on the 2-dimensional differentiable
2337
+ manifold M
2338
+ sage: s.display(e_xy)
2339
+ +t = (x + y) ∂/∂x⊗dx + 2 ∂/∂y⊗dx + (-y + 1) ∂/∂y⊗dy
2340
+ """
2341
+ resu = self._new_instance()
2342
+ for dom, rst in self._restrictions.items():
2343
+ resu._restrictions[dom] = + rst
2344
+ # Compose names:
2345
+ from sage.tensor.modules.format_utilities import (
2346
+ format_unop_latex,
2347
+ format_unop_txt,
2348
+ )
2349
+ resu._name = format_unop_txt('+', self._name)
2350
+ resu._latex_name = format_unop_latex(r'+', self._latex_name)
2351
+ return resu
2352
+
2353
+ def __neg__(self):
2354
+ r"""
2355
+ Unary minus operator.
2356
+
2357
+ OUTPUT: the tensor field `-T`, where `T` is ``self``
2358
+
2359
+ TESTS::
2360
+
2361
+ sage: M = Manifold(2, 'M')
2362
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2363
+ sage: M.declare_union(U,V) # M is the union of U and V
2364
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2365
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2366
+ ....: intersection_name='W', restrictions1= x>0,
2367
+ ....: restrictions2= u+v>0)
2368
+ sage: uv_to_xy = xy_to_uv.inverse()
2369
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2370
+ sage: t = M.tensor_field(1, 1, name='t')
2371
+ sage: t[e_xy, :] = [[x, -x], [y, -y]]
2372
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2373
+ sage: t.display(e_xy)
2374
+ t = x ∂/∂x⊗dx - x ∂/∂x⊗dy + y ∂/∂y⊗dx - y ∂/∂y⊗dy
2375
+ sage: t.display(e_uv)
2376
+ t = u ∂/∂u⊗dv + v ∂/∂v⊗dv
2377
+ sage: s = t.__neg__(); s
2378
+ Tensor field -t of type (1,1) on the 2-dimensional differentiable
2379
+ manifold M
2380
+ sage: s.display(e_xy)
2381
+ -t = -x ∂/∂x⊗dx + x ∂/∂x⊗dy - y ∂/∂y⊗dx + y ∂/∂y⊗dy
2382
+ sage: s.display(e_uv)
2383
+ -t = -u ∂/∂u⊗dv - v ∂/∂v⊗dv
2384
+ sage: s == -t # indirect doctest
2385
+ True
2386
+ """
2387
+ resu = self._new_instance()
2388
+ for dom, rst in self._restrictions.items():
2389
+ resu._restrictions[dom] = - rst
2390
+ # Compose names:
2391
+ from sage.tensor.modules.format_utilities import (
2392
+ format_unop_latex,
2393
+ format_unop_txt,
2394
+ )
2395
+ resu._name = format_unop_txt('-', self._name)
2396
+ resu._latex = format_unop_latex(r'-', self._latex_name)
2397
+ return resu
2398
+
2399
+ ######### ModuleElement arithmetic operators ########
2400
+
2401
+ def _add_(self, other):
2402
+ r"""
2403
+ Tensor field addition.
2404
+
2405
+ INPUT:
2406
+
2407
+ - ``other`` -- a tensor field, in the same tensor module as ``self``
2408
+
2409
+ OUTPUT:
2410
+
2411
+ - the tensor field resulting from the addition of ``self``
2412
+ and ``other``
2413
+
2414
+ TESTS::
2415
+
2416
+ sage: M = Manifold(2, 'M')
2417
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2418
+ sage: M.declare_union(U,V) # M is the union of U and V
2419
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2420
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2421
+ ....: intersection_name='W', restrictions1= x>0,
2422
+ ....: restrictions2= u+v>0)
2423
+ sage: uv_to_xy = xy_to_uv.inverse()
2424
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2425
+ sage: a = M.tensor_field(1, 1, name='a')
2426
+ sage: a[e_xy,:] = [[x, 1], [y, 0]]
2427
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2428
+ sage: b = M.tensor_field(1, 1, name='b')
2429
+ sage: b[e_xy,:] = [[2, y], [x, -x]]
2430
+ sage: b.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2431
+ sage: s = a._add_(b); s
2432
+ Tensor field a+b of type (1,1) on the 2-dimensional differentiable
2433
+ manifold M
2434
+ sage: a.display(e_xy)
2435
+ a = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
2436
+ sage: b.display(e_xy)
2437
+ b = 2 ∂/∂x⊗dx + y ∂/∂x⊗dy + x ∂/∂y⊗dx - x ∂/∂y⊗dy
2438
+ sage: s.display(e_xy)
2439
+ a+b = (x + 2) ∂/∂x⊗dx + (y + 1) ∂/∂x⊗dy + (x + y) ∂/∂y⊗dx - x ∂/∂y⊗dy
2440
+ sage: s == a + b # indirect doctest
2441
+ True
2442
+ sage: z = a.parent().zero(); z
2443
+ Tensor field zero of type (1,1) on the 2-dimensional differentiable
2444
+ manifold M
2445
+ sage: a._add_(z) == a
2446
+ True
2447
+ sage: z._add_(a) == a
2448
+ True
2449
+ """
2450
+ # Case zero:
2451
+ if self._is_zero:
2452
+ return other
2453
+ if other._is_zero:
2454
+ return self
2455
+ # Generic case:
2456
+ resu_rst = {}
2457
+ for dom in self._common_subdomains(other):
2458
+ resu_rst[dom] = self._restrictions[dom] + other._restrictions[dom]
2459
+ some_rst = next(iter(resu_rst.values()))
2460
+ resu_sym = some_rst._sym
2461
+ resu_antisym = some_rst._antisym
2462
+ resu = self._vmodule.tensor(self._tensor_type, sym=resu_sym,
2463
+ antisym=resu_antisym)
2464
+ resu._restrictions = resu_rst
2465
+ if self._name is not None and other._name is not None:
2466
+ resu._name = self._name + '+' + other._name
2467
+ if self._latex_name is not None and other._latex_name is not None:
2468
+ resu._latex_name = self._latex_name + '+' + other._latex_name
2469
+ return resu
2470
+
2471
+ def _sub_(self, other):
2472
+ r"""
2473
+ Tensor field subtraction.
2474
+
2475
+ INPUT:
2476
+
2477
+ - ``other`` -- a tensor field, in the same tensor module as ``self``
2478
+
2479
+ OUTPUT:
2480
+
2481
+ - the tensor field resulting from the subtraction of ``other``
2482
+ from ``self``
2483
+
2484
+ TESTS::
2485
+
2486
+ sage: M = Manifold(2, 'M')
2487
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2488
+ sage: M.declare_union(U,V) # M is the union of U and V
2489
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2490
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2491
+ ....: intersection_name='W', restrictions1= x>0,
2492
+ ....: restrictions2= u+v>0)
2493
+ sage: uv_to_xy = xy_to_uv.inverse()
2494
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2495
+ sage: a = M.tensor_field(1, 1, name='a')
2496
+ sage: a[e_xy,:] = [[x, 1], [y, 0]]
2497
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2498
+ sage: b = M.tensor_field(1, 1, name='b')
2499
+ sage: b[e_xy,:] = [[2, y], [x, -x]]
2500
+ sage: b.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2501
+ sage: s = a._sub_(b); s
2502
+ Tensor field a-b of type (1,1) on the 2-dimensional differentiable
2503
+ manifold M
2504
+ sage: a.display(e_xy)
2505
+ a = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
2506
+ sage: b.display(e_xy)
2507
+ b = 2 ∂/∂x⊗dx + y ∂/∂x⊗dy + x ∂/∂y⊗dx - x ∂/∂y⊗dy
2508
+ sage: s.display(e_xy)
2509
+ a-b = (x - 2) ∂/∂x⊗dx + (-y + 1) ∂/∂x⊗dy + (-x + y) ∂/∂y⊗dx + x ∂/∂y⊗dy
2510
+ sage: s == a - b
2511
+ True
2512
+ sage: z = a.parent().zero()
2513
+ sage: a._sub_(z) == a
2514
+ True
2515
+ sage: z._sub_(a) == -a
2516
+ True
2517
+ """
2518
+ # Case zero:
2519
+ if self._is_zero:
2520
+ return -other
2521
+ if other._is_zero:
2522
+ return self
2523
+ # Generic case:
2524
+ resu_rst = {}
2525
+ for dom in self._common_subdomains(other):
2526
+ resu_rst[dom] = self._restrictions[dom] - other._restrictions[dom]
2527
+ some_rst = next(iter(resu_rst.values()))
2528
+ resu_sym = some_rst._sym
2529
+ resu_antisym = some_rst._antisym
2530
+ resu = self._vmodule.tensor(self._tensor_type, sym=resu_sym,
2531
+ antisym=resu_antisym)
2532
+ resu._restrictions = resu_rst
2533
+ if self._name is not None and other._name is not None:
2534
+ resu._name = self._name + '-' + other._name
2535
+ if self._latex_name is not None and other._latex_name is not None:
2536
+ resu._latex_name = self._latex_name + '-' + other._latex_name
2537
+ return resu
2538
+
2539
+ def _rmul_(self, scalar):
2540
+ r"""
2541
+ Reflected multiplication operator: performs ``scalar * self``.
2542
+
2543
+ This is actually the multiplication by an element of the ring over
2544
+ which the tensor field module is constructed.
2545
+
2546
+ INPUT:
2547
+
2548
+ - ``scalar`` -- scalar field in the scalar field algebra over which
2549
+ the module containing ``self`` is defined
2550
+
2551
+ OUTPUT: the tensor field ``scalar * self``
2552
+
2553
+ TESTS::
2554
+
2555
+ sage: M = Manifold(2, 'M')
2556
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2557
+ sage: M.declare_union(U,V) # M is the union of U and V
2558
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2559
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2560
+ ....: intersection_name='W', restrictions1= x>0,
2561
+ ....: restrictions2= u+v>0)
2562
+ sage: uv_to_xy = xy_to_uv.inverse()
2563
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2564
+ sage: a = M.tensor_field(1, 1, name='a')
2565
+ sage: a[e_xy,:] = [[x, 1], [y, 0]]
2566
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2567
+ sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2)}, name='f')
2568
+ sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
2569
+ sage: f.display()
2570
+ f: M → ℝ
2571
+ on U: (x, y) ↦ 1/(x^2 + y^2 + 1)
2572
+ on V: (u, v) ↦ 2/(u^2 + v^2 + 2)
2573
+ sage: s = a._rmul_(f); s
2574
+ Tensor field f*a of type (1,1) on the 2-dimensional differentiable
2575
+ manifold M
2576
+ sage: a.display(e_xy)
2577
+ a = x ∂/∂x⊗dx + ∂/∂x⊗dy + y ∂/∂y⊗dx
2578
+ sage: s.display(e_xy)
2579
+ f*a = x/(x^2 + y^2 + 1) ∂/∂x⊗dx + 1/(x^2 + y^2 + 1) ∂/∂x⊗dy + y/(x^2 + y^2 + 1) ∂/∂y⊗dx
2580
+ sage: a.display(e_uv)
2581
+ a = (1/2*u + 1/2) ∂/∂u⊗du + (1/2*u - 1/2) ∂/∂u⊗dv + (1/2*v + 1/2) ∂/∂v⊗du + (1/2*v - 1/2) ∂/∂v⊗dv
2582
+ sage: s.display(e_uv)
2583
+ f*a = (u + 1)/(u^2 + v^2 + 2) ∂/∂u⊗du + (u - 1)/(u^2 + v^2 + 2) ∂/∂u⊗dv + (v + 1)/(u^2 + v^2 + 2) ∂/∂v⊗du + (v - 1)/(u^2 + v^2 + 2) ∂/∂v⊗dv
2584
+ sage: s == f*a # indirect doctest
2585
+ True
2586
+ sage: z = a.parent().zero(); z
2587
+ Tensor field zero of type (1,1) on the 2-dimensional differentiable
2588
+ manifold M
2589
+ sage: a._rmul_(M.zero_scalar_field()) == z
2590
+ True
2591
+ sage: z._rmul_(f) == z
2592
+ True
2593
+ """
2594
+ # Case zero:
2595
+ if scalar._is_zero:
2596
+ return self.parent().zero()
2597
+ # Case one:
2598
+ if scalar is self._domain._one_scalar_field:
2599
+ return self
2600
+ # Generic case:
2601
+ resu = self._new_instance()
2602
+ for dom, rst in self._restrictions.items():
2603
+ resu._restrictions[dom] = scalar.restrict(dom) * rst
2604
+ # Compose names:
2605
+ from sage.tensor.modules.format_utilities import (
2606
+ format_mul_latex,
2607
+ format_mul_txt,
2608
+ )
2609
+ resu_name = format_mul_txt(scalar._name, '*', self._name)
2610
+ resu_latex = format_mul_latex(scalar._latex_name, r' \cdot ',
2611
+ self._latex_name)
2612
+ resu.set_name(name=resu_name, latex_name=resu_latex)
2613
+ return resu
2614
+
2615
+ ######### End of ModuleElement arithmetic operators ########
2616
+
2617
+ # TODO: Move to acted_upon or _rmul_
2618
+ def __mul__(self, other: TensorField) -> TensorField:
2619
+ r"""
2620
+ Tensor product (or multiplication of the right by a scalar).
2621
+
2622
+ INPUT:
2623
+
2624
+ - ``other`` -- tensor field on the same manifold as ``self`` (or an
2625
+ object that can be coerced to a scalar field on the same manifold
2626
+ as ``self``)
2627
+
2628
+ OUTPUT:
2629
+
2630
+ - the tensor field resulting from the tensor product of ``self``
2631
+ with ``other`` (or from the product ``other * self`` if ``other``
2632
+ is a scalar)
2633
+
2634
+ TESTS::
2635
+
2636
+ sage: M = Manifold(2, 'M')
2637
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2638
+ sage: M.declare_union(U,V) # M is the union of U and V
2639
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2640
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2641
+ ....: intersection_name='W', restrictions1= x>0,
2642
+ ....: restrictions2= u+v>0)
2643
+ sage: uv_to_xy = xy_to_uv.inverse()
2644
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2645
+ sage: a = M.tensor_field(1, 1, name='a')
2646
+ sage: a[e_xy,:] = [[x, 1], [y, 0]]
2647
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2648
+
2649
+ Tensor product with another tensor field::
2650
+
2651
+ sage: b = M.vector_field(name='b')
2652
+ sage: b[e_xy, :] = [x, y]
2653
+ sage: b.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2654
+ sage: s = a.__mul__(b); s
2655
+ Tensor field of type (2,1) on the 2-dimensional differentiable
2656
+ manifold M
2657
+ sage: s.display(e_xy)
2658
+ a⊗b = x^2 ∂/∂x⊗∂/∂x⊗dx + x ∂/∂x⊗∂/∂x⊗dy + x*y ∂/∂x⊗∂/∂y⊗dx
2659
+ + y ∂/∂x⊗∂/∂y⊗dy + x*y ∂/∂y⊗∂/∂x⊗dx + y^2 ∂/∂y⊗∂/∂y⊗dx
2660
+ sage: s.display(e_uv)
2661
+ a⊗b = (1/2*u^2 + 1/2*u) ∂/∂u⊗∂/∂u⊗du + (1/2*u^2 - 1/2*u) ∂/∂u⊗∂/∂u⊗dv
2662
+ + 1/2*(u + 1)*v ∂/∂u⊗∂/∂v⊗du + 1/2*(u - 1)*v ∂/∂u⊗∂/∂v⊗dv
2663
+ + (1/2*u*v + 1/2*u) ∂/∂v⊗∂/∂u⊗du + (1/2*u*v - 1/2*u) ∂/∂v⊗∂/∂u⊗dv
2664
+ + (1/2*v^2 + 1/2*v) ∂/∂v⊗∂/∂v⊗du + (1/2*v^2 - 1/2*v) ∂/∂v⊗∂/∂v⊗dv
2665
+
2666
+ Multiplication on the right by a scalar field::
2667
+
2668
+ sage: f = M.scalar_field({c_xy: x*y}, name='f')
2669
+ sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
2670
+ sage: s = a.__mul__(f); s
2671
+ Tensor field f*a of type (1,1) on the 2-dimensional differentiable
2672
+ manifold M
2673
+ sage: s.display(e_xy)
2674
+ f*a = x^2*y ∂/∂x⊗dx + x*y ∂/∂x⊗dy + x*y^2 ∂/∂y⊗dx
2675
+ sage: s.display(e_uv)
2676
+ f*a = (1/8*u^3 - 1/8*(u + 1)*v^2 + 1/8*u^2) ∂/∂u⊗du
2677
+ + (1/8*u^3 - 1/8*(u - 1)*v^2 - 1/8*u^2) ∂/∂u⊗dv
2678
+ + (1/8*u^2*v - 1/8*v^3 + 1/8*u^2 - 1/8*v^2) ∂/∂v⊗du
2679
+ + (1/8*u^2*v - 1/8*v^3 - 1/8*u^2 + 1/8*v^2) ∂/∂v⊗dv
2680
+ sage: s == f*a
2681
+ True
2682
+
2683
+ Multiplication on the right by a number::
2684
+
2685
+ sage: s = a.__mul__(2); s
2686
+ Tensor field of type (1,1) on the 2-dimensional differentiable
2687
+ manifold M
2688
+ sage: s.display(e_xy)
2689
+ 2*x ∂/∂x⊗dx + 2 ∂/∂x⊗dy + 2*y ∂/∂y⊗dx
2690
+ sage: s.display(e_uv)
2691
+ (u + 1) ∂/∂u⊗du + (u - 1) ∂/∂u⊗dv + (v + 1) ∂/∂v⊗du
2692
+ + (v - 1) ∂/∂v⊗dv
2693
+ sage: s.restrict(U) == 2*a.restrict(U)
2694
+ True
2695
+ sage: s.restrict(V) == 2*a.restrict(V)
2696
+ True
2697
+ sage: s == 2*a
2698
+ True
2699
+
2700
+ Test with SymPy as calculus engine::
2701
+
2702
+ sage: M.set_calculus_method('sympy')
2703
+ sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
2704
+ sage: s = a.__mul__(f); s
2705
+ Tensor field f*a of type (1,1) on the 2-dimensional differentiable
2706
+ manifold M
2707
+ sage: s.display(e_xy)
2708
+ f*a = x**2*y ∂/∂x⊗dx + x*y ∂/∂x⊗dy + x*y**2 ∂/∂y⊗dx
2709
+ sage: s.display(e_uv)
2710
+ f*a = (u**3/8 + u**2/8 - u*v**2/8 - v**2/8) ∂/∂u⊗du + (u**3/8 -
2711
+ u**2/8 - u*v**2/8 + v**2/8) ∂/∂u⊗dv + (u**2*v/8 + u**2/8 -
2712
+ v**3/8 - v**2/8) ∂/∂v⊗du + (u**2*v/8 - u**2/8 - v**3/8 +
2713
+ v**2/8) ∂/∂v⊗dv
2714
+ sage: s == f*a
2715
+ True
2716
+ """
2717
+ from sage.manifolds.differentiable.mixed_form import MixedForm
2718
+ if isinstance(other, MixedForm):
2719
+ return other.parent()(self)._mul_(other)
2720
+ if not isinstance(other, TensorField):
2721
+ # Multiplication by a scalar field or a number
2722
+ return other * self
2723
+ # Tensor product:
2724
+ dom_resu = self._domain.intersection(other._domain)
2725
+ ambient_dom_resu = self._ambient_domain.intersection(other._ambient_domain)
2726
+ self_r = self.restrict(dom_resu)
2727
+ other_r = other.restrict(dom_resu)
2728
+ if ambient_dom_resu.is_manifestly_parallelizable():
2729
+ # call of the FreeModuleTensor version:
2730
+ return FreeModuleTensor.__mul__(self_r, other_r)
2731
+ dest_map = self._vmodule._dest_map
2732
+ dest_map_resu = dest_map.restrict(dom_resu, subcodomain=ambient_dom_resu)
2733
+ vmodule = dom_resu.vector_field_module(dest_map=dest_map_resu)
2734
+ com_dom = []
2735
+ for dom in self_r._restrictions:
2736
+ if dom in other_r._restrictions:
2737
+ com_dom.append(dom)
2738
+ resu_rst = []
2739
+ for dom in com_dom:
2740
+ self_rr = self_r._restrictions[dom]
2741
+ other_rr = other_r._restrictions[dom]
2742
+ resu_rst.append(self_rr * other_rr)
2743
+ k1, l1 = self._tensor_type
2744
+ k2, l2 = other._tensor_type
2745
+ resu = vmodule.tensor((k1+k2, l1+l2),
2746
+ sym=resu_rst[0]._sym,
2747
+ antisym=resu_rst[0]._antisym)
2748
+ for rst in resu_rst:
2749
+ resu._restrictions[rst._domain] = rst
2750
+
2751
+ return resu
2752
+
2753
+ def __truediv__(self, scalar) -> TensorField:
2754
+ r"""
2755
+ Division by a scalar field.
2756
+
2757
+ INPUT:
2758
+
2759
+ - ``scalar`` -- scalar field in the scalar field algebra over which
2760
+ the module containing ``self`` is defined
2761
+
2762
+ OUTPUT: the tensor field ``scalar * self``
2763
+
2764
+ TESTS::
2765
+
2766
+ sage: M = Manifold(2, 'M')
2767
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2768
+ sage: M.declare_union(U,V) # M is the union of U and V
2769
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2770
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2771
+ ....: intersection_name='W', restrictions1= x>0,
2772
+ ....: restrictions2= u+v>0)
2773
+ sage: uv_to_xy = xy_to_uv.inverse()
2774
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2775
+ sage: a = M.tensor_field(1, 1, name='a')
2776
+ sage: a[e_xy,:] = [[x, 1], [y, 0]]
2777
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2778
+ sage: f = M.scalar_field({c_xy: 1/(1+x^2+y^2)}, name='f')
2779
+ sage: f.add_expr_by_continuation(c_uv, U.intersection(V))
2780
+ sage: f.display()
2781
+ f: M → ℝ
2782
+ on U: (x, y) ↦ 1/(x^2 + y^2 + 1)
2783
+ on V: (u, v) ↦ 2/(u^2 + v^2 + 2)
2784
+ sage: s = a.__truediv__(f); s
2785
+ Tensor field of type (1,1) on the 2-dimensional differentiable
2786
+ manifold M
2787
+ sage: s.display(e_xy)
2788
+ (x^3 + x*y^2 + x) ∂/∂x⊗dx + (x^2 + y^2 + 1) ∂/∂x⊗dy
2789
+ + (y^3 + (x^2 + 1)*y) ∂/∂y⊗dx
2790
+ sage: f*s == a
2791
+ True
2792
+
2793
+ Division by a number::
2794
+
2795
+ sage: s = a.__truediv__(2); s
2796
+ Tensor field of type (1,1) on the 2-dimensional differentiable
2797
+ manifold M
2798
+ sage: s.display(e_xy)
2799
+ 1/2*x ∂/∂x⊗dx + 1/2 ∂/∂x⊗dy + 1/2*y ∂/∂y⊗dx
2800
+ sage: s.display(e_uv)
2801
+ (1/4*u + 1/4) ∂/∂u⊗du + (1/4*u - 1/4) ∂/∂u⊗dv
2802
+ + (1/4*v + 1/4) ∂/∂v⊗du + (1/4*v - 1/4) ∂/∂v⊗dv
2803
+ sage: s == a/2
2804
+ True
2805
+ sage: 2*s == a
2806
+ True
2807
+ """
2808
+ resu = self._new_instance()
2809
+ for dom, rst in self._restrictions.items():
2810
+ resu._restrictions[dom] = rst / scalar
2811
+ return resu
2812
+
2813
+ def __call__(self, *args):
2814
+ r"""
2815
+ The tensor field acting on 1-forms and vector fields as a
2816
+ multilinear map.
2817
+
2818
+ In the particular case of tensor field of type `(1,1)`, the action can
2819
+ be on a single vector field, the tensor field being identified to a
2820
+ field of tangent-space endomorphisms. The output is then a vector
2821
+ field.
2822
+
2823
+ INPUT:
2824
+
2825
+ - ``*args`` -- list of `k` 1-forms and `l` vector fields, ``self``
2826
+ being a tensor of type `(k,l)`
2827
+
2828
+ OUTPUT:
2829
+
2830
+ - either the scalar field resulting from the action of ``self`` on
2831
+ the 1-forms and vector fields passed as arguments or the vector
2832
+ field resulting from the action of ``self`` as a field of
2833
+ tangent-space endomorphisms (case of a type-(1,1) tensor field)
2834
+
2835
+ TESTS:
2836
+
2837
+ Action of a tensor field of type `(1,1)` on the 2-sphere::
2838
+
2839
+ sage: M = Manifold(2, 'M')
2840
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
2841
+ sage: M.declare_union(U,V) # M is the union of U and V
2842
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
2843
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
2844
+ ....: intersection_name='W', restrictions1= x>0,
2845
+ ....: restrictions2= u+v>0)
2846
+ sage: uv_to_xy = xy_to_uv.inverse()
2847
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
2848
+ sage: t = M.tensor_field(1,1, name='t')
2849
+ sage: t[e_xy,:] = [[x, 1], [y, 0]]
2850
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2851
+ sage: w = M.vector_field(name='w')
2852
+ sage: w[e_xy,:] = [y^2, x^2]
2853
+ sage: w.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2854
+ sage: a = M.one_form(name='a')
2855
+ sage: a[e_xy,:] = [-1+y, x*y]
2856
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
2857
+
2858
+ The tensor field acting on a pair (1-form, vector field)::
2859
+
2860
+ sage: s = t.__call__(a,w); s
2861
+ Scalar field t(a,w) on the 2-dimensional differentiable manifold M
2862
+ sage: s.display()
2863
+ t(a,w): M → ℝ
2864
+ on U: (x, y) ↦ x*y^4 + x*y^3 + x^2*y - x*y^2 - x^2
2865
+ on V: (u, v) ↦ 1/32*u^5 - 1/32*(3*u + 2)*v^4 + 1/32*v^5
2866
+ + 1/16*u^4 + 1/16*(u^2 + 2*u - 4)*v^3 + 1/16*(u^3 - 4)*v^2
2867
+ - 1/4*u^2 - 1/32*(3*u^4 + 4*u^3 - 8*u^2 + 16*u)*v
2868
+ sage: s.restrict(U) == t.restrict(U)(a.restrict(U), w.restrict(U))
2869
+ True
2870
+ sage: s.restrict(V) == t.restrict(V)(a.restrict(V), w.restrict(V))
2871
+ True
2872
+
2873
+ The tensor field acting on vector field, as a field of tangent-space
2874
+ endomorphisms::
2875
+
2876
+ sage: s = t.__call__(w); s
2877
+ Vector field t(w) on the 2-dimensional differentiable manifold M
2878
+ sage: s.display(e_xy)
2879
+ t(w) = (x*y^2 + x^2) ∂/∂x + y^3 ∂/∂y
2880
+ sage: s.display(e_uv)
2881
+ t(w) = (1/4*u^3 + 1/4*(u + 1)*v^2 + 1/4*u^2 - 1/2*(u^2 - u)*v) ∂/∂u
2882
+ + (-1/4*(2*u - 1)*v^2 + 1/4*v^3 + 1/4*u^2 + 1/4*(u^2 + 2*u)*v) ∂/∂v
2883
+ sage: s.restrict(U) == t.restrict(U)(w.restrict(U))
2884
+ True
2885
+ sage: s.restrict(V) == t.restrict(V)(w.restrict(V))
2886
+ True
2887
+ """
2888
+ p = len(args)
2889
+ if p == 1 and self._tensor_type == (1,1):
2890
+ # type-(1,1) tensor acting as a field of tangent-space
2891
+ # endomorphisms:
2892
+ vector = args[0]
2893
+ if vector._tensor_type != (1,0):
2894
+ raise TypeError("the argument must be a vector field")
2895
+ dom_resu = self._domain.intersection(vector._domain)
2896
+ if dom_resu.is_manifestly_parallelizable():
2897
+ # call of the TensorFieldParal version:
2898
+ return self.restrict(dom_resu)(vector.restrict(dom_resu))
2899
+ if self._name is not None and vector._name is not None:
2900
+ name_resu = "{}({})".format(self._name, vector._name)
2901
+ else:
2902
+ name_resu = None
2903
+ if self._latex_name is not None and vector._latex_name is not None:
2904
+ latex_name_resu = r"{}\left({}\right)".format(self._latex_name,
2905
+ vector._latex_name)
2906
+ else:
2907
+ latex_name_resu = None
2908
+ dest_map = vector._vmodule._dest_map
2909
+ dest_map_resu = dest_map.restrict(dom_resu)
2910
+ resu = dom_resu.vector_field(name=name_resu,
2911
+ latex_name=latex_name_resu,
2912
+ dest_map=dest_map_resu)
2913
+ for dom in self._common_subdomains(vector):
2914
+ if dom.is_subset(dom_resu):
2915
+ resu._restrictions[dom] = \
2916
+ self._restrictions[dom](vector._restrictions[dom])
2917
+ return resu
2918
+ # Generic case
2919
+ if p != self._tensor_rank:
2920
+ raise TypeError("{} arguments must be ".format(self._tensor_rank) +
2921
+ "provided")
2922
+ # Domain of the result
2923
+ dom_resu = self._domain
2924
+ ambient_dom = self._ambient_domain
2925
+ for arg in args:
2926
+ dom_resu = dom_resu.intersection(arg._domain)
2927
+ ambient_dom = ambient_dom.intersection(arg._ambient_domain)
2928
+ self_r = self.restrict(dom_resu)
2929
+ args_r = [args[i].restrict(dom_resu) for i in range(p)]
2930
+ if ambient_dom.is_manifestly_parallelizable():
2931
+ # TensorFieldParal version
2932
+ return self_r(*args_r)
2933
+ else:
2934
+ resu = dom_resu.scalar_field()
2935
+ com_dom = []
2936
+ for dom in self_r._restrictions:
2937
+ for arg in args_r:
2938
+ if dom not in arg._restrictions:
2939
+ break
2940
+ else:
2941
+ com_dom.append(dom)
2942
+ for dom in com_dom:
2943
+ self_rr = self_r._restrictions[dom]
2944
+ args_rr = [args_r[i]._restrictions[dom] for i in range(p)]
2945
+ resu_rr = self_rr(*args_rr)
2946
+ if resu_rr.is_trivial_zero():
2947
+ for chart in resu_rr._domain._atlas:
2948
+ resu._express[chart] = chart.zero_function()
2949
+ else:
2950
+ for chart, expr in resu_rr._express.items():
2951
+ resu._express[chart] = expr
2952
+ if resu.is_trivial_zero():
2953
+ return dom_resu._zero_scalar_field
2954
+ # Name of the output:
2955
+ res_name = None
2956
+ if self._name is not None:
2957
+ res_name = self._name + "("
2958
+ for i in range(p-1):
2959
+ if args[i]._name is not None:
2960
+ res_name += args[i]._name + ","
2961
+ else:
2962
+ res_name = None
2963
+ break
2964
+ if res_name is not None:
2965
+ if args[p-1]._name is not None:
2966
+ res_name += args[p-1]._name + ")"
2967
+ else:
2968
+ res_name = None
2969
+ resu._name = res_name
2970
+ # LaTeX symbol of the output:
2971
+ res_latex = None
2972
+ if self._latex_name is not None:
2973
+ res_latex = self._latex_name + r"\left("
2974
+ for i in range(p-1):
2975
+ if args[i]._latex_name is not None:
2976
+ res_latex += args[i]._latex_name + ","
2977
+ else:
2978
+ res_latex = None
2979
+ break
2980
+ if res_latex is not None:
2981
+ if args[p-1]._latex_name is not None:
2982
+ res_latex += args[p-1]._latex_name + r"\right)"
2983
+ else:
2984
+ res_latex = None
2985
+ resu._latex_name = res_latex
2986
+ return resu
2987
+
2988
+ def trace(
2989
+ self,
2990
+ pos1=0,
2991
+ pos2=1,
2992
+ using: Optional[
2993
+ Union[PseudoRiemannianMetric, SymplecticForm, PoissonTensorField]
2994
+ ] = None,
2995
+ ):
2996
+ r"""
2997
+ Trace (contraction) on two slots of the tensor field.
2998
+
2999
+ If a non-degenerate form is provided, the trace of a `(0,2)` tensor field
3000
+ is computed by first raising the last index.
3001
+
3002
+ INPUT:
3003
+
3004
+ - ``pos1`` -- (default: 0) position of the first index for the
3005
+ contraction, with the convention ``pos1=0`` for the first slot
3006
+ - ``pos2`` -- (default: 1) position of the second index for the
3007
+ contraction, with the same convention as for ``pos1``. The variance
3008
+ type of ``pos2`` must be opposite to that of ``pos1``
3009
+ - ``using`` -- (default: ``None``) a non-degenerate form
3010
+
3011
+ OUTPUT:
3012
+
3013
+ - tensor field resulting from the ``(pos1, pos2)`` contraction
3014
+
3015
+ EXAMPLES:
3016
+
3017
+ Trace of a type-`(1,1)` tensor field on a 2-dimensional
3018
+ non-parallelizable manifold::
3019
+
3020
+ sage: M = Manifold(2, 'M')
3021
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
3022
+ sage: M.declare_union(U,V) # M is the union of U and V
3023
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
3024
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
3025
+ ....: intersection_name='W', restrictions1= x>0,
3026
+ ....: restrictions2= u+v>0)
3027
+ sage: uv_to_xy = xy_to_uv.inverse()
3028
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
3029
+ sage: W = U.intersection(V)
3030
+ sage: a = M.tensor_field(1,1, name='a')
3031
+ sage: a[e_xy,:] = [[1,x], [2,y]]
3032
+ sage: a.add_comp_by_continuation(e_uv, W, chart=c_uv)
3033
+ sage: s = a.trace() ; s
3034
+ Scalar field on the 2-dimensional differentiable manifold M
3035
+ sage: s.display()
3036
+ M → ℝ
3037
+ on U: (x, y) ↦ y + 1
3038
+ on V: (u, v) ↦ 1/2*u - 1/2*v + 1
3039
+ sage: s == a.trace(0,1) # explicit mention of the positions
3040
+ True
3041
+
3042
+ The trace of a type-`(0,2)` tensor field using a metric::
3043
+
3044
+ sage: g = M.metric('g')
3045
+ sage: g[0,0], g[0,1], g[1,1] = 1, 0, 1
3046
+ sage: g.trace(using=g).display()
3047
+ M → ℝ
3048
+ on U: (x, y) ↦ 2
3049
+ on W: (u, v) ↦ 2
3050
+
3051
+ Instead of the explicit call to the method :meth:`trace`, one
3052
+ may use the index notation with Einstein convention (summation over
3053
+ repeated indices); it suffices to pass the indices as a string inside
3054
+ square brackets::
3055
+
3056
+ sage: a['^i_i']
3057
+ Scalar field on the 2-dimensional differentiable manifold M
3058
+ sage: a['^i_i'] == s
3059
+ True
3060
+
3061
+ Any letter can be used to denote the repeated index::
3062
+
3063
+ sage: a['^b_b'] == s
3064
+ True
3065
+
3066
+ Trace of a type-`(1,2)` tensor field::
3067
+
3068
+ sage: b = M.tensor_field(1,2, name='b') ; b
3069
+ Tensor field b of type (1,2) on the 2-dimensional differentiable
3070
+ manifold M
3071
+ sage: b[e_xy,:] = [[[0,x+y], [y,0]], [[0,2], [3*x,-2]]]
3072
+ sage: b.add_comp_by_continuation(e_uv, W, chart=c_uv) # long time
3073
+ sage: s = b.trace(0,1) ; s # contraction on first and second slots
3074
+ 1-form on the 2-dimensional differentiable manifold M
3075
+ sage: s.display(e_xy)
3076
+ 3*x dx + (x + y - 2) dy
3077
+ sage: s.display(e_uv) # long time
3078
+ (5/4*u + 3/4*v - 1) du + (1/4*u + 3/4*v + 1) dv
3079
+
3080
+ Use of the index notation::
3081
+
3082
+ sage: b['^k_ki']
3083
+ 1-form on the 2-dimensional differentiable manifold M
3084
+ sage: b['^k_ki'] == s # long time
3085
+ True
3086
+
3087
+ Indices not involved in the contraction may be replaced by dots::
3088
+
3089
+ sage: b['^k_k.'] == s # long time
3090
+ True
3091
+
3092
+ The symbol ``^`` may be omitted::
3093
+
3094
+ sage: b['k_k.'] == s # long time
3095
+ True
3096
+
3097
+ LaTeX notations are allowed::
3098
+
3099
+ sage: b['^{k}_{ki}'] == s # long time
3100
+ True
3101
+
3102
+ Contraction on first and third slots::
3103
+
3104
+ sage: s = b.trace(0,2) ; s
3105
+ 1-form on the 2-dimensional differentiable manifold M
3106
+ sage: s.display(e_xy)
3107
+ 2 dx + (y - 2) dy
3108
+ sage: s.display(e_uv) # long time
3109
+ (1/4*u - 1/4*v) du + (-1/4*u + 1/4*v + 2) dv
3110
+
3111
+ Use of index notation::
3112
+
3113
+ sage: b['^k_.k'] == s # long time
3114
+ True
3115
+ """
3116
+ if using is not None:
3117
+ if self.tensor_type() != (0, 2):
3118
+ raise ValueError(
3119
+ "trace with respect to a non-degenerate form is only defined for type-(0,2) tensor fields"
3120
+ )
3121
+ return self.up(using, 1).trace()
3122
+
3123
+ # The indices at pos1 and pos2 must be of different types:
3124
+ k_con = self._tensor_type[0]
3125
+ l_cov = self._tensor_type[1]
3126
+ if pos1 < k_con and pos2 < k_con:
3127
+ raise IndexError("contraction on two contravariant indices is " +
3128
+ "not allowed")
3129
+ if pos1 >= k_con and pos2 >= k_con:
3130
+ raise IndexError("contraction on two covariant indices is " +
3131
+ "not allowed")
3132
+ resu_rst = []
3133
+ for rst in self._restrictions.values():
3134
+ resu_rst.append(rst.trace(pos1, pos2))
3135
+ if (k_con, l_cov) == (1,1):
3136
+ # scalar field result
3137
+ resu = self._domain.scalar_field()
3138
+ all_zero = True
3139
+ for rst in resu_rst:
3140
+ if rst == 0:
3141
+ for chart in rst._domain._atlas:
3142
+ resu._express[chart] = 0
3143
+ else:
3144
+ all_zero = False
3145
+ for chart, funct in rst._express.items():
3146
+ resu._express[chart] = funct
3147
+ if all_zero:
3148
+ resu = self._domain._zero_scalar_field
3149
+ else:
3150
+ # tensor field result
3151
+ resu = self._vmodule.tensor((k_con-1, l_cov-1),
3152
+ sym=resu_rst[0]._sym, antisym=resu_rst[0]._antisym)
3153
+ for rst in resu_rst:
3154
+ resu._restrictions[rst._domain] = rst
3155
+ return resu
3156
+
3157
+ def contract(self, *args: Union[int, TensorField]) -> TensorField:
3158
+ r"""
3159
+ Contraction of ``self`` with another tensor field on one or
3160
+ more indices.
3161
+
3162
+ INPUT:
3163
+
3164
+ - ``pos1`` -- positions of the indices in the current tensor field
3165
+ involved in the contraction; ``pos1`` must be a sequence of integers,
3166
+ with 0 standing for the first index position, 1 for the second one,
3167
+ etc.; if ``pos1`` is not provided, a single contraction on the last
3168
+ index position of the tensor field is assumed
3169
+ - ``other`` -- the tensor field to contract with
3170
+ - ``pos2`` -- positions of the indices in ``other`` involved in the
3171
+ contraction, with the same conventions as for ``pos1``; if ``pos2``
3172
+ is not provided, a single contraction on the first index position of
3173
+ ``other`` is assumed
3174
+
3175
+ OUTPUT:
3176
+
3177
+ - tensor field resulting from the contraction at the positions
3178
+ ``pos1`` and ``pos2`` of the tensor field with ``other``
3179
+
3180
+ EXAMPLES:
3181
+
3182
+ Contractions of a type-`(1,1)` tensor field with a type-`(2,0)`
3183
+ one on a 2-dimensional non-parallelizable manifold::
3184
+
3185
+ sage: M = Manifold(2, 'M')
3186
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
3187
+ sage: M.declare_union(U,V) # M is the union of U and V
3188
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
3189
+ sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W',
3190
+ ....: restrictions1= x>0, restrictions2= u+v>0)
3191
+ sage: inv = transf.inverse()
3192
+ sage: W = U.intersection(V)
3193
+ sage: eU = c_xy.frame() ; eV = c_uv.frame()
3194
+ sage: a = M.tensor_field(1, 1, {eU: [[1, x], [0, 2]]}, name='a')
3195
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
3196
+ sage: b = M.tensor_field(2, 0, {eU: [[y, -1], [x+y, 2]]}, name='b')
3197
+ sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
3198
+ sage: s = a.contract(b) ; s # contraction on last index of a and first one of b
3199
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3200
+ manifold M
3201
+
3202
+ Check 1: components with respect to the manifold's default
3203
+ frame (``eU``)::
3204
+
3205
+ sage: all(bool(s[i,j] == sum(a[i,k]*b[k,j] for k in M.irange()))
3206
+ ....: for i in M.irange() for j in M.irange())
3207
+ True
3208
+
3209
+ Check 2: components with respect to the frame ``eV``::
3210
+
3211
+ sage: all(bool(s[eV,i,j] == sum(a[eV,i,k]*b[eV,k,j]
3212
+ ....: for k in M.irange()))
3213
+ ....: for i in M.irange() for j in M.irange())
3214
+ True
3215
+
3216
+ Instead of the explicit call to the method :meth:`contract`, one
3217
+ may use the index notation with Einstein convention (summation over
3218
+ repeated indices); it suffices to pass the indices as a string inside
3219
+ square brackets::
3220
+
3221
+ sage: a['^i_k']*b['^kj'] == s
3222
+ True
3223
+
3224
+ Indices not involved in the contraction may be replaced by dots::
3225
+
3226
+ sage: a['^._k']*b['^k.'] == s
3227
+ True
3228
+
3229
+ LaTeX notation may be used::
3230
+
3231
+ sage: a['^{i}_{k}']*b['^{kj}'] == s
3232
+ True
3233
+
3234
+ Contraction on the last index of ``a`` and last index of ``b``::
3235
+
3236
+ sage: s = a.contract(b, 1) ; s
3237
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3238
+ manifold M
3239
+ sage: a['^i_k']*b['^jk'] == s
3240
+ True
3241
+
3242
+ Contraction on the first index of ``b`` and the last index of ``a``::
3243
+
3244
+ sage: s = b.contract(0,a,1) ; s
3245
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3246
+ manifold M
3247
+ sage: b['^ki']*a['^j_k'] == s
3248
+ True
3249
+
3250
+ The domain of the result is the intersection of the domains of
3251
+ the two tensor fields::
3252
+
3253
+ sage: aU = a.restrict(U) ; bV = b.restrict(V)
3254
+ sage: s = aU.contract(b) ; s
3255
+ Tensor field of type (2,0) on the Open subset U of the
3256
+ 2-dimensional differentiable manifold M
3257
+ sage: s = a.contract(bV) ; s
3258
+ Tensor field of type (2,0) on the Open subset V of the
3259
+ 2-dimensional differentiable manifold M
3260
+ sage: s = aU.contract(bV) ; s
3261
+ Tensor field of type (2,0) on the Open subset W of the
3262
+ 2-dimensional differentiable manifold M
3263
+ sage: s0 = a.contract(b)
3264
+ sage: s == s0.restrict(W)
3265
+ True
3266
+
3267
+ The contraction can be performed on more than one index: ``c`` being a
3268
+ type-`(2,2)` tensor, contracting the indices in positions 2 and 3
3269
+ of ``c`` with respectively those in positions 0 and 1 of ``b`` is::
3270
+
3271
+ sage: c = a*a ; c
3272
+ Tensor field of type (2,2) on the 2-dimensional differentiable
3273
+ manifold M
3274
+ sage: s = c.contract(2,3, b, 0,1) ; s # long time
3275
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3276
+ manifold M
3277
+
3278
+ The same double contraction using index notation::
3279
+
3280
+ sage: s == c['^.._kl']*b['^kl'] # long time
3281
+ True
3282
+
3283
+ The symmetries are either conserved or destroyed by the contraction::
3284
+
3285
+ sage: c = c.symmetrize(0,1).antisymmetrize(2,3)
3286
+ sage: c.symmetries()
3287
+ symmetry: (0, 1); antisymmetry: (2, 3)
3288
+ sage: s = b.contract(0, c, 2) ; s
3289
+ Tensor field of type (3,1) on the 2-dimensional differentiable
3290
+ manifold M
3291
+ sage: s.symmetries()
3292
+ symmetry: (1, 2); no antisymmetry
3293
+
3294
+ Case of a scalar field result::
3295
+
3296
+ sage: a = M.one_form({eU: [y, 1+x]}, name='a')
3297
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
3298
+ sage: b = M.vector_field({eU: [x, y^2]}, name='b')
3299
+ sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
3300
+ sage: a.display(eU)
3301
+ a = y dx + (x + 1) dy
3302
+ sage: b.display(eU)
3303
+ b = x ∂/∂x + y^2 ∂/∂y
3304
+ sage: s = a.contract(b) ; s
3305
+ Scalar field on the 2-dimensional differentiable manifold M
3306
+ sage: s.display()
3307
+ M → ℝ
3308
+ on U: (x, y) ↦ (x + 1)*y^2 + x*y
3309
+ on V: (u, v) ↦ 1/8*u^3 - 1/8*u*v^2 + 1/8*v^3 + 1/2*u^2 - 1/8*(u^2 + 4*u)*v
3310
+ sage: s == a['_i']*b['^i'] # use of index notation
3311
+ True
3312
+ sage: s == b.contract(a)
3313
+ True
3314
+
3315
+ Case of a vanishing scalar field result::
3316
+
3317
+ sage: b = M.vector_field({eU: [1+x, -y]}, name='b')
3318
+ sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
3319
+ sage: s = a.contract(b) ; s
3320
+ Scalar field zero on the 2-dimensional differentiable manifold M
3321
+ sage: s.display()
3322
+ zero: M → ℝ
3323
+ on U: (x, y) ↦ 0
3324
+ on V: (u, v) ↦ 0
3325
+ """
3326
+ nargs = len(args)
3327
+ for i, arg in enumerate(args):
3328
+ if isinstance(arg, TensorField):
3329
+ other = arg
3330
+ it = i
3331
+ break
3332
+ else:
3333
+ raise TypeError("a tensor field must be provided in the " +
3334
+ "argument list")
3335
+ if it == 0:
3336
+ pos1 = (self._tensor_rank - 1,)
3337
+ else:
3338
+ pos1 = args[:it]
3339
+ if it == nargs-1:
3340
+ pos2 = (0,)
3341
+ else:
3342
+ pos2 = args[it+1:]
3343
+ ncontr = len(pos1) # number of contractions
3344
+ if len(pos2) != ncontr:
3345
+ raise IndexError("different number of indices for the contraction")
3346
+ if self._domain.is_subset(other._domain):
3347
+ if not self._ambient_domain.is_subset(other._ambient_domain):
3348
+ raise ValueError("incompatible ambient domains for contraction")
3349
+ elif other._domain.is_subset(self._domain):
3350
+ if not other._ambient_domain.is_subset(self._ambient_domain):
3351
+ raise ValueError("incompatible ambient domains for contraction")
3352
+ dom_resu = self._domain.intersection(other._domain)
3353
+ ambient_dom_resu = self._ambient_domain.intersection(other._ambient_domain)
3354
+ self_r = self.restrict(dom_resu)
3355
+ other_r = other.restrict(dom_resu)
3356
+ k1, l1 = self._tensor_type
3357
+ k2, l2 = other._tensor_type
3358
+ tensor_type_resu = (k1 + k2 - ncontr, l1 + l2 - ncontr)
3359
+ if ambient_dom_resu.is_manifestly_parallelizable():
3360
+ # call of the FreeModuleTensor version:
3361
+ args = pos1 + (other_r,) + pos2
3362
+ return FreeModuleTensor.contract(self_r, *args)
3363
+ com_dom = []
3364
+ for dom in self_r._restrictions:
3365
+ if dom in other_r._restrictions:
3366
+ com_dom.append(dom)
3367
+ resu_rst = []
3368
+ for dom in com_dom:
3369
+ self_rr = self_r._restrictions[dom]
3370
+ other_rr = other_r._restrictions[dom]
3371
+ args = pos1 + (other_rr,) + pos2
3372
+ resu_rst.append(self_rr.contract(*args))
3373
+ if tensor_type_resu == (0,0):
3374
+ # scalar field result
3375
+ resu = dom_resu.scalar_field()
3376
+ all_zero = True
3377
+ for rst in resu_rst:
3378
+ if rst == 0:
3379
+ for chart in rst._domain._atlas:
3380
+ resu._express[chart] = 0
3381
+ else:
3382
+ all_zero = False
3383
+ for chart, funct in rst._express.items():
3384
+ resu._express[chart] = funct
3385
+ if all_zero:
3386
+ resu = dom_resu._zero_scalar_field
3387
+ else:
3388
+ # tensor field result
3389
+ dest_map = self._vmodule._dest_map
3390
+ dest_map_resu = dest_map.restrict(dom_resu,
3391
+ subcodomain=ambient_dom_resu)
3392
+ vmodule = dom_resu.vector_field_module(dest_map=dest_map_resu)
3393
+
3394
+ resu = vmodule.tensor(tensor_type_resu, sym=resu_rst[0]._sym,
3395
+ antisym=resu_rst[0]._antisym)
3396
+ for rst in resu_rst:
3397
+ resu._restrictions[rst._domain] = rst
3398
+ return resu
3399
+
3400
+ def symmetrize(self, *pos):
3401
+ r"""
3402
+ Symmetrization over some arguments.
3403
+
3404
+ INPUT:
3405
+
3406
+ - ``pos`` -- (default: ``None``) list of argument positions involved
3407
+ in the symmetrization (with the convention ``position=0`` for the
3408
+ first argument); if ``None``, the symmetrization is performed
3409
+ over all the arguments
3410
+
3411
+ OUTPUT:
3412
+
3413
+ - the symmetrized tensor field (instance of :class:`TensorField`)
3414
+
3415
+ EXAMPLES:
3416
+
3417
+ Symmetrization of a type-`(0,2)` tensor field on a 2-dimensional
3418
+ non-parallelizable manifold::
3419
+
3420
+ sage: M = Manifold(2, 'M')
3421
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
3422
+ sage: M.declare_union(U,V) # M is the union of U and V
3423
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
3424
+ sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W',
3425
+ ....: restrictions1= x>0, restrictions2= u+v>0)
3426
+ sage: inv = transf.inverse()
3427
+ sage: W = U.intersection(V)
3428
+ sage: eU = c_xy.frame() ; eV = c_uv.frame()
3429
+ sage: a = M.tensor_field(0,2, {eU: [[1,x], [2,y]]}, name='a')
3430
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
3431
+ sage: a[eV,:]
3432
+ [ 1/4*u + 3/4 -1/4*u + 3/4]
3433
+ [ 1/4*v - 1/4 -1/4*v - 1/4]
3434
+ sage: s = a.symmetrize() ; s
3435
+ Field of symmetric bilinear forms on the 2-dimensional
3436
+ differentiable manifold M
3437
+ sage: s[eU,:]
3438
+ [ 1 1/2*x + 1]
3439
+ [1/2*x + 1 y]
3440
+ sage: s[eV,:]
3441
+ [ 1/4*u + 3/4 -1/8*u + 1/8*v + 1/4]
3442
+ [-1/8*u + 1/8*v + 1/4 -1/4*v - 1/4]
3443
+ sage: s == a.symmetrize(0,1) # explicit positions
3444
+ True
3445
+
3446
+ .. SEEALSO::
3447
+
3448
+ For more details and examples, see
3449
+ :meth:`sage.tensor.modules.free_module_tensor.FreeModuleTensor.symmetrize`.
3450
+ """
3451
+ resu_rst = []
3452
+ for rst in self._restrictions.values():
3453
+ resu_rst.append(rst.symmetrize(*pos))
3454
+ resu = self._vmodule.tensor(self._tensor_type, sym=resu_rst[0]._sym,
3455
+ antisym=resu_rst[0]._antisym)
3456
+ for rst in resu_rst:
3457
+ resu._restrictions[rst._domain] = rst
3458
+ return resu
3459
+
3460
+ def antisymmetrize(self, *pos):
3461
+ r"""
3462
+ Antisymmetrization over some arguments.
3463
+
3464
+ INPUT:
3465
+
3466
+ - ``pos`` -- (default: ``None``) list of argument positions involved
3467
+ in the antisymmetrization (with the convention ``position=0`` for
3468
+ the first argument); if ``None``, the antisymmetrization is
3469
+ performed over all the arguments
3470
+
3471
+ OUTPUT:
3472
+
3473
+ - the antisymmetrized tensor field (instance of :class:`TensorField`)
3474
+
3475
+ EXAMPLES:
3476
+
3477
+ Antisymmetrization of a type-`(0,2)` tensor field on a 2-dimensional
3478
+ non-parallelizable manifold::
3479
+
3480
+ sage: M = Manifold(2, 'M')
3481
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
3482
+ sage: M.declare_union(U,V) # M is the union of U and V
3483
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
3484
+ sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W',
3485
+ ....: restrictions1= x>0, restrictions2= u+v>0)
3486
+ sage: inv = transf.inverse()
3487
+ sage: W = U.intersection(V)
3488
+ sage: eU = c_xy.frame() ; eV = c_uv.frame()
3489
+ sage: a = M.tensor_field(0,2, {eU: [[1,x], [2,y]]}, name='a')
3490
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
3491
+ sage: a[eV,:]
3492
+ [ 1/4*u + 3/4 -1/4*u + 3/4]
3493
+ [ 1/4*v - 1/4 -1/4*v - 1/4]
3494
+ sage: s = a.antisymmetrize() ; s
3495
+ 2-form on the 2-dimensional differentiable manifold M
3496
+ sage: s[eU,:]
3497
+ [ 0 1/2*x - 1]
3498
+ [-1/2*x + 1 0]
3499
+ sage: s[eV,:]
3500
+ [ 0 -1/8*u - 1/8*v + 1/2]
3501
+ [ 1/8*u + 1/8*v - 1/2 0]
3502
+ sage: s == a.antisymmetrize(0,1) # explicit positions
3503
+ True
3504
+ sage: s == a.antisymmetrize(1,0) # the order of positions does not matter
3505
+ True
3506
+
3507
+ .. SEEALSO::
3508
+
3509
+ For more details and examples, see
3510
+ :meth:`sage.tensor.modules.free_module_tensor.FreeModuleTensor.antisymmetrize`.
3511
+ """
3512
+ resu_rst = []
3513
+ for rst in self._restrictions.values():
3514
+ resu_rst.append(rst.antisymmetrize(*pos))
3515
+ resu = self._vmodule.tensor(self._tensor_type, sym=resu_rst[0]._sym,
3516
+ antisym=resu_rst[0]._antisym)
3517
+ for rst in resu_rst:
3518
+ resu._restrictions[rst._domain] = rst
3519
+ return resu
3520
+
3521
+ def lie_derivative(self, vector):
3522
+ r"""
3523
+ Lie derivative of ``self`` with respect to a vector field.
3524
+
3525
+ INPUT:
3526
+
3527
+ - ``vector`` -- vector field with respect to which the Lie derivative
3528
+ is to be taken
3529
+
3530
+ OUTPUT:
3531
+
3532
+ - the tensor field that is the Lie derivative of the current tensor
3533
+ field with respect to ``vector``
3534
+
3535
+ EXAMPLES:
3536
+
3537
+ Lie derivative of a type-`(1,1)` tensor field along a vector field on
3538
+ a non-parallelizable 2-dimensional manifold::
3539
+
3540
+ sage: M = Manifold(2, 'M')
3541
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
3542
+ sage: M.declare_union(U,V) # M is the union of U and V
3543
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
3544
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
3545
+ ....: intersection_name='W', restrictions1= x>0,
3546
+ ....: restrictions2= u+v>0)
3547
+ sage: uv_to_xy = xy_to_uv.inverse()
3548
+ sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
3549
+ sage: t = M.tensor_field(1, 1, {e_xy: [[x, 1], [y, 0]]}, name='t')
3550
+ sage: t.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
3551
+ sage: w = M.vector_field({e_xy: [-y, x]}, name='w')
3552
+ sage: w.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
3553
+ sage: lt = t.lie_derivative(w); lt
3554
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3555
+ manifold M
3556
+ sage: lt.display(e_xy)
3557
+ ∂/∂x⊗dx - x ∂/∂x⊗dy + (-y - 1) ∂/∂y⊗dy
3558
+ sage: lt.display(e_uv)
3559
+ -1/2*u ∂/∂u⊗du + (1/2*u + 1) ∂/∂u⊗dv + (-1/2*v + 1) ∂/∂v⊗du + 1/2*v ∂/∂v⊗dv
3560
+
3561
+ The result is cached::
3562
+
3563
+ sage: t.lie_derivative(w) is lt
3564
+ True
3565
+
3566
+ An alias is ``lie_der``::
3567
+
3568
+ sage: t.lie_der(w) is t.lie_derivative(w)
3569
+ True
3570
+
3571
+ Lie derivative of a vector field::
3572
+
3573
+ sage: a = M.vector_field({e_xy: [1-x, x-y]}, name='a')
3574
+ sage: a.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
3575
+ sage: a.lie_der(w)
3576
+ Vector field on the 2-dimensional differentiable manifold M
3577
+ sage: a.lie_der(w).display(e_xy)
3578
+ x ∂/∂x + (-y - 1) ∂/∂y
3579
+ sage: a.lie_der(w).display(e_uv)
3580
+ (v - 1) ∂/∂u + (u + 1) ∂/∂v
3581
+
3582
+ The Lie derivative is antisymmetric::
3583
+
3584
+ sage: a.lie_der(w) == - w.lie_der(a)
3585
+ True
3586
+
3587
+ and it coincides with the commutator of the two vector fields::
3588
+
3589
+ sage: f = M.scalar_field({c_xy: 3*x-1, c_uv: 3/2*(u+v)-1})
3590
+ sage: a.lie_der(w)(f) == w(a(f)) - a(w(f)) # long time
3591
+ True
3592
+ """
3593
+ if vector._tensor_type != (1,0):
3594
+ raise TypeError("the argument must be a vector field")
3595
+
3596
+ # The Lie derivative is cached in _lie_derivates while neither
3597
+ # the tensor field nor ``vector`` have been modified
3598
+ if id(vector) not in self._lie_derivatives:
3599
+ # the computation must be performed:
3600
+ resu_rst = []
3601
+ for dom, rst in self._restrictions.items():
3602
+ resu_rst.append(rst.lie_derivative(vector.restrict(dom)))
3603
+ resu = self._vmodule.tensor(self._tensor_type,
3604
+ sym=resu_rst[0]._sym,
3605
+ antisym=resu_rst[0]._antisym)
3606
+ for rst in resu_rst:
3607
+ resu._restrictions[rst._domain] = rst
3608
+ self._lie_derivatives[id(vector)] = (vector, resu)
3609
+ vector._lie_der_along_self[id(self)] = self
3610
+ return self._lie_derivatives[id(vector)][1]
3611
+
3612
+ lie_der = lie_derivative
3613
+
3614
+ def at(self, point: ManifoldPoint) -> FreeModuleTensor:
3615
+ r"""
3616
+ Value of ``self`` at a point of its domain.
3617
+
3618
+ If the current tensor field is
3619
+
3620
+ .. MATH::
3621
+
3622
+ t:\ U \longrightarrow T^{(k,l)} M
3623
+
3624
+ associated with the differentiable map
3625
+
3626
+ .. MATH::
3627
+
3628
+ \Phi:\ U \longrightarrow M,
3629
+
3630
+ where `U` and `M` are two manifolds (possibly `U = M` and
3631
+ `\Phi = \mathrm{Id}_M`), then for any point `p \in U`, `t(p)`
3632
+ is a tensor on the tangent space to `M` at the point `\Phi(p)`.
3633
+
3634
+ INPUT:
3635
+
3636
+ - ``point`` -- :class:`~sage.manifolds.point.ManifoldPoint`;
3637
+ point `p` in the domain of the tensor field `U`
3638
+
3639
+ OUTPUT:
3640
+
3641
+ - :class:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor`
3642
+ representing the tensor `t(p)` on the tangent vector space
3643
+ `T_{\Phi(p)} M`
3644
+
3645
+ EXAMPLES:
3646
+
3647
+ Tensor on a tangent space of a non-parallelizable 2-dimensional
3648
+ manifold::
3649
+
3650
+ sage: M = Manifold(2, 'M')
3651
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
3652
+ sage: M.declare_union(U,V) # M is the union of U and V
3653
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
3654
+ sage: transf = c_xy.transition_map(c_uv, (x+y, x-y),
3655
+ ....: intersection_name='W', restrictions1= x>0,
3656
+ ....: restrictions2= u+v>0)
3657
+ sage: inv = transf.inverse()
3658
+ sage: W = U.intersection(V)
3659
+ sage: eU = c_xy.frame() ; eV = c_uv.frame()
3660
+ sage: a = M.tensor_field(1, 1, {eU: [[1+y,x], [0,x+y]]}, name='a')
3661
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
3662
+ sage: a.display(eU)
3663
+ a = (y + 1) ∂/∂x⊗dx + x ∂/∂x⊗dy + (x + y) ∂/∂y⊗dy
3664
+ sage: a.display(eV)
3665
+ a = (u + 1/2) ∂/∂u⊗du + (-1/2*u - 1/2*v + 1/2) ∂/∂u⊗dv
3666
+ + 1/2 ∂/∂v⊗du + (1/2*u - 1/2*v + 1/2) ∂/∂v⊗dv
3667
+ sage: p = M.point((2,3), chart=c_xy, name='p')
3668
+ sage: ap = a.at(p) ; ap
3669
+ Type-(1,1) tensor a on the Tangent space at Point p on the
3670
+ 2-dimensional differentiable manifold M
3671
+ sage: ap.parent()
3672
+ Free module of type-(1,1) tensors on the Tangent space at Point p
3673
+ on the 2-dimensional differentiable manifold M
3674
+ sage: ap.display(eU.at(p))
3675
+ a = 4 ∂/∂x⊗dx + 2 ∂/∂x⊗dy + 5 ∂/∂y⊗dy
3676
+ sage: ap.display(eV.at(p))
3677
+ a = 11/2 ∂/∂u⊗du - 3/2 ∂/∂u⊗dv + 1/2 ∂/∂v⊗du + 7/2 ∂/∂v⊗dv
3678
+ sage: p.coord(c_uv) # to check the above expression
3679
+ (5, -1)
3680
+ """
3681
+ if point not in self._domain:
3682
+ raise ValueError("the {} is not a point in the ".format(point) +
3683
+ "domain of {}".format(self))
3684
+ for dom, rst in self._restrictions.items():
3685
+ if point in dom:
3686
+ return rst.at(point)
3687
+
3688
+ def up(
3689
+ self,
3690
+ non_degenerate_form: Union[PseudoRiemannianMetric, SymplecticForm, PoissonTensorField],
3691
+ pos: Optional[int] = None,
3692
+ ) -> TensorField:
3693
+ r"""
3694
+ Compute a dual of the tensor field by raising some index with the
3695
+ given tensor field (usually, a pseudo-Riemannian metric, a symplectic form or a Poisson tensor).
3696
+
3697
+ If `T` is the tensor field, `(k,l)` its type and `p` the position of a
3698
+ covariant index (i.e. `k\leq p < k+l`), this method called with
3699
+ ``pos`` `=p` yields the tensor field `T^\sharp` of type `(k+1,l-1)`
3700
+ whose components are
3701
+
3702
+ .. MATH::
3703
+
3704
+ (T^\sharp)^{a_1\ldots a_{k+1}}_{\phantom{a_1\ldots a_{k+1}}\,
3705
+ b_1 \ldots b_{l-1}} = g^{a_{k+1} i} \,
3706
+ T^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\, b_1 \ldots b_{p-k}
3707
+ \, i \, b_{p-k+1}\ldots b_{l-1}},
3708
+
3709
+ `g^{ab}` being the components of the inverse metric or the Poisson tensor, respectively.
3710
+
3711
+ The reverse operation is :meth:`TensorField.down`.
3712
+
3713
+ INPUT:
3714
+
3715
+ - ``non_degenerate_form`` -- non-degenerate form `g`, or a Poisson tensor
3716
+ - ``pos`` -- (default: ``None``) position of the index (with the
3717
+ convention ``pos=0`` for the first index); if ``None``, the raising
3718
+ is performed over all the covariant indices, starting from the first
3719
+ one
3720
+
3721
+ OUTPUT:
3722
+
3723
+ - the tensor field `T^\sharp` resulting from the index raising
3724
+ operation
3725
+
3726
+ EXAMPLES:
3727
+
3728
+ Raising the index of a 1-form results in a vector field::
3729
+
3730
+ sage: M = Manifold(2, 'M', start_index=1)
3731
+ sage: c_xy.<x,y> = M.chart()
3732
+ sage: g = M.metric('g')
3733
+ sage: g[1,1], g[1,2], g[2,2] = 1+x, x*y, 1-y
3734
+ sage: w = M.one_form(-1, 2)
3735
+ sage: v = w.up(g) ; v
3736
+ Vector field on the 2-dimensional differentiable manifold M
3737
+ sage: v.display()
3738
+ ((2*x - 1)*y + 1)/(x^2*y^2 + (x + 1)*y - x - 1) ∂/∂x
3739
+ - (x*y + 2*x + 2)/(x^2*y^2 + (x + 1)*y - x - 1) ∂/∂y
3740
+ sage: ig = g.inverse(); ig[:]
3741
+ [ (y - 1)/(x^2*y^2 + (x + 1)*y - x - 1) x*y/(x^2*y^2 + (x + 1)*y - x - 1)]
3742
+ [ x*y/(x^2*y^2 + (x + 1)*y - x - 1) -(x + 1)/(x^2*y^2 + (x + 1)*y - x - 1)]
3743
+
3744
+ Using the index notation instead of :meth:`up`::
3745
+
3746
+ sage: v == ig['^ab']*w['_b']
3747
+ True
3748
+
3749
+ The reverse operation::
3750
+
3751
+ sage: w1 = v.down(g) ; w1
3752
+ 1-form on the 2-dimensional differentiable manifold M
3753
+ sage: w1.display()
3754
+ -dx + 2 dy
3755
+ sage: w1 == w
3756
+ True
3757
+
3758
+ The reverse operation in index notation::
3759
+
3760
+ sage: g['_ab']*v['^b'] == w
3761
+ True
3762
+
3763
+ Raising the indices of a tensor field of type (0,2)::
3764
+
3765
+ sage: t = M.tensor_field(0, 2, [[1,2], [3,4]])
3766
+ sage: tu0 = t.up(g, 0) ; tu0 # raising the first index
3767
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3768
+ manifold M
3769
+ sage: tu0[:]
3770
+ [ ((3*x + 1)*y - 1)/(x^2*y^2 + (x + 1)*y - x - 1) 2*((2*x + 1)*y - 1)/(x^2*y^2 + (x + 1)*y - x - 1)]
3771
+ [ (x*y - 3*x - 3)/(x^2*y^2 + (x + 1)*y - x - 1) 2*(x*y - 2*x - 2)/(x^2*y^2 + (x + 1)*y - x - 1)]
3772
+ sage: tu0 == ig['^ac']*t['_cb'] # the same operation in index notation
3773
+ True
3774
+ sage: tuu0 = tu0.up(g) ; tuu0 # the two indices have been raised, starting from the first one
3775
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3776
+ manifold M
3777
+ sage: tuu0 == tu0['^a_c']*ig['^cb'] # the same operation in index notation
3778
+ True
3779
+ sage: tu1 = t.up(g, 1) ; tu1 # raising the second index
3780
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3781
+ manifold M
3782
+ sage: tu1 == ig['^ac']*t['_bc'] # the same operation in index notation
3783
+ True
3784
+ sage: tu1[:]
3785
+ [((2*x + 1)*y - 1)/(x^2*y^2 + (x + 1)*y - x - 1) ((4*x + 3)*y - 3)/(x^2*y^2 + (x + 1)*y - x - 1)]
3786
+ [ (x*y - 2*x - 2)/(x^2*y^2 + (x + 1)*y - x - 1) (3*x*y - 4*x - 4)/(x^2*y^2 + (x + 1)*y - x - 1)]
3787
+ sage: tuu1 = tu1.up(g) ; tuu1 # the two indices have been raised, starting from the second one
3788
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3789
+ manifold M
3790
+ sage: tuu1 == tu1['^a_c']*ig['^cb'] # the same operation in index notation
3791
+ True
3792
+ sage: tuu0 == tuu1 # the order of index raising is important
3793
+ False
3794
+ sage: tuu = t.up(g) ; tuu # both indices are raised, starting from the first one
3795
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3796
+ manifold M
3797
+ sage: tuu0 == tuu # the same order for index raising has been applied
3798
+ True
3799
+ sage: tuu1 == tuu # to get tuu1, indices have been raised from the last one, contrary to tuu
3800
+ False
3801
+ sage: d0tuu = tuu.down(g, 0) ; d0tuu # the first index is lowered again
3802
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3803
+ manifold M
3804
+ sage: dd0tuu = d0tuu.down(g) ; dd0tuu # the second index is then lowered
3805
+ Tensor field of type (0,2) on the 2-dimensional differentiable
3806
+ manifold M
3807
+ sage: d1tuu = tuu.down(g, 1) ; d1tuu # lowering operation, starting from the last index
3808
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3809
+ manifold M
3810
+ sage: dd1tuu = d1tuu.down(g) ; dd1tuu
3811
+ Tensor field of type (0,2) on the 2-dimensional differentiable
3812
+ manifold M
3813
+ sage: ddtuu = tuu.down(g) ; ddtuu # both indices are lowered, starting from the last one
3814
+ Tensor field of type (0,2) on the 2-dimensional differentiable
3815
+ manifold M
3816
+ sage: ddtuu == t # should be true
3817
+ True
3818
+ sage: dd0tuu == t # not true, because of the order of index lowering to get dd0tuu
3819
+ False
3820
+ sage: dd1tuu == t # should be true
3821
+ True
3822
+ """
3823
+ n_con = self._tensor_type[0] # number of contravariant indices = k
3824
+ if pos is None:
3825
+ result = self
3826
+ for p in range(n_con, self._tensor_rank):
3827
+ k = result._tensor_type[0]
3828
+ result = result.up(non_degenerate_form, k)
3829
+ return result
3830
+
3831
+ if pos < n_con or pos > self._tensor_rank - 1:
3832
+ print("pos = {}".format(pos))
3833
+ raise ValueError("position out of range")
3834
+
3835
+ from sage.manifolds.differentiable.metric import PseudoRiemannianMetric
3836
+ from sage.manifolds.differentiable.poisson_tensor import PoissonTensorField
3837
+ from sage.manifolds.differentiable.symplectic_form import SymplecticForm
3838
+
3839
+ if isinstance(non_degenerate_form, PseudoRiemannianMetric):
3840
+ return self.contract(pos, non_degenerate_form.inverse(), 1)
3841
+ elif isinstance(non_degenerate_form, SymplecticForm):
3842
+ return self.contract(pos, non_degenerate_form.poisson(), 1)
3843
+ elif isinstance(non_degenerate_form, PoissonTensorField):
3844
+ return self.contract(pos, non_degenerate_form, 1)
3845
+ else:
3846
+ raise ValueError("The non-degenerate form has to be a metric, a symplectic form or a Poisson tensor field")
3847
+
3848
+ def down(
3849
+ self,
3850
+ non_degenerate_form: Union[PseudoRiemannianMetric, SymplecticForm],
3851
+ pos: Optional[int] = None,
3852
+ ) -> TensorField:
3853
+ r"""
3854
+ Compute a dual of the tensor field by lowering some index with a
3855
+ given non-degenerate form (pseudo-Riemannian metric or symplectic form).
3856
+
3857
+ If `T` is the tensor field, `(k,l)` its type and `p` the position of a
3858
+ contravariant index (i.e. `0\leq p < k`), this method called with
3859
+ ``pos`` `=p` yields the tensor field `T^\flat` of type `(k-1,l+1)`
3860
+ whose components are
3861
+
3862
+ .. MATH::
3863
+
3864
+ (T^\flat)^{a_1\ldots a_{k-1}}_{\phantom{a_1\ldots a_{k-1}}
3865
+ \, b_1 \ldots b_{l+1}} = g_{i b_1} \,
3866
+ T^{a_1\ldots a_{p} \, i \, a_{p+1}\ldots a_{k-1}}_{\phantom{a_1
3867
+ \ldots a_{p} \, i \, a_{p+1}\ldots a_{k-1}}\, b_2 \ldots b_{l+1}},
3868
+
3869
+ `g_{ab}` being the components of the metric tensor or the symplectic form, respectively.
3870
+
3871
+ The reverse operation is :meth:`TensorField.up`.
3872
+
3873
+ INPUT:
3874
+
3875
+ - ``non_degenerate_form`` -- non-degenerate form `g`
3876
+ - ``pos`` -- (default: ``None``) position of the index (with the
3877
+ convention ``pos=0`` for the first index); if ``None``, the lowering
3878
+ is performed over all the contravariant indices, starting from the
3879
+ last one
3880
+
3881
+ OUTPUT:
3882
+
3883
+ - the tensor field `T^\flat` resulting from the index lowering
3884
+ operation
3885
+
3886
+ EXAMPLES:
3887
+
3888
+ Lowering the index of a vector field results in a 1-form::
3889
+
3890
+ sage: M = Manifold(2, 'M', start_index=1)
3891
+ sage: c_xy.<x,y> = M.chart()
3892
+ sage: g = M.metric('g')
3893
+ sage: g[1,1], g[1,2], g[2,2] = 1+x, x*y, 1-y
3894
+ sage: v = M.vector_field(-1, 2)
3895
+ sage: w = v.down(g) ; w
3896
+ 1-form on the 2-dimensional differentiable manifold M
3897
+ sage: w.display()
3898
+ (2*x*y - x - 1) dx + (-(x + 2)*y + 2) dy
3899
+
3900
+ Using the index notation instead of :meth:`down`::
3901
+
3902
+ sage: w == g['_ab']*v['^b']
3903
+ True
3904
+
3905
+ The reverse operation::
3906
+
3907
+ sage: v1 = w.up(g) ; v1
3908
+ Vector field on the 2-dimensional differentiable manifold M
3909
+ sage: v1 == v
3910
+ True
3911
+
3912
+ Lowering the indices of a tensor field of type (2,0)::
3913
+
3914
+ sage: t = M.tensor_field(2, 0, [[1,2], [3,4]])
3915
+ sage: td0 = t.down(g, 0) ; td0 # lowering the first index
3916
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3917
+ manifold M
3918
+ sage: td0 == g['_ac']*t['^cb'] # the same operation in index notation
3919
+ True
3920
+ sage: td0[:]
3921
+ [ 3*x*y + x + 1 (x - 3)*y + 3]
3922
+ [4*x*y + 2*x + 2 2*(x - 2)*y + 4]
3923
+ sage: tdd0 = td0.down(g) ; tdd0 # the two indices have been lowered, starting from the first one
3924
+ Tensor field of type (0,2) on the 2-dimensional differentiable
3925
+ manifold M
3926
+ sage: tdd0 == g['_ac']*td0['^c_b'] # the same operation in index notation
3927
+ True
3928
+ sage: tdd0[:]
3929
+ [ 4*x^2*y^2 + x^2 + 5*(x^2 + x)*y + 2*x + 1 2*(x^2 - 2*x)*y^2 + (x^2 + 2*x - 3)*y + 3*x + 3]
3930
+ [(3*x^2 - 4*x)*y^2 + (x^2 + 3*x - 2)*y + 2*x + 2 (x^2 - 5*x + 4)*y^2 + (5*x - 8)*y + 4]
3931
+ sage: td1 = t.down(g, 1) ; td1 # lowering the second index
3932
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3933
+ manifold M
3934
+ sage: td1 == g['_ac']*t['^bc'] # the same operation in index notation
3935
+ True
3936
+ sage: td1[:]
3937
+ [ 2*x*y + x + 1 (x - 2)*y + 2]
3938
+ [4*x*y + 3*x + 3 (3*x - 4)*y + 4]
3939
+ sage: tdd1 = td1.down(g) ; tdd1 # the two indices have been lowered, starting from the second one
3940
+ Tensor field of type (0,2) on the 2-dimensional differentiable
3941
+ manifold M
3942
+ sage: tdd1 == g['_ac']*td1['^c_b'] # the same operation in index notation
3943
+ True
3944
+ sage: tdd1[:]
3945
+ [ 4*x^2*y^2 + x^2 + 5*(x^2 + x)*y + 2*x + 1 (3*x^2 - 4*x)*y^2 + (x^2 + 3*x - 2)*y + 2*x + 2]
3946
+ [2*(x^2 - 2*x)*y^2 + (x^2 + 2*x - 3)*y + 3*x + 3 (x^2 - 5*x + 4)*y^2 + (5*x - 8)*y + 4]
3947
+ sage: tdd1 == tdd0 # the order of index lowering is important
3948
+ False
3949
+ sage: tdd = t.down(g) ; tdd # both indices are lowered, starting from the last one
3950
+ Tensor field of type (0,2) on the 2-dimensional differentiable
3951
+ manifold M
3952
+ sage: tdd[:]
3953
+ [ 4*x^2*y^2 + x^2 + 5*(x^2 + x)*y + 2*x + 1 (3*x^2 - 4*x)*y^2 + (x^2 + 3*x - 2)*y + 2*x + 2]
3954
+ [2*(x^2 - 2*x)*y^2 + (x^2 + 2*x - 3)*y + 3*x + 3 (x^2 - 5*x + 4)*y^2 + (5*x - 8)*y + 4]
3955
+ sage: tdd0 == tdd # to get tdd0, indices have been lowered from the first one, contrary to tdd
3956
+ False
3957
+ sage: tdd1 == tdd # the same order for index lowering has been applied
3958
+ True
3959
+ sage: u0tdd = tdd.up(g, 0) ; u0tdd # the first index is raised again
3960
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3961
+ manifold M
3962
+ sage: uu0tdd = u0tdd.up(g) ; uu0tdd # the second index is then raised
3963
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3964
+ manifold M
3965
+ sage: u1tdd = tdd.up(g, 1) ; u1tdd # raising operation, starting from the last index
3966
+ Tensor field of type (1,1) on the 2-dimensional differentiable
3967
+ manifold M
3968
+ sage: uu1tdd = u1tdd.up(g) ; uu1tdd
3969
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3970
+ manifold M
3971
+ sage: uutdd = tdd.up(g) ; uutdd # both indices are raised, starting from the first one
3972
+ Tensor field of type (2,0) on the 2-dimensional differentiable
3973
+ manifold M
3974
+ sage: uutdd == t # should be true
3975
+ True
3976
+ sage: uu0tdd == t # should be true
3977
+ True
3978
+ sage: uu1tdd == t # not true, because of the order of index raising to get uu1tdd
3979
+ False
3980
+ """
3981
+ n_con = self._tensor_type[0] # number of contravariant indices = k
3982
+ if pos is None:
3983
+ result = self
3984
+ for p in range(n_con):
3985
+ k = result._tensor_type[0]
3986
+ result = result.down(non_degenerate_form, k - 1)
3987
+ return result
3988
+ if not isinstance(pos, (int, Integer)):
3989
+ raise TypeError("the argument 'pos' must be an integer")
3990
+ if pos < 0 or pos >= n_con:
3991
+ print("pos = {}".format(pos))
3992
+ raise ValueError("position out of range")
3993
+ return non_degenerate_form.contract(0, self, pos)
3994
+
3995
+ def divergence(self, metric=None):
3996
+ r"""
3997
+ Return the divergence of ``self`` (with respect to a given
3998
+ metric).
3999
+
4000
+ The divergence is taken on the *last* index: if
4001
+ ``self`` is a tensor field `t` of type `(k,0)` with `k\geq 1`, the
4002
+ divergence of `t` with respect to the metric `g` is the tensor field
4003
+ of type `(k-1,0)` defined by
4004
+
4005
+ .. MATH::
4006
+
4007
+ (\mathrm{div}\, t)^{a_1\ldots a_{k-1}} =
4008
+ \nabla_i t^{a_1\ldots a_{k-1} i} =
4009
+ (\nabla t)^{a_1\ldots a_{k-1} i}_{\phantom{a_1\ldots a_{k-1} i}\, i},
4010
+
4011
+ where `\nabla` is the Levi-Civita connection of `g` (cf.
4012
+ :class:`~sage.manifolds.differentiable.levi_civita_connection.LeviCivitaConnection`).
4013
+
4014
+ This definition is extended to tensor fields of type `(k,l)` with
4015
+ `k\geq 0` and `l\geq 1`, by raising the last index with the metric `g`:
4016
+ `\mathrm{div}\, t` is then the tensor field of type `(k,l-1)` defined by
4017
+
4018
+ .. MATH::
4019
+
4020
+ (\mathrm{div}\, t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\, b_1
4021
+ \ldots b_{l-1}} = \nabla_i (g^{ij} t^{a_1\ldots a_k}_{\phantom{a_1
4022
+ \ldots a_k}\, b_1\ldots b_{l-1} j})
4023
+ = (\nabla t^\sharp)^{a_1\ldots a_k i}_{\phantom{a_1\ldots a_k i}\,
4024
+ b_1\ldots b_{l-1} i},
4025
+
4026
+ where `t^\sharp` is the tensor field deduced from `t` by raising the
4027
+ last index with the metric `g` (see :meth:`up`).
4028
+
4029
+ INPUT:
4030
+
4031
+ - ``metric`` -- (default: ``None``) the pseudo-Riemannian metric `g`
4032
+ involved in the definition of the divergence; if none is provided,
4033
+ the domain of ``self`` is supposed to be endowed with a default
4034
+ metric (i.e. is supposed to be pseudo-Riemannian manifold, see
4035
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`)
4036
+ and the latter is used to define the divergence.
4037
+
4038
+ OUTPUT:
4039
+
4040
+ - instance of either
4041
+ :class:`~sage.manifolds.differentiable.scalarfield.DiffScalarField`
4042
+ if `(k,l)=(1,0)` (``self`` is a vector field) or `(k,l)=(0,1)`
4043
+ (``self`` is a 1-form) or of :class:`TensorField` if `k+l\geq 2`
4044
+ representing the divergence of ``self`` with respect to ``metric``
4045
+
4046
+ EXAMPLES:
4047
+
4048
+ Divergence of a vector field in the Euclidean plane::
4049
+
4050
+ sage: M.<x,y> = EuclideanSpace()
4051
+ sage: v = M.vector_field(x, y, name='v')
4052
+ sage: s = v.divergence(); s
4053
+ Scalar field div(v) on the Euclidean plane E^2
4054
+ sage: s.display()
4055
+ div(v): E^2 → ℝ
4056
+ (x, y) ↦ 2
4057
+
4058
+ A shortcut alias of ``divergence`` is ``div``::
4059
+
4060
+ sage: v.div() == s
4061
+ True
4062
+
4063
+ The function :func:`~sage.manifolds.operators.div` from the
4064
+ :mod:`~sage.manifolds.operators` module can be used instead of the
4065
+ method :meth:`divergence`::
4066
+
4067
+ sage: from sage.manifolds.operators import div
4068
+ sage: div(v) == s
4069
+ True
4070
+
4071
+ The divergence can be taken with respect to a metric tensor that is
4072
+ not the default one::
4073
+
4074
+ sage: h = M.lorentzian_metric('h')
4075
+ sage: h[1,1], h[2,2] = -1, 1/(1+x^2+y^2)
4076
+ sage: s = v.div(h); s
4077
+ Scalar field div_h(v) on the Euclidean plane E^2
4078
+ sage: s.display()
4079
+ div_h(v): E^2 → ℝ
4080
+ (x, y) ↦ (x^2 + y^2 + 2)/(x^2 + y^2 + 1)
4081
+
4082
+ The standard formula
4083
+
4084
+ .. MATH::
4085
+
4086
+ \mathrm{div}_h \, v = \frac{1}{\sqrt{|\det h|}}
4087
+ \frac{\partial}{\partial x^i} \left( \sqrt{|\det h|} \, v^i \right)
4088
+
4089
+ is checked as follows::
4090
+
4091
+ sage: sqrth = h.sqrt_abs_det().expr(); sqrth
4092
+ 1/sqrt(x^2 + y^2 + 1)
4093
+ sage: s == 1/sqrth * sum( (sqrth*v[i]).diff(i) for i in M.irange())
4094
+ True
4095
+
4096
+ A divergence-free vector::
4097
+
4098
+ sage: w = M.vector_field(-y, x, name='w')
4099
+ sage: w.div().display()
4100
+ div(w): E^2 → ℝ
4101
+ (x, y) ↦ 0
4102
+ sage: w.div(h).display()
4103
+ div_h(w): E^2 → ℝ
4104
+ (x, y) ↦ 0
4105
+
4106
+ Divergence of a type-``(2,0)`` tensor field::
4107
+
4108
+ sage: t = v*w; t
4109
+ Tensor field v⊗w of type (2,0) on the Euclidean plane E^2
4110
+ sage: s = t.div(); s
4111
+ Vector field div(v⊗w) on the Euclidean plane E^2
4112
+ sage: s.display()
4113
+ div(v⊗w) = -y e_x + x e_y
4114
+ """
4115
+ n_con = self._tensor_type[0] # number of contravariant indices = k
4116
+ n_cov = self._tensor_type[1] # number of covariant indices = l
4117
+ default_metric = metric is None
4118
+ if default_metric:
4119
+ metric = self._domain.metric()
4120
+ nabla = metric.connection()
4121
+ if n_cov == 0:
4122
+ resu = nabla(self).trace(n_con-1, n_con)
4123
+ else:
4124
+ tup = self.up(metric, self._tensor_rank-1)
4125
+ resu = nabla(tup).trace(n_con, self._tensor_rank)
4126
+ if self._name is not None:
4127
+ if default_metric:
4128
+ resu._name = "div({})".format(self._name)
4129
+ resu._latex_name = r"\mathrm{div}\left(" + self._latex_name + \
4130
+ r"\right)"
4131
+ else:
4132
+ resu._name = "div_{}({})".format(metric._name, self._name)
4133
+ resu._latex_name = r"\mathrm{div}_{" + metric._latex_name + \
4134
+ r"}\left(" + self._latex_name + r"\right)"
4135
+ # The name is propagated to possible restrictions of self:
4136
+ for restrict in resu._restrictions.values():
4137
+ restrict.set_name(resu._name, latex_name=resu._latex_name)
4138
+ return resu
4139
+
4140
+ div = divergence
4141
+
4142
+ def laplacian(self, metric=None):
4143
+ r"""
4144
+ Return the Laplacian of ``self`` with respect to a given
4145
+ metric (Laplace-Beltrami operator).
4146
+
4147
+ If ``self`` is a tensor field `t` of type `(k,l)`, the Laplacian of `t`
4148
+ with respect to the metric `g` is the tensor field of type `(k,l)`
4149
+ defined by
4150
+
4151
+ .. MATH::
4152
+
4153
+ (\Delta t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}}
4154
+ = \nabla_i \nabla^i
4155
+ t^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}},
4156
+
4157
+ where `\nabla` is the Levi-Civita connection of `g` (cf.
4158
+ :class:`~sage.manifolds.differentiable.levi_civita_connection.LeviCivitaConnection`)
4159
+ and `\nabla^i := g^{ij} \nabla_j`. The operator
4160
+ `\Delta = \nabla_i \nabla^i` is called the *Laplace-Beltrami operator*
4161
+ of metric `g`.
4162
+
4163
+ INPUT:
4164
+
4165
+ - ``metric`` -- (default: ``None``) the pseudo-Riemannian metric `g`
4166
+ involved in the definition of the Laplacian; if none is provided, the
4167
+ domain of ``self`` is supposed to be endowed with a default metric
4168
+ (i.e. is supposed to be pseudo-Riemannian manifold, see
4169
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`)
4170
+ and the latter is used to define the Laplacian
4171
+
4172
+ OUTPUT:
4173
+
4174
+ - instance of :class:`TensorField` representing the Laplacian of
4175
+ ``self``
4176
+
4177
+ EXAMPLES:
4178
+
4179
+ Laplacian of a vector field in the Euclidean plane::
4180
+
4181
+ sage: M.<x,y> = EuclideanSpace()
4182
+ sage: v = M.vector_field(x^3 + y^2, x*y, name='v')
4183
+ sage: Dv = v.laplacian(); Dv
4184
+ Vector field Delta(v) on the Euclidean plane E^2
4185
+ sage: Dv.display()
4186
+ Delta(v) = (6*x + 2) e_x
4187
+
4188
+ The function :func:`~sage.manifolds.operators.laplacian` from the
4189
+ :mod:`~sage.manifolds.operators` module can be used instead of the
4190
+ method :meth:`laplacian`::
4191
+
4192
+ sage: from sage.manifolds.operators import laplacian
4193
+ sage: laplacian(v) == Dv
4194
+ True
4195
+
4196
+ In the present case (Euclidean metric and Cartesian coordinates), the
4197
+ components of the Laplacian are the Laplacians of the components::
4198
+
4199
+ sage: all(Dv[[i]] == laplacian(v[[i]]) for i in M.irange())
4200
+ True
4201
+
4202
+ The Laplacian can be taken with respect to a metric tensor that is
4203
+ not the default one::
4204
+
4205
+ sage: h = M.lorentzian_metric('h')
4206
+ sage: h[1,1], h[2,2] = -1, 1+x^2
4207
+ sage: Dv = v.laplacian(h); Dv
4208
+ Vector field Delta_h(v) on the Euclidean plane E^2
4209
+ sage: Dv.display()
4210
+ Delta_h(v) = -(8*x^5 - 2*x^4 - x^2*y^2 + 15*x^3 - 4*x^2 + 6*x
4211
+ - 2)/(x^4 + 2*x^2 + 1) e_x - 3*x^3*y/(x^4 + 2*x^2 + 1) e_y
4212
+ """
4213
+ n_con = self._tensor_type[0] # number of contravariant indices = k
4214
+ trank = self._tensor_rank # k + l
4215
+ default_metric = metric is None
4216
+ if default_metric:
4217
+ metric = self._domain.metric()
4218
+ nabla = metric.connection()
4219
+ tmp = nabla(nabla(self).up(metric, pos=trank))
4220
+ resu = tmp.trace(n_con, trank+1)
4221
+ if self._name is not None:
4222
+ if default_metric:
4223
+ resu._name = "Delta({})".format(self._name)
4224
+ resu._latex_name = r"\Delta\left(" + self._latex_name + \
4225
+ r"\right)"
4226
+ else:
4227
+ resu._name = "Delta_{}({})".format(metric._name, self._name)
4228
+ resu._latex_name = r"\Delta_{" + metric._latex_name + \
4229
+ r"}\left(" + self._latex_name + r"\right)"
4230
+ # The name is propagated to possible restrictions of self:
4231
+ for restrict in resu._restrictions.values():
4232
+ restrict.set_name(resu._name, latex_name=resu._latex_name)
4233
+ return resu
4234
+
4235
+ def dalembertian(self, metric=None):
4236
+ r"""
4237
+ Return the d'Alembertian of ``self`` with respect to a given
4238
+ Lorentzian metric.
4239
+
4240
+ The *d'Alembertian* of a tensor field `t` with respect to a Lorentzian
4241
+ metric `g` is nothing but the Laplace-Beltrami operator of `g` applied
4242
+ to `t` (see :meth:`laplacian`); if ``self`` a tensor field `t` of type
4243
+ `(k,l)`, the d'Alembertian of `t` with respect to `g` is then the
4244
+ tensor field of type `(k,l)` defined by
4245
+
4246
+ .. MATH::
4247
+
4248
+ (\Box t)^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}}
4249
+ = \nabla_i \nabla^i
4250
+ t^{a_1\ldots a_k}_{\phantom{a_1\ldots a_k}\,{b_1\ldots b_k}},
4251
+
4252
+ where `\nabla` is the Levi-Civita connection of `g` (cf.
4253
+ :class:`~sage.manifolds.differentiable.levi_civita_connection.LeviCivitaConnection`)
4254
+ and `\nabla^i := g^{ij} \nabla_j`.
4255
+
4256
+ .. NOTE::
4257
+
4258
+ If the metric `g` is not Lorentzian, the name *d'Alembertian* is
4259
+ not appropriate and one should use :meth:`laplacian` instead.
4260
+
4261
+ INPUT:
4262
+
4263
+ - ``metric`` -- (default: ``None``) the Lorentzian metric `g`
4264
+ involved in the definition of the d'Alembertian; if none is provided,
4265
+ the domain of ``self`` is supposed to be endowed with a default
4266
+ Lorentzian metric (i.e. is supposed to be Lorentzian manifold, see
4267
+ :class:`~sage.manifolds.differentiable.pseudo_riemannian.PseudoRiemannianManifold`)
4268
+ and the latter is used to define the d'Alembertian
4269
+
4270
+ OUTPUT:
4271
+
4272
+ - instance of :class:`TensorField` representing the d'Alembertian of
4273
+ ``self``
4274
+
4275
+ EXAMPLES:
4276
+
4277
+ d'Alembertian of a vector field in Minkowski spacetime, representing
4278
+ the electric field of a simple plane electromagnetic wave::
4279
+
4280
+ sage: M = Manifold(4, 'M', structure='Lorentzian')
4281
+ sage: X.<t,x,y,z> = M.chart()
4282
+ sage: g = M.metric()
4283
+ sage: g[0,0], g[1,1], g[2,2], g[3,3] = -1, 1, 1, 1
4284
+ sage: e = M.vector_field(name='e')
4285
+ sage: e[1] = cos(t-z)
4286
+ sage: e.display() # plane wave propagating in the z direction
4287
+ e = cos(t - z) ∂/∂x
4288
+ sage: De = e.dalembertian(); De # long time
4289
+ Vector field Box(e) on the 4-dimensional Lorentzian manifold M
4290
+
4291
+ The function :func:`~sage.manifolds.operators.dalembertian` from the
4292
+ :mod:`~sage.manifolds.operators` module can be used instead of the
4293
+ method :meth:`dalembertian`::
4294
+
4295
+ sage: from sage.manifolds.operators import dalembertian
4296
+ sage: dalembertian(e) == De # long time
4297
+ True
4298
+
4299
+ We check that the electric field obeys the wave equation::
4300
+
4301
+ sage: De.display() # long time
4302
+ Box(e) = 0
4303
+ """
4304
+ default_metric = metric is None
4305
+ if default_metric:
4306
+ metric = self._domain.metric()
4307
+ nm2 = self._domain.dim() - 2
4308
+ if metric.signature() not in [nm2, -nm2]:
4309
+ raise TypeError("the {} is not a Lorentzian ".format(metric) +
4310
+ "metric; use laplacian() instead")
4311
+ resu = self.laplacian(metric=metric)
4312
+ if self._name is not None:
4313
+ if default_metric:
4314
+ resu._name = "Box({})".format(self._name)
4315
+ resu._latex_name = r"\Box\left(" + self._latex_name + \
4316
+ r"\right)"
4317
+ else:
4318
+ resu._name = "Box_{}({})".format(metric._name, self._name)
4319
+ resu._latex_name = r"\Box_{" + metric._latex_name + \
4320
+ r"}\left(" + self._latex_name + r"\right)"
4321
+ # The name is propagated to possible restrictions of self:
4322
+ for restrict in resu._restrictions.values():
4323
+ restrict.set_name(resu._name, latex_name=resu._latex_name)
4324
+ return resu
4325
+
4326
+ def along(self, mapping):
4327
+ r"""
4328
+ Return the tensor field deduced from ``self`` via a differentiable map,
4329
+ the codomain of which is included in the domain of ``self``.
4330
+
4331
+ More precisely, if ``self`` is a tensor field `t` on `M` and if
4332
+ `\Phi: U \rightarrow M` is a differentiable map from some
4333
+ differentiable manifold `U` to `M`, the returned object is
4334
+ a tensor field `\tilde t` along `U` with values on `M` such that
4335
+
4336
+ .. MATH::
4337
+
4338
+ \forall p \in U,\ \tilde t(p) = t(\Phi(p)).
4339
+
4340
+ INPUT:
4341
+
4342
+ - ``mapping`` -- differentiable map `\Phi: U \rightarrow M`
4343
+
4344
+ OUTPUT: tensor field `\tilde t` along `U` defined above
4345
+
4346
+ EXAMPLES:
4347
+
4348
+ Let us consider the 2-dimensional sphere `S^2`::
4349
+
4350
+ sage: M = Manifold(2, 'S^2') # the 2-dimensional sphere S^2
4351
+ sage: U = M.open_subset('U') # complement of the North pole
4352
+ sage: c_xy.<x,y> = U.chart() # stereographic coordinates from the North pole
4353
+ sage: V = M.open_subset('V') # complement of the South pole
4354
+ sage: c_uv.<u,v> = V.chart() # stereographic coordinates from the South pole
4355
+ sage: M.declare_union(U,V) # S^2 is the union of U and V
4356
+ sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
4357
+ ....: intersection_name='W', restrictions1= x^2+y^2!=0,
4358
+ ....: restrictions2= u^2+v^2!=0)
4359
+ sage: uv_to_xy = xy_to_uv.inverse()
4360
+ sage: W = U.intersection(V)
4361
+
4362
+ and the following map from the open interval `(0,5\pi/2)` to `S^2`,
4363
+ the image of it being the great circle `x=0`, `u=0`, which goes through
4364
+ the North and South poles::
4365
+
4366
+ sage: I.<t> = manifolds.OpenInterval(0, 5*pi/2)
4367
+ sage: J = I.open_interval(0, 3*pi/2)
4368
+ sage: K = I.open_interval(pi, 5*pi/2)
4369
+ sage: c_J = J.canonical_chart(); c_K = K.canonical_chart()
4370
+ sage: Phi = I.diff_map(M, {(c_J, c_xy):
4371
+ ....: (0, sgn(pi-t)*sqrt((1+cos(t))/(1-cos(t)))),
4372
+ ....: (c_K, c_uv):
4373
+ ....: (0, sgn(t-2*pi)*sqrt((1-cos(t))/(1+cos(t))))},
4374
+ ....: name='Phi')
4375
+
4376
+ Let us consider a vector field on `S^2`::
4377
+
4378
+ sage: eU = c_xy.frame(); eV = c_uv.frame()
4379
+ sage: w = M.vector_field(name='w')
4380
+ sage: w[eU,0] = 1
4381
+ sage: w.add_comp_by_continuation(eV, W, chart=c_uv)
4382
+ sage: w.display(eU)
4383
+ w = ∂/∂x
4384
+ sage: w.display(eV)
4385
+ w = (-u^2 + v^2) ∂/∂u - 2*u*v ∂/∂v
4386
+
4387
+ We have then::
4388
+
4389
+ sage: wa = w.along(Phi); wa
4390
+ Vector field w along the Real interval (0, 5/2*pi) with values on
4391
+ the 2-dimensional differentiable manifold S^2
4392
+ sage: wa.display(eU.along(Phi))
4393
+ w = ∂/∂x
4394
+ sage: wa.display(eV.along(Phi))
4395
+ w = -(cos(t) - 1)*sgn(-2*pi + t)^2/(cos(t) + 1) ∂/∂u
4396
+
4397
+ Some tests::
4398
+
4399
+ sage: p = K.an_element()
4400
+ sage: wa.at(p) == w.at(Phi(p))
4401
+ True
4402
+ sage: wa.at(J(4*pi/3)) == wa.at(K(4*pi/3))
4403
+ True
4404
+ sage: wa.at(I(4*pi/3)) == wa.at(K(4*pi/3))
4405
+ True
4406
+ sage: wa.at(K(7*pi/4)) == eU[0].at(Phi(I(7*pi/4))) # since eU[0]=∂/∂x
4407
+ True
4408
+ """
4409
+ dom = self._domain
4410
+ if self._ambient_domain is not dom:
4411
+ raise ValueError("{} is not a tensor field ".format(self) +
4412
+ "with values in the {}".format(dom))
4413
+ if mapping.codomain().is_subset(dom):
4414
+ rmapping = mapping
4415
+ else:
4416
+ rmapping = None
4417
+ for doms, rest in mapping._restrictions.items():
4418
+ if doms[1].is_subset(dom):
4419
+ rmapping = rest
4420
+ break
4421
+ else:
4422
+ raise ValueError("the codomain of {} is not ".format(mapping) +
4423
+ "included in the domain of {}".format(self))
4424
+ resu_ambient_domain = rmapping.codomain()
4425
+ if resu_ambient_domain.is_manifestly_parallelizable():
4426
+ return self.restrict(resu_ambient_domain).along(rmapping)
4427
+ dom_resu = rmapping.domain()
4428
+ vmodule = dom_resu.vector_field_module(dest_map=rmapping)
4429
+ resu = vmodule.tensor(self._tensor_type, name=self._name,
4430
+ latex_name=self._latex_name, sym=self._sym,
4431
+ antisym=self._antisym)
4432
+ for rdom in resu_ambient_domain._parallelizable_parts:
4433
+ if rdom in resu_ambient_domain._top_subsets:
4434
+ for chart1, chart2 in rmapping._coord_expression:
4435
+ if chart2.domain() is rdom:
4436
+ dom1 = chart1.domain()
4437
+ rrmap = rmapping.restrict(dom1, subcodomain=rdom)
4438
+ resu._restrictions[dom1] = self.restrict(rdom).along(rrmap)
4439
+ return resu
4440
+
4441
+ def set_calc_order(self, symbol, order, truncate=False):
4442
+ r"""
4443
+ Trigger a series expansion with respect to a small parameter in
4444
+ computations involving the tensor field.
4445
+
4446
+ This property is propagated by usual operations. The internal
4447
+ representation must be ``SR`` for this to take effect.
4448
+
4449
+ If the small parameter is `\epsilon` and `T` is ``self``, the
4450
+ power series expansion to order `n` is
4451
+
4452
+ .. MATH::
4453
+
4454
+ T = T_0 + \epsilon T_1 + \epsilon^2 T_2 + \cdots + \epsilon^n T_n
4455
+ + O(\epsilon^{n+1}),
4456
+
4457
+ where `T_0, T_1, \ldots, T_n` are `n+1` tensor fields of the same
4458
+ tensor type as ``self`` and do not depend upon `\epsilon`.
4459
+
4460
+ INPUT:
4461
+
4462
+ - ``symbol`` -- symbolic variable (the "small parameter" `\epsilon`)
4463
+ with respect to which the components of ``self`` are expanded in
4464
+ power series
4465
+ - ``order`` -- integer; the order `n` of the expansion, defined as the
4466
+ degree of the polynomial representing the truncated power series in
4467
+ ``symbol``
4468
+ - ``truncate`` -- boolean (default: ``False``); determines whether the
4469
+ components of ``self`` are replaced by their expansions to the
4470
+ given order
4471
+
4472
+ EXAMPLES:
4473
+
4474
+ Let us consider two vector fields depending on a small parameter `h`
4475
+ on a non-parallelizable manifold::
4476
+
4477
+ sage: M = Manifold(2, 'M')
4478
+ sage: U = M.open_subset('U') ; V = M.open_subset('V')
4479
+ sage: M.declare_union(U,V) # M is the union of U and V
4480
+ sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
4481
+ sage: transf = c_xy.transition_map(c_uv, (x+y, x-y), intersection_name='W',
4482
+ ....: restrictions1= x>0, restrictions2= u+v>0)
4483
+ sage: inv = transf.inverse()
4484
+ sage: W = U.intersection(V)
4485
+ sage: eU = c_xy.frame() ; eV = c_uv.frame()
4486
+ sage: a = M.vector_field()
4487
+ sage: h = var('h', domain='real')
4488
+ sage: a[eU,:] = (cos(h*x), -y)
4489
+ sage: a.add_comp_by_continuation(eV, W, chart=c_uv)
4490
+ sage: b = M.vector_field()
4491
+ sage: b[eU,:] = (exp(h*x), exp(h*y))
4492
+ sage: b.add_comp_by_continuation(eV, W, chart=c_uv)
4493
+
4494
+ If we set the calculus order on one of the vector fields, any operation
4495
+ involving both of them is performed to that order::
4496
+
4497
+ sage: a.set_calc_order(h, 2)
4498
+ sage: s = a + b
4499
+ sage: s[eU,:]
4500
+ [h*x + 2, 1/2*h^2*y^2 + h*y - y + 1]
4501
+ sage: s[eV,:]
4502
+ [1/8*(u^2 - 2*u*v + v^2)*h^2 + h*u - 1/2*u + 1/2*v + 3,
4503
+ -1/8*(u^2 - 2*u*v + v^2)*h^2 + h*v + 1/2*u - 1/2*v + 1]
4504
+
4505
+ Note that the components of ``a`` have not been affected by the above
4506
+ call to ``set_calc_order``::
4507
+
4508
+ sage: a[eU,:]
4509
+ [cos(h*x), -y]
4510
+ sage: a[eV,:]
4511
+ [cos(1/2*h*u)*cos(1/2*h*v) - sin(1/2*h*u)*sin(1/2*h*v) - 1/2*u + 1/2*v,
4512
+ cos(1/2*h*u)*cos(1/2*h*v) - sin(1/2*h*u)*sin(1/2*h*v) + 1/2*u - 1/2*v]
4513
+
4514
+ To have ``set_calc_order`` act on them, set the optional argument
4515
+ ``truncate`` to ``True``::
4516
+
4517
+ sage: a.set_calc_order(h, 2, truncate=True)
4518
+ sage: a[eU,:]
4519
+ [-1/2*h^2*x^2 + 1, -y]
4520
+ sage: a[eV,:]
4521
+ [-1/8*(u^2 + 2*u*v + v^2)*h^2 - 1/2*u + 1/2*v + 1,
4522
+ -1/8*(u^2 + 2*u*v + v^2)*h^2 + 1/2*u - 1/2*v + 1]
4523
+ """
4524
+ for rst in self._restrictions.values():
4525
+ rst.set_calc_order(symbol, order, truncate)
4526
+ self._del_derived()
4527
+
4528
+ def apply_map(self, fun, frame=None, chart=None,
4529
+ keep_other_components=False):
4530
+ r"""
4531
+ Apply a function to the coordinate expressions of all components of
4532
+ ``self`` in a given vector frame.
4533
+
4534
+ This method allows operations like factorization, expansion,
4535
+ simplification or substitution to be performed on all components of
4536
+ ``self`` in a given vector frame (see examples below).
4537
+
4538
+ INPUT:
4539
+
4540
+ - ``fun`` -- function to be applied to the coordinate expressions of
4541
+ the components
4542
+ - ``frame`` -- (default: ``None``) vector frame defining the
4543
+ components on which the operation ``fun`` is to be performed; if
4544
+ ``None``, the default frame of the domain of ``self`` is assumed
4545
+ - ``chart`` -- (default: ``None``) coordinate chart; if specified, the
4546
+ operation ``fun`` is performed only on the coordinate expressions
4547
+ with respect to ``chart`` of the components w.r.t. ``frame``; if
4548
+ ``None``, the operation ``fun`` is performed on all available
4549
+ coordinate expressions
4550
+ - ``keep_other_components`` -- boolean (default: ``False``); determine whether
4551
+ the components with respect to vector frames distinct from ``frame``
4552
+ and having the same domain as ``frame`` are kept. If ``fun`` is
4553
+ non-destructive, ``keep_other_components`` can be set to ``True``;
4554
+ otherwise, it is advised to set it to ``False`` (the default) in
4555
+ order to avoid any inconsistency between the various sets of
4556
+ components
4557
+
4558
+ EXAMPLES:
4559
+
4560
+ Factorizing all components in the default frame of a vector field::
4561
+
4562
+ sage: M = Manifold(2, 'M')
4563
+ sage: X.<x,y> = M.chart()
4564
+ sage: a, b = var('a b')
4565
+ sage: v = M.vector_field(x^2 - y^2, a*(b^2 - b)*x)
4566
+ sage: v.display()
4567
+ (x^2 - y^2) ∂/∂x + (b^2 - b)*a*x ∂/∂y
4568
+ sage: v.apply_map(factor)
4569
+ sage: v.display()
4570
+ (x + y)*(x - y) ∂/∂x + a*(b - 1)*b*x ∂/∂y
4571
+
4572
+ Performing a substitution in all components in the default frame::
4573
+
4574
+ sage: v.apply_map(lambda f: f.subs({a: 2}))
4575
+ sage: v.display()
4576
+ (x + y)*(x - y) ∂/∂x + 2*(b - 1)*b*x ∂/∂y
4577
+
4578
+ Specifying the vector frame via the argument ``frame``::
4579
+
4580
+ sage: P.<p, q> = M.chart()
4581
+ sage: X_to_P = X.transition_map(P, [x + 1, y - 1])
4582
+ sage: P_to_X = X_to_P.inverse()
4583
+ sage: v.display(P)
4584
+ (p^2 - q^2 - 2*p - 2*q) ∂/∂p + (-2*b^2 + 2*(b^2 - b)*p + 2*b) ∂/∂q
4585
+ sage: v.apply_map(lambda f: f.subs({b: pi}), frame=P.frame())
4586
+ sage: v.display(P)
4587
+ (p^2 - q^2 - 2*p - 2*q) ∂/∂p + (2*pi - 2*pi^2 - 2*(pi - pi^2)*p) ∂/∂q
4588
+
4589
+ Note that the required operation has been performed in all charts::
4590
+
4591
+ sage: v.display(P.frame(), P)
4592
+ (p^2 - q^2 - 2*p - 2*q) ∂/∂p + (2*pi - 2*pi^2 - 2*(pi - pi^2)*p) ∂/∂q
4593
+ sage: v.display(P.frame(), X)
4594
+ (x + y)*(x - y) ∂/∂p + 2*pi*(pi - 1)*x ∂/∂q
4595
+
4596
+ By default, the components of ``v`` in frames distinct from the
4597
+ specified one have been deleted::
4598
+
4599
+ sage: X.frame() in v._components
4600
+ False
4601
+
4602
+ When requested, they are recomputed by change-of-frame formulas,
4603
+ thereby enforcing the consistency between the representations in
4604
+ various vector frames. In particular, we can check that the
4605
+ substitution of ``b`` by ``pi``, which was asked in ``P.frame()``,
4606
+ is effective in ``X.frame()`` as well::
4607
+
4608
+ sage: v.display(X.frame(), X)
4609
+ (x + y)*(x - y) ∂/∂x + 2*pi*(pi - 1)*x ∂/∂y
4610
+
4611
+ When the requested operation does not change the value of the tensor
4612
+ field, one can use the keyword argument ``keep_other_components=True``,
4613
+ in order to avoid the recomputation of the components in other frames::
4614
+
4615
+ sage: v.apply_map(factor, keep_other_components=True)
4616
+ sage: v.display()
4617
+ (x + y)*(x - y) ∂/∂x + 2*pi*(pi - 1)*x ∂/∂y
4618
+
4619
+ The components with respect to ``P.frame()`` have been kept::
4620
+
4621
+ sage: P.frame() in v._components
4622
+ True
4623
+
4624
+ One can restrict the operation to expressions in a given chart, via
4625
+ the argument ``chart``::
4626
+
4627
+ sage: v.display(X.frame(), P)
4628
+ (p + q)*(p - q - 2) ∂/∂x + 2*pi*(pi - 1)*(p - 1) ∂/∂y
4629
+ sage: v.apply_map(expand, chart=P)
4630
+ sage: v.display(X.frame(), P)
4631
+ (p^2 - q^2 - 2*p - 2*q) ∂/∂x + (2*pi + 2*pi^2*p - 2*pi^2 - 2*pi*p) ∂/∂y
4632
+ sage: v.display(X.frame(), X)
4633
+ (x + y)*(x - y) ∂/∂x + 2*pi*(pi - 1)*x ∂/∂y
4634
+
4635
+ TESTS:
4636
+
4637
+ Check that the cached quantities derived from the components are
4638
+ erased::
4639
+
4640
+ sage: w = M.vector_field(a*x, 0)
4641
+ sage: diff(w[[0]]).display()
4642
+ a dx
4643
+ sage: w.apply_map(lambda t: t.subs(a=-2))
4644
+ sage: w.display()
4645
+ -2*x ∂/∂x
4646
+ sage: diff(w[[0]]).display()
4647
+ -2 dx
4648
+ """
4649
+ # The dictionary of components w.r.t. frame:
4650
+ if keep_other_components:
4651
+ comps = self.comp(frame)._comp
4652
+ else:
4653
+ comps = self.set_comp(frame)._comp # set_comp() deletes the
4654
+ # components in other frames
4655
+ if chart:
4656
+ for scalar in comps.values():
4657
+ scalar.add_expr(fun(scalar.expr(chart=chart)), chart=chart)
4658
+ else:
4659
+ for scalar in comps.values():
4660
+ cfunc_dict = {} # new dict of chart functions in order not to
4661
+ # modify scalar._express while looping on it
4662
+ for ch, fct in scalar._express.items():
4663
+ cfunc_dict[ch] = ch.function(fun(fct.expr()))
4664
+ scalar._express = cfunc_dict
4665
+ scalar._del_derived()