passagemath-symbolics 10.6.37__cp310-cp310-musllinux_1_2_x86_64.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 (171) hide show
  1. passagemath_symbolics/__init__.py +3 -0
  2. passagemath_symbolics-10.6.37.dist-info/METADATA +187 -0
  3. passagemath_symbolics-10.6.37.dist-info/RECORD +171 -0
  4. passagemath_symbolics-10.6.37.dist-info/WHEEL +5 -0
  5. passagemath_symbolics-10.6.37.dist-info/top_level.txt +3 -0
  6. sage/all__sagemath_symbolics.py +17 -0
  7. sage/calculus/all.py +14 -0
  8. sage/calculus/calculus.py +2826 -0
  9. sage/calculus/desolvers.py +1866 -0
  10. sage/calculus/predefined.py +51 -0
  11. sage/calculus/tests.py +225 -0
  12. sage/calculus/var.cpython-310-x86_64-linux-gnu.so +0 -0
  13. sage/calculus/var.pyx +401 -0
  14. sage/dynamics/all__sagemath_symbolics.py +6 -0
  15. sage/dynamics/complex_dynamics/all.py +5 -0
  16. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  17. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-310-x86_64-linux-gnu.so +0 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
  19. sage/ext/all__sagemath_symbolics.py +1 -0
  20. sage/ext_data/kenzo/CP2.txt +45 -0
  21. sage/ext_data/kenzo/CP3.txt +349 -0
  22. sage/ext_data/kenzo/CP4.txt +4774 -0
  23. sage/ext_data/kenzo/README.txt +49 -0
  24. sage/ext_data/kenzo/S4.txt +20 -0
  25. sage/ext_data/magma/latex/latex.m +1021 -0
  26. sage/ext_data/magma/latex/latex.spec +1 -0
  27. sage/ext_data/magma/sage/basic.m +356 -0
  28. sage/ext_data/magma/sage/sage.spec +1 -0
  29. sage/ext_data/magma/spec +9 -0
  30. sage/geometry/all__sagemath_symbolics.py +8 -0
  31. sage/geometry/hyperbolic_space/all.py +5 -0
  32. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  39. sage/geometry/riemannian_manifolds/all.py +7 -0
  40. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  41. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  42. sage/interfaces/all__sagemath_symbolics.py +1 -0
  43. sage/interfaces/magma.py +3017 -0
  44. sage/interfaces/magma_free.py +92 -0
  45. sage/interfaces/maple.py +1397 -0
  46. sage/interfaces/mathematica.py +1345 -0
  47. sage/interfaces/mathics.py +1312 -0
  48. sage/interfaces/sympy.py +1398 -0
  49. sage/interfaces/sympy_wrapper.py +197 -0
  50. sage/interfaces/tides.py +938 -0
  51. sage/libs/all__sagemath_symbolics.py +6 -0
  52. sage/manifolds/all.py +7 -0
  53. sage/manifolds/calculus_method.py +555 -0
  54. sage/manifolds/catalog.py +437 -0
  55. sage/manifolds/chart.py +4019 -0
  56. sage/manifolds/chart_func.py +3419 -0
  57. sage/manifolds/continuous_map.py +2183 -0
  58. sage/manifolds/continuous_map_image.py +155 -0
  59. sage/manifolds/differentiable/affine_connection.py +2475 -0
  60. sage/manifolds/differentiable/all.py +1 -0
  61. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  62. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  63. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  64. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  65. sage/manifolds/differentiable/chart.py +1241 -0
  66. sage/manifolds/differentiable/curve.py +1028 -0
  67. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  68. sage/manifolds/differentiable/degenerate.py +559 -0
  69. sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
  70. sage/manifolds/differentiable/diff_form.py +1658 -0
  71. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  72. sage/manifolds/differentiable/diff_map.py +1315 -0
  73. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  74. sage/manifolds/differentiable/examples/all.py +1 -0
  75. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  76. sage/manifolds/differentiable/examples/real_line.py +897 -0
  77. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  78. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  79. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  80. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  81. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  82. sage/manifolds/differentiable/manifold.py +4254 -0
  83. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  84. sage/manifolds/differentiable/metric.py +3032 -0
  85. sage/manifolds/differentiable/mixed_form.py +1507 -0
  86. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  87. sage/manifolds/differentiable/multivector_module.py +800 -0
  88. sage/manifolds/differentiable/multivectorfield.py +1520 -0
  89. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  90. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  91. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  92. sage/manifolds/differentiable/scalarfield.py +1343 -0
  93. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  94. sage/manifolds/differentiable/symplectic_form.py +910 -0
  95. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  96. sage/manifolds/differentiable/tangent_space.py +412 -0
  97. sage/manifolds/differentiable/tangent_vector.py +616 -0
  98. sage/manifolds/differentiable/tensorfield.py +4665 -0
  99. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  100. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  101. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  102. sage/manifolds/differentiable/vector_bundle.py +1728 -0
  103. sage/manifolds/differentiable/vectorfield.py +1717 -0
  104. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  105. sage/manifolds/differentiable/vectorframe.py +1832 -0
  106. sage/manifolds/family.py +270 -0
  107. sage/manifolds/local_frame.py +1490 -0
  108. sage/manifolds/manifold.py +3090 -0
  109. sage/manifolds/manifold_homset.py +452 -0
  110. sage/manifolds/operators.py +359 -0
  111. sage/manifolds/point.py +994 -0
  112. sage/manifolds/scalarfield.py +3718 -0
  113. sage/manifolds/scalarfield_algebra.py +629 -0
  114. sage/manifolds/section.py +3111 -0
  115. sage/manifolds/section_module.py +831 -0
  116. sage/manifolds/structure.py +229 -0
  117. sage/manifolds/subset.py +2764 -0
  118. sage/manifolds/subsets/all.py +1 -0
  119. sage/manifolds/subsets/closure.py +131 -0
  120. sage/manifolds/subsets/pullback.py +885 -0
  121. sage/manifolds/topological_submanifold.py +891 -0
  122. sage/manifolds/trivialization.py +733 -0
  123. sage/manifolds/utilities.py +1348 -0
  124. sage/manifolds/vector_bundle.py +1342 -0
  125. sage/manifolds/vector_bundle_fiber.py +332 -0
  126. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  127. sage/matrix/all__sagemath_symbolics.py +1 -0
  128. sage/matrix/matrix_symbolic_dense.cpython-310-x86_64-linux-gnu.so +0 -0
  129. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  130. sage/matrix/matrix_symbolic_dense.pyx +1022 -0
  131. sage/matrix/matrix_symbolic_sparse.cpython-310-x86_64-linux-gnu.so +0 -0
  132. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  133. sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
  134. sage/modules/all__sagemath_symbolics.py +1 -0
  135. sage/modules/vector_callable_symbolic_dense.py +105 -0
  136. sage/modules/vector_symbolic_dense.py +116 -0
  137. sage/modules/vector_symbolic_sparse.py +118 -0
  138. sage/rings/all__sagemath_symbolics.py +4 -0
  139. sage/rings/asymptotic/all.py +6 -0
  140. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  141. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  142. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
  143. sage/rings/asymptotic/growth_group.py +5373 -0
  144. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  145. sage/rings/asymptotic/term_monoid.py +5237 -0
  146. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  147. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  148. sage/symbolic/all.py +15 -0
  149. sage/symbolic/assumptions.py +985 -0
  150. sage/symbolic/benchmark.py +93 -0
  151. sage/symbolic/callable.py +459 -0
  152. sage/symbolic/complexity_measures.py +35 -0
  153. sage/symbolic/constants.py +1287 -0
  154. sage/symbolic/expression_conversion_algebraic.py +310 -0
  155. sage/symbolic/expression_conversion_sympy.py +317 -0
  156. sage/symbolic/expression_conversions.py +1713 -0
  157. sage/symbolic/function_factory.py +355 -0
  158. sage/symbolic/integration/all.py +1 -0
  159. sage/symbolic/integration/external.py +270 -0
  160. sage/symbolic/integration/integral.py +1115 -0
  161. sage/symbolic/maxima_wrapper.py +162 -0
  162. sage/symbolic/operators.py +267 -0
  163. sage/symbolic/random_tests.py +462 -0
  164. sage/symbolic/relation.py +1907 -0
  165. sage/symbolic/ring.cpython-310-x86_64-linux-gnu.so +0 -0
  166. sage/symbolic/ring.pxd +5 -0
  167. sage/symbolic/ring.pyx +1396 -0
  168. sage/symbolic/subring.py +1025 -0
  169. sage/symbolic/symengine.py +19 -0
  170. sage/symbolic/tests.py +40 -0
  171. sage/symbolic/units.py +1470 -0
@@ -0,0 +1,2450 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Tensor Fields with Values on a Parallelizable Manifold
4
+
5
+ The class :class:`TensorFieldParal` implements tensor fields along a
6
+ differentiable manifolds with values on a parallelizable differentiable
7
+ manifold. For non-parallelizable manifolds, see the class
8
+ :class:`~sage.manifolds.differentiable.tensorfield.TensorField`.
9
+
10
+ Various derived classes of :class:`TensorFieldParal` are devoted to specific
11
+ tensor fields:
12
+
13
+ * :class:`~sage.manifolds.differentiable.vectorfield.VectorFieldParal` for
14
+ vector fields (rank-1 contravariant tensor fields)
15
+
16
+ * :class:`~sage.manifolds.differentiable.automorphismfield.AutomorphismFieldParal`
17
+ for fields of tangent-space automorphisms
18
+
19
+ * :class:`~sage.manifolds.differentiable.diff_form.DiffFormParal` for
20
+ differential forms (fully antisymmetric covariant tensor fields)
21
+
22
+ * :class:`~sage.manifolds.differentiable.multivectorfield.MultivectorFieldParal`
23
+ for multivector fields (fully antisymmetric contravariant tensor fields)
24
+
25
+
26
+ AUTHORS:
27
+
28
+ - Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version
29
+ - Travis Scrimshaw (2016): review tweaks
30
+ - Eric Gourgoulhon (2018): method :meth:`TensorFieldParal.along`
31
+ - Florentin Jaffredo (2018) : series expansion with respect to a given
32
+ parameter
33
+
34
+ REFERENCES:
35
+
36
+ - [KN1963]_
37
+ - [Lee2013]_
38
+ - [ONe1983]_
39
+
40
+ EXAMPLES:
41
+
42
+ A tensor field of type `(1,1)` on a 2-dimensional differentiable manifold::
43
+
44
+ sage: M = Manifold(2, 'M', start_index=1)
45
+ sage: c_xy.<x,y> = M.chart()
46
+ sage: t = M.tensor_field(1, 1, name='T') ; t
47
+ Tensor field T of type (1,1) on the 2-dimensional differentiable manifold M
48
+ sage: t.tensor_type()
49
+ (1, 1)
50
+ sage: t.tensor_rank()
51
+ 2
52
+
53
+ Components with respect to the manifold's default frame are created
54
+ by providing the relevant indices inside square brackets::
55
+
56
+ sage: t[1,1] = x^2
57
+
58
+ Unset components are initialized to zero::
59
+
60
+ sage: t[:] # list of components w.r.t. the manifold's default vector frame
61
+ [x^2 0]
62
+ [ 0 0]
63
+
64
+ It is also possible to initialize the components at the tensor field
65
+ construction::
66
+
67
+ sage: t = M.tensor_field(1, 1, [[x^2, 0], [0, 0]], name='T')
68
+ sage: t[:]
69
+ [x^2 0]
70
+ [ 0 0]
71
+
72
+ The full set of components with respect to a given vector frame is
73
+ returned by the method
74
+ :meth:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.comp`::
75
+
76
+ sage: t.comp(c_xy.frame())
77
+ 2-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
78
+
79
+ If no vector frame is mentioned in the argument of
80
+ :meth:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.comp`,
81
+ it is assumed to be the manifold's default frame::
82
+
83
+ sage: M.default_frame()
84
+ Coordinate frame (M, (∂/∂x,∂/∂y))
85
+ sage: t.comp() is t.comp(c_xy.frame())
86
+ True
87
+
88
+ Individual components with respect to the manifold's default frame are
89
+ accessed by listing their indices inside double square brackets. They
90
+ are :class:`scalar fields
91
+ <sage.manifolds.differentiable.scalarfield.DiffScalarField>` on the manifold::
92
+
93
+ sage: t[[1,1]]
94
+ Scalar field on the 2-dimensional differentiable manifold M
95
+ sage: t[[1,1]].display()
96
+ M → ℝ
97
+ (x, y) ↦ x^2
98
+ sage: t[[1,2]]
99
+ Scalar field zero on the 2-dimensional differentiable manifold M
100
+ sage: t[[1,2]].display()
101
+ zero: M → ℝ
102
+ (x, y) ↦ 0
103
+
104
+ A direct access to the coordinate expression of some component is obtained
105
+ via the single square brackets::
106
+
107
+ sage: t[1,1]
108
+ x^2
109
+ sage: t[1,1] is t[[1,1]].coord_function() # the coordinate function
110
+ True
111
+ sage: t[1,1] is t[[1,1]].coord_function(c_xy)
112
+ True
113
+ sage: t[1,1].expr() is t[[1,1]].expr() # the symbolic expression
114
+ True
115
+
116
+ Expressions in a chart different from the manifold's default one are
117
+ obtained by specifying the chart as the last argument inside the
118
+ single square brackets::
119
+
120
+ sage: c_uv.<u,v> = M.chart()
121
+ sage: xy_to_uv = c_xy.transition_map(c_uv, [x+y, x-y])
122
+ sage: uv_to_xy = xy_to_uv.inverse()
123
+ sage: t[1,1, c_uv]
124
+ 1/4*u^2 + 1/2*u*v + 1/4*v^2
125
+
126
+ Note that ``t[1,1, c_uv]`` is the component of the tensor ``t`` with respect
127
+ to the coordinate frame associated to the chart `(x,y)` expressed in terms of
128
+ the coordinates `(u,v)`. Indeed, ``t[1,1, c_uv]`` is a shortcut for
129
+ ``t.comp(c_xy.frame())[[1,1]].coord_function(c_uv)``::
130
+
131
+ sage: t[1,1, c_uv] is t.comp(c_xy.frame())[[1,1]].coord_function(c_uv)
132
+ True
133
+
134
+ Similarly, ``t[1,1]`` is a shortcut for
135
+ ``t.comp(c_xy.frame())[[1,1]].coord_function(c_xy)``::
136
+
137
+ sage: t[1,1] is t.comp(c_xy.frame())[[1,1]].coord_function(c_xy)
138
+ True
139
+ sage: t[1,1] is t.comp()[[1,1]].coord_function() # since c_xy.frame() and c_xy are the manifold's default values
140
+ True
141
+
142
+ All the components can be set at once via ``[:]``::
143
+
144
+ sage: t[:] = [[1, -x], [x*y, 2]]
145
+ sage: t[:]
146
+ [ 1 -x]
147
+ [x*y 2]
148
+
149
+ To set the components in a vector frame different from the manifold's
150
+ default one, the method
151
+ :meth:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.set_comp`
152
+ can be employed::
153
+
154
+ sage: e = M.vector_frame('e')
155
+ sage: t.set_comp(e)[1,1] = x+y
156
+ sage: t.set_comp(e)[2,1], t.set_comp(e)[2,2] = y, -3*x
157
+
158
+ but, as a shortcut, one may simply specify the frame as the first argument
159
+ of the square brackets::
160
+
161
+ sage: t[e,1,1] = x+y
162
+ sage: t[e,2,1], t[e,2,2] = y, -3*x
163
+ sage: t.comp(e)
164
+ 2-indices components w.r.t. Vector frame (M, (e_1,e_2))
165
+ sage: t.comp(e)[:]
166
+ [x + y 0]
167
+ [ y -3*x]
168
+ sage: t[e,:] # a shortcut of the above
169
+ [x + y 0]
170
+ [ y -3*x]
171
+
172
+ All the components in some frame can be set at once, via
173
+ the operator ``[:]``::
174
+
175
+ sage: t[e,:] = [[x+y, 0], [y, -3*x]]
176
+ sage: t[e,:] # same as above:
177
+ [x + y 0]
178
+ [ y -3*x]
179
+
180
+ Equivalently, one can initialize the components in ``e`` at the tensor field
181
+ construction::
182
+
183
+ sage: t = M.tensor_field(1, 1, [[x+y, 0], [y, -3*x]], frame=e, name='T')
184
+ sage: t[e,:] # same as above:
185
+ [x + y 0]
186
+ [ y -3*x]
187
+
188
+ To avoid any inconsistency between the various components, the method
189
+ :meth:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.set_comp`
190
+ clears the components in other frames.
191
+ To keep the other components, one must use the method
192
+ :meth:`~sage.manifolds.differentiable.tensorfield_paral.TensorFieldParal.add_comp`::
193
+
194
+ sage: t = M.tensor_field(1, 1, name='T') # Let us restart
195
+ sage: t[:] = [[1, -x], [x*y, 2]] # by first setting the components in the frame c_xy.frame()
196
+
197
+ We now set the components in the frame e with add_comp::
198
+
199
+ sage: t.add_comp(e)[:] = [[x+y, 0], [y, -3*x]]
200
+
201
+ The expansion of the tensor field in a given frame is obtained via the method
202
+ ``display``::
203
+
204
+ sage: t.display() # expansion in the manifold's default frame
205
+ T = ∂/∂x⊗dx - x ∂/∂x⊗dy + x*y ∂/∂y⊗dx + 2 ∂/∂y⊗dy
206
+ sage: t.display(e)
207
+ T = (x + y) e_1⊗e^1 + y e_2⊗e^1 - 3*x e_2⊗e^2
208
+
209
+ See :meth:`~sage.manifolds.differentiable.tensorfield.TensorField.display`
210
+ for more examples.
211
+
212
+ By definition, a tensor field acts as a multilinear map on 1-forms and vector
213
+ fields; in the present case, ``T`` being of type `(1,1)`, it acts on pairs
214
+ (1-form, vector field)::
215
+
216
+ sage: a = M.one_form(1, x, name='a')
217
+ sage: v = M.vector_field(y, 2, name='V')
218
+ sage: t(a,v)
219
+ Scalar field T(a,V) on the 2-dimensional differentiable manifold M
220
+ sage: t(a,v).display()
221
+ T(a,V): M → ℝ
222
+ (x, y) ↦ x^2*y^2 + 2*x + y
223
+ (u, v) ↦ 1/16*u^4 - 1/8*u^2*v^2 + 1/16*v^4 + 3/2*u + 1/2*v
224
+ sage: latex(t(a,v))
225
+ T\left(a,V\right)
226
+
227
+ Check by means of the component expression of ``t(a,v)``::
228
+
229
+ sage: t(a,v).expr() - t[1,1]*a[1]*v[1] - t[1,2]*a[1]*v[2] \
230
+ ....: - t[2,1]*a[2]*v[1] - t[2,2]*a[2]*v[2]
231
+ 0
232
+
233
+ A scalar field (rank-0 tensor field)::
234
+
235
+ sage: f = M.scalar_field(x*y + 2, name='f') ; f
236
+ Scalar field f on the 2-dimensional differentiable manifold M
237
+ sage: f.tensor_type()
238
+ (0, 0)
239
+
240
+ A scalar field acts on points on the manifold::
241
+
242
+ sage: p = M.point((1,2))
243
+ sage: f(p)
244
+ 4
245
+
246
+ See :class:`~sage.manifolds.differentiable.scalarfield.DiffScalarField` for
247
+ more details on scalar fields.
248
+
249
+ A vector field (rank-1 contravariant tensor field)::
250
+
251
+ sage: v = M.vector_field(-x, y, name='v') ; v
252
+ Vector field v on the 2-dimensional differentiable manifold M
253
+ sage: v.tensor_type()
254
+ (1, 0)
255
+ sage: v.display()
256
+ v = -x ∂/∂x + y ∂/∂y
257
+
258
+ A field of symmetric bilinear forms::
259
+
260
+ sage: q = M.sym_bilin_form_field(name='Q') ; q
261
+ Field of symmetric bilinear forms Q on the 2-dimensional differentiable
262
+ manifold M
263
+ sage: q.tensor_type()
264
+ (0, 2)
265
+
266
+ The components of a symmetric bilinear form are dealt by the subclass
267
+ :class:`~sage.tensor.modules.comp.CompFullySym` of the class
268
+ :class:`~sage.tensor.modules.comp.Components`, which takes into
269
+ account the symmetry between the two indices::
270
+
271
+ sage: q[1,1], q[1,2], q[2,2] = (0, -x, y) # no need to set the component (2,1)
272
+ sage: type(q.comp())
273
+ <class 'sage.tensor.modules.comp.CompFullySym'>
274
+ sage: q[:] # note that the component (2,1) is equal to the component (1,2)
275
+ [ 0 -x]
276
+ [-x y]
277
+ sage: q.display()
278
+ Q = -x dx⊗dy - x dy⊗dx + y dy⊗dy
279
+
280
+ More generally, tensor symmetries or antisymmetries can be specified via
281
+ the keywords ``sym`` and ``antisym``. For instance a rank-4 covariant
282
+ tensor symmetric with respect to its first two arguments (no. 0 and no. 1) and
283
+ antisymmetric with respect to its last two ones (no. 2 and no. 3) is declared
284
+ as follows::
285
+
286
+ sage: t = M.tensor_field(0, 4, name='T', sym=(0,1), antisym=(2,3))
287
+ sage: t[1,2,1,2] = 3
288
+ sage: t[2,1,1,2] # check of the symmetry with respect to the first 2 indices
289
+ 3
290
+ sage: t[1,2,2,1] # check of the antisymmetry with respect to the last 2 indices
291
+ -3
292
+ """
293
+
294
+ # *****************************************************************************
295
+ # Copyright (C) 2015 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
296
+ # Copyright (C) 2015 Michal Bejger <bejger@camk.edu.pl>
297
+ # Copyright (C) 2016 Travis Scrimshaw <tscrimsh@umn.edu>
298
+ # Copyright (C) 2018 Florentin Jaffredo <florentin.jaffredo@polytechnique.edu>
299
+ #
300
+ # Distributed under the terms of the GNU General Public License (GPL)
301
+ # as published by the Free Software Foundation; either version 2 of
302
+ # the License, or (at your option) any later version.
303
+ # https://www.gnu.org/licenses/
304
+ # *****************************************************************************
305
+
306
+ from __future__ import annotations
307
+
308
+ from typing import TYPE_CHECKING, Optional, Union
309
+
310
+ from sage.manifolds.chart import Chart
311
+ from sage.manifolds.differentiable.tensorfield import TensorField
312
+ from sage.parallel.decorate import parallel
313
+ from sage.parallel.parallelism import Parallelism
314
+ from sage.symbolic.ring import SR
315
+ from sage.tensor.modules.free_module_tensor import FreeModuleTensor
316
+
317
+ if TYPE_CHECKING:
318
+ from sage.manifolds.differentiable.diff_map import DiffMap
319
+ from sage.manifolds.differentiable.manifold import DifferentiableManifold
320
+ from sage.symbolic.expression import Expression
321
+ from sage.tensor.modules.comp import Components
322
+
323
+
324
+ class TensorFieldParal(FreeModuleTensor, TensorField):
325
+ r"""
326
+ Tensor field along a differentiable manifold, with values on a
327
+ parallelizable manifold.
328
+
329
+ An instance of this class is a tensor field along a differentiable
330
+ manifold `U` with values on a parallelizable manifold `M`, via a
331
+ differentiable map `\Phi: U \rightarrow M`. More precisely, given two
332
+ nonnegative integers `k` and `l` and a differentiable map
333
+
334
+ .. MATH::
335
+
336
+ \Phi:\ U \longrightarrow M,
337
+
338
+ a *tensor field of type* `(k,l)` *along* `U` *with values on* `M` is
339
+ a differentiable map
340
+
341
+ .. MATH::
342
+
343
+ t:\ U \longrightarrow T^{(k,l)}M
344
+
345
+ (where `T^{(k,l)}M` is the tensor bundle of type `(k,l)` over `M`) such
346
+ that
347
+
348
+ .. MATH::
349
+
350
+ t(p) \in T^{(k,l)}(T_q M)
351
+
352
+ for all `p \in U`, i.e. `t(p)` is a tensor of type `(k,l)` on the
353
+ tangent space `T_q M` at the point `q=\Phi(p)`. That is to say
354
+ a multilinear map
355
+
356
+ .. MATH::
357
+
358
+ t(p):\ \underbrace{T_q^*M\times\cdots\times T_q^*M}_{k\ \; \text{times}}
359
+ \times \underbrace{T_q M\times\cdots\times T_q M}_{l\ \; \text{times}}
360
+ \longrightarrow K,
361
+
362
+ where `T_q^* M` is the dual vector space to `T_q M` and `K` is the
363
+ topological field over which the manifold `M` is defined.
364
+ The integer `k+l` is called the *tensor rank*.
365
+
366
+ The standard case of a tensor field *on* a differentiable manifold
367
+ corresponds to `U=M` and `\Phi = \mathrm{Id}_M`. Other common cases
368
+ are `\Phi` being an immersion and `\Phi` being a curve in `M`
369
+ (`U` is then an open interval of `\RR`).
370
+
371
+ .. NOTE::
372
+
373
+ If `M` is not parallelizable, the class
374
+ :class:`~sage.manifolds.differentiable.tensorfield.TensorField`
375
+ should be used instead.
376
+
377
+ INPUT:
378
+
379
+ - ``vector_field_module`` -- free module `\mathfrak{X}(U,\Phi)` of vector
380
+ fields along `U` associated with the map `\Phi: U \rightarrow M` (cf.
381
+ :class:`~sage.manifolds.differentiable.vectorfield_module.VectorFieldFreeModule`)
382
+ - ``tensor_type`` -- pair `(k,l)` with `k` being the contravariant rank
383
+ and `l` the covariant rank
384
+ - ``name`` -- (default: ``None``) name given to the tensor field
385
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the tensor
386
+ field; if none is provided, the LaTeX symbol is set to ``name``
387
+ - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries among
388
+ the tensor arguments: each symmetry is described by a tuple containing
389
+ the positions of the involved arguments, with the convention position=0
390
+ for the first argument; for instance:
391
+
392
+ * ``sym=(0,1)`` for a symmetry between the 1st and 2nd arguments
393
+ * ``sym=[(0,2),(1,3,4)]`` for a symmetry between the 1st and 3rd
394
+ arguments and a symmetry between the 2nd, 4th and 5th arguments
395
+
396
+ - ``antisym`` -- (default: ``None``) antisymmetry or list of
397
+ antisymmetries among the arguments, with the same convention
398
+ as for ``sym``
399
+
400
+ EXAMPLES:
401
+
402
+ A tensor field of type `(2,0)` on a 3-dimensional parallelizable
403
+ manifold::
404
+
405
+ sage: M = Manifold(3, 'M')
406
+ sage: c_xyz.<x,y,z> = M.chart() # makes M parallelizable
407
+ sage: t = M.tensor_field(2, 0, name='T') ; t
408
+ Tensor field T of type (2,0) on the 3-dimensional differentiable
409
+ manifold M
410
+
411
+ Tensor fields are considered as elements of a module over the ring
412
+ `C^k(M)` of scalar fields on `M`::
413
+
414
+ sage: t.parent()
415
+ Free module T^(2,0)(M) of type-(2,0) tensors fields on the
416
+ 3-dimensional differentiable manifold M
417
+ sage: t.parent().base_ring()
418
+ Algebra of differentiable scalar fields on the 3-dimensional
419
+ differentiable manifold M
420
+
421
+ The components with respect to the manifold's default frame are
422
+ set or read by means of square brackets::
423
+
424
+ sage: e = M.vector_frame('e') ; M.set_default_frame(e)
425
+ sage: for i in M.irange():
426
+ ....: for j in M.irange():
427
+ ....: t[i,j] = (i+1)**(j+1)
428
+ sage: [[ t[i,j] for j in M.irange()] for i in M.irange()]
429
+ [[1, 1, 1], [2, 4, 8], [3, 9, 27]]
430
+
431
+ A shortcut for the above is using ``[:]``::
432
+
433
+ sage: t[:]
434
+ [ 1 1 1]
435
+ [ 2 4 8]
436
+ [ 3 9 27]
437
+
438
+ The components with respect to another frame are set via the method
439
+ :meth:`set_comp` and read via the method :meth:`comp`; both return an
440
+ instance of :class:`~sage.tensor.modules.comp.Components`::
441
+
442
+ sage: f = M.vector_frame('f') # a new frame defined on M, in addition to e
443
+ sage: t.set_comp(f)[0,0] = -3
444
+ sage: t.comp(f)
445
+ 2-indices components w.r.t. Vector frame (M, (f_0,f_1,f_2))
446
+ sage: t.comp(f)[0,0]
447
+ -3
448
+ sage: t.comp(f)[:] # the full list of components
449
+ [-3 0 0]
450
+ [ 0 0 0]
451
+ [ 0 0 0]
452
+
453
+ To avoid any inconsistency between the various components, the method
454
+ :meth:`set_comp` deletes the components in other frames.
455
+ Accordingly, the components in the frame ``e`` have been deleted::
456
+
457
+ sage: t._components
458
+ {Vector frame (M, (f_0,f_1,f_2)): 2-indices components w.r.t. Vector
459
+ frame (M, (f_0,f_1,f_2))}
460
+
461
+ To keep the other components, one must use the method :meth:`add_comp`::
462
+
463
+ sage: t = M.tensor_field(2, 0, name='T') # let us restart
464
+ sage: t[0,0] = 2 # sets the components in the frame e
465
+
466
+ We now set the components in the frame f with add_comp::
467
+
468
+ sage: t.add_comp(f)[0,0] = -3
469
+
470
+ The components w.r.t. frame e have been kept::
471
+
472
+ sage: t._components # random (dictionary output)
473
+ {Vector frame (M, (e_0,e_1,e_2)): 2-indices components w.r.t. Vector frame (M, (e_0,e_1,e_2)),
474
+ Vector frame (M, (f_0,f_1,f_2)): 2-indices components w.r.t. Vector frame (M, (f_0,f_1,f_2))}
475
+
476
+ The basic properties of a tensor field are::
477
+
478
+ sage: t.domain()
479
+ 3-dimensional differentiable manifold M
480
+ sage: t.tensor_type()
481
+ (2, 0)
482
+
483
+ Symmetries and antisymmetries are declared via the keywords ``sym`` and
484
+ ``antisym``. For instance, a rank-6 covariant tensor that is symmetric
485
+ with respect to its 1st and 3rd arguments and antisymmetric with respect
486
+ to the 2nd, 5th and 6th arguments is set up as follows::
487
+
488
+ sage: a = M.tensor_field(0, 6, name='T', sym=(0,2), antisym=(1,4,5))
489
+ sage: a[0,0,1,0,1,2] = 3
490
+ sage: a[1,0,0,0,1,2] # check of the symmetry
491
+ 3
492
+ sage: a[0,1,1,0,0,2], a[0,1,1,0,2,0] # check of the antisymmetry
493
+ (-3, 3)
494
+
495
+ Multiple symmetries or antisymmetries are allowed; they must then be
496
+ declared as a list. For instance, a rank-4 covariant tensor that is
497
+ antisymmetric with respect to its 1st and 2nd arguments and with
498
+ respect to its 3rd and 4th argument must be declared as::
499
+
500
+ sage: r = M.tensor_field(0, 4, name='T', antisym=[(0,1), (2,3)])
501
+ sage: r[0,1,2,0] = 3
502
+ sage: r[1,0,2,0] # first antisymmetry
503
+ -3
504
+ sage: r[0,1,0,2] # second antisymmetry
505
+ -3
506
+ sage: r[1,0,0,2] # both antisymmetries acting
507
+ 3
508
+
509
+ Tensor fields of the same type can be added and subtracted::
510
+
511
+ sage: a = M.tensor_field(2, 0)
512
+ sage: a[0,0], a[0,1], a[0,2] = (1,2,3)
513
+ sage: b = M.tensor_field(2, 0)
514
+ sage: b[0,0], b[1,1], b[2,2], b[0,2] = (4,5,6,7)
515
+ sage: s = a + 2*b ; s
516
+ Tensor field of type (2,0) on the 3-dimensional differentiable
517
+ manifold M
518
+ sage: a[:], (2*b)[:], s[:]
519
+ (
520
+ [1 2 3] [ 8 0 14] [ 9 2 17]
521
+ [0 0 0] [ 0 10 0] [ 0 10 0]
522
+ [0 0 0], [ 0 0 12], [ 0 0 12]
523
+ )
524
+ sage: s = a - b ; s
525
+ Tensor field of type (2,0) on the 3-dimensional differentiable
526
+ manifold M
527
+ sage: a[:], b[:], s[:]
528
+ (
529
+ [1 2 3] [4 0 7] [-3 2 -4]
530
+ [0 0 0] [0 5 0] [ 0 -5 0]
531
+ [0 0 0], [0 0 6], [ 0 0 -6]
532
+ )
533
+
534
+ Symmetries are preserved by the addition whenever it is possible::
535
+
536
+ sage: a = M.tensor_field(2, 0, sym=(0,1))
537
+ sage: a[0,0], a[0,1], a[0,2] = (1,2,3)
538
+ sage: s = a + b
539
+ sage: a[:], b[:], s[:]
540
+ (
541
+ [1 2 3] [4 0 7] [ 5 2 10]
542
+ [2 0 0] [0 5 0] [ 2 5 0]
543
+ [3 0 0], [0 0 6], [ 3 0 6]
544
+ )
545
+ sage: a.symmetries()
546
+ symmetry: (0, 1); no antisymmetry
547
+ sage: b.symmetries()
548
+ no symmetry; no antisymmetry
549
+ sage: s.symmetries()
550
+ no symmetry; no antisymmetry
551
+
552
+ Let us now make b symmetric::
553
+
554
+ sage: b = M.tensor_field(2, 0, sym=(0,1))
555
+ sage: b[0,0], b[1,1], b[2,2], b[0,2] = (4,5,6,7)
556
+ sage: s = a + b
557
+ sage: a[:], b[:], s[:]
558
+ (
559
+ [1 2 3] [4 0 7] [ 5 2 10]
560
+ [2 0 0] [0 5 0] [ 2 5 0]
561
+ [3 0 0], [7 0 6], [10 0 6]
562
+ )
563
+ sage: s.symmetries() # s is symmetric because both a and b are
564
+ symmetry: (0, 1); no antisymmetry
565
+
566
+ The tensor product is taken with the operator ``*``::
567
+
568
+ sage: c = a*b ; c
569
+ Tensor field of type (4,0) on the 3-dimensional differentiable
570
+ manifold M
571
+ sage: c.symmetries() # since a and b are both symmetric, a*b has two symmetries:
572
+ symmetries: [(0, 1), (2, 3)]; no antisymmetry
573
+
574
+ The tensor product of two fully contravariant tensors is not
575
+ symmetric in general::
576
+
577
+ sage: a*b == b*a
578
+ False
579
+
580
+ The tensor product of a fully contravariant tensor by a fully
581
+ covariant one is symmetric::
582
+
583
+ sage: d = M.diff_form(2) # a fully covariant tensor field
584
+ sage: d[0,1], d[0,2], d[1,2] = (3, 2, 1)
585
+ sage: s = a*d ; s
586
+ Tensor field of type (2,2) on the 3-dimensional differentiable
587
+ manifold M
588
+ sage: s.symmetries()
589
+ symmetry: (0, 1); antisymmetry: (2, 3)
590
+ sage: s1 = d*a ; s1
591
+ Tensor field of type (2,2) on the 3-dimensional differentiable
592
+ manifold M
593
+ sage: s1.symmetries()
594
+ symmetry: (0, 1); antisymmetry: (2, 3)
595
+ sage: d*a == a*d
596
+ True
597
+
598
+ Example of tensor field associated with a non-trivial differentiable
599
+ map `\Phi`: tensor field along a curve in `M`::
600
+
601
+ sage: R = Manifold(1, 'R') # R as a 1-dimensional manifold
602
+ sage: T.<t> = R.chart() # canonical chart on R
603
+ sage: Phi = R.diff_map(M, [cos(t), sin(t), t], name='Phi') ; Phi
604
+ Differentiable map Phi from the 1-dimensional differentiable manifold R
605
+ to the 3-dimensional differentiable manifold M
606
+ sage: h = R.tensor_field(2, 0, name='h', dest_map=Phi) ; h
607
+ Tensor field h of type (2,0) along the 1-dimensional differentiable
608
+ manifold R with values on the 3-dimensional differentiable manifold M
609
+ sage: h.parent()
610
+ Free module T^(2,0)(R,Phi) of type-(2,0) tensors fields along the
611
+ 1-dimensional differentiable manifold R mapped into the 3-dimensional
612
+ differentiable manifold M
613
+ sage: h[0,0], h[0,1], h[2,0] = 1+t, t^2, sin(t)
614
+ sage: h.display()
615
+ h = (t + 1) ∂/∂x⊗∂/∂x + t^2 ∂/∂x⊗∂/∂y + sin(t) ∂/∂z⊗∂/∂x
616
+ """
617
+ def __init__(self, vector_field_module, tensor_type, name=None,
618
+ latex_name=None, sym=None, antisym=None):
619
+ r"""
620
+ Construct a tensor field.
621
+
622
+ TESTS:
623
+
624
+ Construction via ``parent.element_class``, and not via a direct call
625
+ to ``TensorFieldParal``, to fit with the category framework::
626
+
627
+ sage: M = Manifold(2, 'M')
628
+ sage: X.<x,y> = M.chart()
629
+ sage: XM = M.vector_field_module()
630
+ sage: T02 = M.tensor_field_module((0,2))
631
+ sage: t = T02.element_class(XM, (0,2), name='t'); t
632
+ Tensor field t of type (0,2) on the 2-dimensional differentiable
633
+ manifold M
634
+ sage: t[:] = [[1+x^2, x*y], [0, 1+y^2]]
635
+ sage: t.display()
636
+ t = (x^2 + 1) dx⊗dx + x*y dx⊗dy + (y^2 + 1) dy⊗dy
637
+ sage: t.parent()
638
+ Free module T^(0,2)(M) of type-(0,2) tensors fields on the
639
+ 2-dimensional differentiable manifold M
640
+ sage: TestSuite(t).run()
641
+ """
642
+ FreeModuleTensor.__init__(self, vector_field_module, tensor_type,
643
+ name=name, latex_name=latex_name,
644
+ sym=sym, antisym=antisym)
645
+ # TensorField attributes:
646
+ self._vmodule = vector_field_module
647
+ self._domain = vector_field_module._domain
648
+ self._ambient_domain = vector_field_module._ambient_domain
649
+ # NB: the TensorField attribute self._restrictions is considered as a
650
+ # derived quantity in the present case (the primary attribute
651
+ # being self._components, which is initialized by
652
+ # FreeModuleTensor.__init__ ); accordingly self._restrictions is
653
+ # initialized by _init_derived() and cleared by _del_derived().
654
+
655
+ # Initialization of derived quantities:
656
+ self._init_derived()
657
+
658
+ def _repr_(self):
659
+ r"""
660
+ String representation of ``self``.
661
+
662
+ TESTS::
663
+
664
+ sage: M = Manifold(2, 'M')
665
+ sage: X.<x,y> = M.chart() # makes M parallelizable
666
+ sage: t = M.tensor_field(1,1, name='t')
667
+ sage: t._repr_()
668
+ 'Tensor field t of type (1,1) on the 2-dimensional differentiable manifold M'
669
+ sage: repr(t) # indirect doctest
670
+ 'Tensor field t of type (1,1) on the 2-dimensional differentiable manifold M'
671
+ sage: t # indirect doctest
672
+ Tensor field t of type (1,1) on the 2-dimensional differentiable
673
+ manifold M
674
+ """
675
+ return TensorField._repr_(self)
676
+
677
+ def _new_instance(self):
678
+ r"""
679
+ Create an instance of the same class as ``self`` on the same
680
+ vector field module, with the same tensor type and same symmetries.
681
+
682
+ TESTS::
683
+
684
+ sage: M = Manifold(2, 'M')
685
+ sage: X.<x,y> = M.chart() # makes M parallelizable
686
+ sage: t = M.tensor_field(1,1, name='t')
687
+ sage: t._new_instance()
688
+ Tensor field of type (1,1) on the 2-dimensional differentiable
689
+ manifold M
690
+ sage: type(t._new_instance()) is type(t)
691
+ True
692
+ """
693
+ return type(self)(self._fmodule, self._tensor_type, sym=self._sym,
694
+ antisym=self._antisym)
695
+
696
+ def _init_derived(self):
697
+ r"""
698
+ Initialize the derived quantities.
699
+
700
+ TESTS::
701
+
702
+ sage: M = Manifold(2, 'M')
703
+ sage: X.<x,y> = M.chart() # makes M parallelizable
704
+ sage: t = M.tensor_field(1,1, name='t')
705
+ sage: t._init_derived()
706
+ """
707
+ FreeModuleTensor._init_derived(self)
708
+ TensorField._init_derived(self)
709
+ self._restrictions = {} # dict. of restrictions of self on subdomains
710
+ # of self._domain, with the subdomains as keys
711
+ self._extensions_graph = {self._domain: self}
712
+ self._restrictions_graph = {self._domain: self}
713
+
714
+ def _del_derived(self, del_restrictions: bool = True):
715
+ r"""
716
+ Delete the derived quantities.
717
+
718
+ INPUT:
719
+
720
+ - ``del_restrictions`` -- boolean (default: ``True``); determines whether the
721
+ restrictions of ``self`` to subdomains are deleted
722
+
723
+ TESTS::
724
+
725
+ sage: M = Manifold(2, 'M')
726
+ sage: X.<x,y> = M.chart() # makes M parallelizable
727
+ sage: t = M.tensor_field(1,1, name='t')
728
+ sage: t._del_derived()
729
+ """
730
+ FreeModuleTensor._del_derived(self)
731
+ TensorField._del_derived(self)
732
+ if del_restrictions:
733
+ self._del_restrictions()
734
+
735
+ def _preparse_display(self, basis=None, format_spec=None):
736
+ r"""
737
+ Helper function, to be used by FreeModuleTensor.display.
738
+
739
+ TESTS::
740
+
741
+ sage: M = Manifold(2, 'M')
742
+ sage: X.<x,y> = M.chart()
743
+ sage: t = M.tensor_field(1, 1)
744
+ sage: t._preparse_display()
745
+ (Coordinate frame (M, (∂/∂x,∂/∂y)), None)
746
+ sage: t._preparse_display(X.frame())
747
+ (Coordinate frame (M, (∂/∂x,∂/∂y)), None)
748
+ sage: t._preparse_display(X.frame(), X)
749
+ (Coordinate frame (M, (∂/∂x,∂/∂y)), Chart (M, (x, y)))
750
+ sage: t._preparse_display(X) # passing a chart instead of a frame
751
+ (Coordinate frame (M, (∂/∂x,∂/∂y)), Chart (M, (x, y)))
752
+ """
753
+ if basis is None:
754
+ basis = self._fmodule._def_basis
755
+ elif isinstance(basis, Chart):
756
+ # a coordinate chart has been passed instead of a vector frame;
757
+ # the frame is then assumed to be the coordinate frame
758
+ # associated to the chart:
759
+ if format_spec is None:
760
+ format_spec = basis
761
+ basis = basis.frame()
762
+ return (basis, format_spec)
763
+
764
+ def _set_comp_unsafe(self, basis=None):
765
+ r"""
766
+ Return the components of the tensor field in a given vector frame
767
+ for assignment. This private method invokes no security check. Use
768
+ this method at your own risk.
769
+
770
+ The components with respect to other frames on the same domain are
771
+ deleted, in order to avoid any inconsistency. To keep them, use the
772
+ method :meth:`_add_comp_unsafe` instead.
773
+
774
+ INPUT:
775
+
776
+ - ``basis`` -- (default: ``None``) vector frame in which the
777
+ components are defined; if none is provided, the components are
778
+ assumed to refer to the tensor field domain's default frame
779
+
780
+ OUTPUT:
781
+
782
+ - components in the given frame, as an instance of the
783
+ class :class:`~sage.tensor.modules.comp.Components`; if such
784
+ components did not exist previously, they are created
785
+
786
+ EXAMPLES::
787
+
788
+ sage: M = Manifold(2, 'M')
789
+ sage: X.<x,y> = M.chart()
790
+ sage: e_xy = X.frame()
791
+ sage: t = M.tensor_field(1,1, name='t')
792
+ sage: t._set_comp_unsafe(e_xy)
793
+ 2-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
794
+ sage: t._set_comp_unsafe(e_xy)[1,0] = 2
795
+ sage: t.display(e_xy)
796
+ t = 2 ∂/∂y⊗dx
797
+
798
+ Setting components in a new frame (``e``)::
799
+
800
+ sage: e = M.vector_frame('e')
801
+ sage: t._set_comp_unsafe(e)
802
+ 2-indices components w.r.t. Vector frame (M, (e_0,e_1))
803
+ sage: t._set_comp_unsafe(e)[0,1] = x
804
+ sage: t.display(e)
805
+ t = x e_0⊗e^1
806
+
807
+ The components with respect to the frame ``e_xy`` have be erased::
808
+
809
+ sage: t.display(e_xy)
810
+ Traceback (most recent call last):
811
+ ...
812
+ ValueError: no basis could be found for computing the components
813
+ in the Coordinate frame (M, (∂/∂x,∂/∂y))
814
+
815
+ Setting components in a frame defined on a subdomain deletes
816
+ previously defined components as well::
817
+
818
+ sage: U = M.open_subset('U', coord_def={X: x>0})
819
+ sage: f = U.vector_frame('f')
820
+ sage: t._set_comp_unsafe(f)
821
+ 2-indices components w.r.t. Vector frame (U, (f_0,f_1))
822
+ sage: t._set_comp_unsafe(f)[0,1] = 1+y
823
+ sage: t.display(f)
824
+ t = (y + 1) f_0⊗f^1
825
+ sage: t.display(e)
826
+ Traceback (most recent call last):
827
+ ...
828
+ ValueError: no basis could be found for computing the components
829
+ in the Vector frame (M, (e_0,e_1))
830
+ """
831
+ if basis is None:
832
+ basis = self._fmodule._def_basis
833
+
834
+ if basis._domain == self._domain:
835
+ # Setting components on the tensor field domain with an unsafe
836
+ # method:
837
+ return FreeModuleTensor._set_comp_unsafe(self, basis=basis)
838
+
839
+ # Setting components on a subdomain:
840
+ #
841
+ # Creating or saving the restriction to the subdomain:
842
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
843
+ # Deleting all the components on self._domain and the derived
844
+ # quantities:
845
+ self._components.clear()
846
+ self._del_derived()
847
+ # Restoring the restriction to the subdomain (which has been
848
+ # deleted by _del_derived):
849
+ self._restrictions[basis._domain] = rst
850
+ # The _set_comp_unsafe operation is performed on the subdomain:
851
+ return rst._set_comp_unsafe(basis)
852
+
853
+ def set_comp(self, basis=None):
854
+ r"""
855
+ Return the components of the tensor field in a given vector frame
856
+ for assignment.
857
+
858
+ The components with respect to other frames on the same domain are
859
+ deleted, in order to avoid any inconsistency. To keep them, use the
860
+ method :meth:`add_comp` instead.
861
+
862
+ INPUT:
863
+
864
+ - ``basis`` -- (default: ``None``) vector frame in which the
865
+ components are defined; if none is provided, the components are
866
+ assumed to refer to the tensor field domain's default frame
867
+
868
+ OUTPUT:
869
+
870
+ - components in the given frame, as an instance of the
871
+ class :class:`~sage.tensor.modules.comp.Components`; if such
872
+ components did not exist previously, they are created
873
+
874
+ EXAMPLES::
875
+
876
+ sage: M = Manifold(2, 'M')
877
+ sage: X.<x,y> = M.chart()
878
+ sage: e_xy = X.frame()
879
+ sage: t = M.tensor_field(1,1, name='t')
880
+ sage: t.set_comp(e_xy)
881
+ 2-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
882
+ sage: t.set_comp(e_xy)[1,0] = 2
883
+ sage: t.display(e_xy)
884
+ t = 2 ∂/∂y⊗dx
885
+
886
+ Setting components in a new frame (``e``)::
887
+
888
+ sage: e = M.vector_frame('e')
889
+ sage: t.set_comp(e)
890
+ 2-indices components w.r.t. Vector frame (M, (e_0,e_1))
891
+ sage: t.set_comp(e)[0,1] = x
892
+ sage: t.display(e)
893
+ t = x e_0⊗e^1
894
+
895
+ The components with respect to the frame ``e_xy`` have be erased::
896
+
897
+ sage: t.display(e_xy)
898
+ Traceback (most recent call last):
899
+ ...
900
+ ValueError: no basis could be found for computing the components
901
+ in the Coordinate frame (M, (∂/∂x,∂/∂y))
902
+
903
+ Setting components in a frame defined on a subdomain deletes
904
+ previously defined components as well::
905
+
906
+ sage: U = M.open_subset('U', coord_def={X: x>0})
907
+ sage: f = U.vector_frame('f')
908
+ sage: t.set_comp(f)
909
+ 2-indices components w.r.t. Vector frame (U, (f_0,f_1))
910
+ sage: t.set_comp(f)[0,1] = 1+y
911
+ sage: t.display(f)
912
+ t = (y + 1) f_0⊗f^1
913
+ sage: t.display(e)
914
+ Traceback (most recent call last):
915
+ ...
916
+ ValueError: no basis could be found for computing the components
917
+ in the Vector frame (M, (e_0,e_1))
918
+ """
919
+ if self.is_immutable():
920
+ raise ValueError("the components of an immutable element "
921
+ "cannot be changed")
922
+ if basis is None:
923
+ basis = self._fmodule._def_basis
924
+
925
+ self._is_zero = False # a priori
926
+
927
+ if basis._domain == self._domain:
928
+ # Setting components on the tensor field domain:
929
+ return FreeModuleTensor.set_comp(self, basis=basis)
930
+
931
+ # Setting components on a subdomain:
932
+ #
933
+ # Creating or saving the restriction to the subdomain:
934
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
935
+ # Deleting all the components on self._domain and the derived
936
+ # quantities:
937
+ self._components.clear()
938
+ self._del_derived()
939
+ # Restoring the restriction to the subdomain (which has been
940
+ # deleted by _del_derived):
941
+ self._restrictions[basis._domain] = rst
942
+ # The set_comp operation is performed on the subdomain:
943
+ return rst.set_comp(basis=basis)
944
+
945
+ def _add_comp_unsafe(self, basis=None):
946
+ r"""
947
+ Return the components of the tensor field in a given vector frame
948
+ for assignment. This private method invokes no security check. Use
949
+ this method at your own risk.
950
+
951
+ The components with respect to other frames on the same domain are
952
+ kept. To delete them, use the method :meth:`_set_comp_unsafe` instead.
953
+
954
+ INPUT:
955
+
956
+ - ``basis`` -- (default: ``None``) vector frame in which the
957
+ components are defined; if none is provided, the components are
958
+ assumed to refer to the tensor field domain's default frame
959
+
960
+ OUTPUT:
961
+
962
+ - components in the given frame, as an instance of the
963
+ class :class:`~sage.tensor.modules.comp.Components`; if such
964
+ components did not exist previously, they are created
965
+
966
+ TESTS::
967
+
968
+ sage: M = Manifold(2, 'M')
969
+ sage: X.<x,y> = M.chart()
970
+ sage: e_xy = X.frame()
971
+ sage: t = M.tensor_field(1,1, name='t')
972
+ sage: t._add_comp_unsafe(e_xy)
973
+ 2-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
974
+ sage: t._add_comp_unsafe(e_xy)[1,0] = 2
975
+ sage: t.display(e_xy)
976
+ t = 2 ∂/∂y⊗dx
977
+
978
+ Adding components with respect to a new frame (``e``)::
979
+
980
+ sage: e = M.vector_frame('e')
981
+ sage: t._add_comp_unsafe(e)
982
+ 2-indices components w.r.t. Vector frame (M, (e_0,e_1))
983
+ sage: t._add_comp_unsafe(e)[0,1] = x
984
+ sage: t.display(e)
985
+ t = x e_0⊗e^1
986
+
987
+ The components with respect to the frame ``e_xy`` are kept::
988
+
989
+ sage: t.display(e_xy)
990
+ t = 2 ∂/∂y⊗dx
991
+
992
+ Adding components in a frame defined on a subdomain::
993
+
994
+ sage: U = M.open_subset('U', coord_def={X: x>0})
995
+ sage: f = U.vector_frame('f')
996
+ sage: t._add_comp_unsafe(f)
997
+ 2-indices components w.r.t. Vector frame (U, (f_0,f_1))
998
+ sage: t._add_comp_unsafe(f)[0,1] = 1+y
999
+ sage: t.display(f)
1000
+ t = (y + 1) f_0⊗f^1
1001
+
1002
+ The components previously defined are kept::
1003
+
1004
+ sage: t.display(e_xy)
1005
+ t = 2 ∂/∂y⊗dx
1006
+ sage: t.display(e)
1007
+ t = x e_0⊗e^1
1008
+ """
1009
+ if basis is None:
1010
+ basis = self._fmodule._def_basis
1011
+
1012
+ if basis._domain == self._domain:
1013
+ # Adding components on the tensor field domain:
1014
+ # We perform a backup of the restrictions, since
1015
+ # they are deleted by FreeModuleTensor._add_comp_unsafe (which
1016
+ # invokes del_derived()), and restore them afterwards
1017
+ restrictions_save = self._restrictions.copy()
1018
+ comp = FreeModuleTensor._add_comp_unsafe(self, basis=basis)
1019
+ self._restrictions = restrictions_save
1020
+ return comp
1021
+
1022
+ # Adding components on a subdomain:
1023
+ #
1024
+ # Creating or saving the restriction to the subdomain:
1025
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1026
+ # Deleting the derived quantities except for the restrictions to
1027
+ # subdomains:
1028
+ self._del_derived(del_restrictions=False)
1029
+ # The _add_comp_unsafe operation is performed on the subdomain:
1030
+ return rst._add_comp_unsafe(basis)
1031
+
1032
+ def add_comp(self, basis=None):
1033
+ r"""
1034
+ Return the components of the tensor field in a given vector frame
1035
+ for assignment.
1036
+
1037
+ The components with respect to other frames on the same domain are
1038
+ kept. To delete them, use the method :meth:`set_comp` instead.
1039
+
1040
+ INPUT:
1041
+
1042
+ - ``basis`` -- (default: ``None``) vector frame in which the
1043
+ components are defined; if none is provided, the components are
1044
+ assumed to refer to the tensor field domain's default frame
1045
+
1046
+ OUTPUT:
1047
+
1048
+ - components in the given frame, as an instance of the
1049
+ class :class:`~sage.tensor.modules.comp.Components`; if such
1050
+ components did not exist previously, they are created
1051
+
1052
+ EXAMPLES::
1053
+
1054
+ sage: M = Manifold(2, 'M')
1055
+ sage: X.<x,y> = M.chart()
1056
+ sage: e_xy = X.frame()
1057
+ sage: t = M.tensor_field(1,1, name='t')
1058
+ sage: t.add_comp(e_xy)
1059
+ 2-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
1060
+ sage: t.add_comp(e_xy)[1,0] = 2
1061
+ sage: t.display(e_xy)
1062
+ t = 2 ∂/∂y⊗dx
1063
+
1064
+ Adding components with respect to a new frame (``e``)::
1065
+
1066
+ sage: e = M.vector_frame('e')
1067
+ sage: t.add_comp(e)
1068
+ 2-indices components w.r.t. Vector frame (M, (e_0,e_1))
1069
+ sage: t.add_comp(e)[0,1] = x
1070
+ sage: t.display(e)
1071
+ t = x e_0⊗e^1
1072
+
1073
+ The components with respect to the frame ``e_xy`` are kept::
1074
+
1075
+ sage: t.display(e_xy)
1076
+ t = 2 ∂/∂y⊗dx
1077
+
1078
+ Adding components in a frame defined on a subdomain::
1079
+
1080
+ sage: U = M.open_subset('U', coord_def={X: x>0})
1081
+ sage: f = U.vector_frame('f')
1082
+ sage: t.add_comp(f)
1083
+ 2-indices components w.r.t. Vector frame (U, (f_0,f_1))
1084
+ sage: t.add_comp(f)[0,1] = 1+y
1085
+ sage: t.display(f)
1086
+ t = (y + 1) f_0⊗f^1
1087
+
1088
+ The components previously defined are kept::
1089
+
1090
+ sage: t.display(e_xy)
1091
+ t = 2 ∂/∂y⊗dx
1092
+ sage: t.display(e)
1093
+ t = x e_0⊗e^1
1094
+ """
1095
+ if self.is_immutable():
1096
+ raise ValueError("the components of an immutable element "
1097
+ "cannot be changed")
1098
+ if basis is None:
1099
+ basis = self._fmodule._def_basis
1100
+
1101
+ self._is_zero = False # a priori
1102
+
1103
+ if basis._domain == self._domain:
1104
+ # Adding components on the tensor field domain:
1105
+ # We perform a backup of the restrictions, since
1106
+ # they are deleted by FreeModuleTensor.add_comp (which
1107
+ # invokes del_derived()), and restore them afterwards
1108
+ restrictions_save = self._restrictions.copy()
1109
+ comp = FreeModuleTensor.add_comp(self, basis=basis)
1110
+ self._restrictions = restrictions_save
1111
+ return comp
1112
+
1113
+ # Adding components on a subdomain:
1114
+ #
1115
+ # Creating or saving the restriction to the subdomain:
1116
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1117
+ # Deleting the derived quantities except for the restrictions to
1118
+ # subdomains:
1119
+ self._del_derived(del_restrictions=False)
1120
+ # The add_comp operation is performed on the subdomain:
1121
+ return rst.add_comp(basis=basis)
1122
+
1123
+ def comp(self, basis=None, from_basis=None) -> Components:
1124
+ r"""
1125
+ Return the components in a given vector frame.
1126
+
1127
+ If the components are not known already, they are computed by the
1128
+ tensor change-of-basis formula from components in another vector frame.
1129
+
1130
+ INPUT:
1131
+
1132
+ - ``basis`` -- (default: ``None``) vector frame in which the components
1133
+ are required; if none is provided, the components are assumed to
1134
+ refer to the tensor field domain's default frame
1135
+ - ``from_basis`` -- (default: ``None``) vector frame from which the
1136
+ required components are computed, via the tensor change-of-basis
1137
+ formula, if they are not known already in the basis ``basis``
1138
+
1139
+ OUTPUT:
1140
+
1141
+ - components in the vector frame ``basis``, as an instance of the
1142
+ class :class:`~sage.tensor.modules.comp.Components`
1143
+
1144
+ EXAMPLES::
1145
+
1146
+ sage: M = Manifold(2, 'M', start_index=1)
1147
+ sage: X.<x,y> = M.chart()
1148
+ sage: t = M.tensor_field(1,2, name='t')
1149
+ sage: t[1,2,1] = x*y
1150
+ sage: t.comp(X.frame())
1151
+ 3-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
1152
+ sage: t.comp() # the default frame is X.frame()
1153
+ 3-indices components w.r.t. Coordinate frame (M, (∂/∂x,∂/∂y))
1154
+ sage: t.comp()[:]
1155
+ [[[0, 0], [x*y, 0]], [[0, 0], [0, 0]]]
1156
+ sage: e = M.vector_frame('e')
1157
+ sage: t[e, 2,1,1] = x-3
1158
+ sage: t.comp(e)
1159
+ 3-indices components w.r.t. Vector frame (M, (e_1,e_2))
1160
+ sage: t.comp(e)[:]
1161
+ [[[0, 0], [0, 0]], [[x - 3, 0], [0, 0]]]
1162
+ """
1163
+ if basis is None:
1164
+ basis = self._fmodule._def_basis
1165
+
1166
+ if basis._domain == self._domain:
1167
+ # components on the tensor field domain:
1168
+ return FreeModuleTensor.comp(self, basis=basis,
1169
+ from_basis=from_basis)
1170
+
1171
+ # components on a subdomain:
1172
+ rst = self.restrict(basis._domain, dest_map=basis._dest_map)
1173
+ return rst.comp(basis=basis, from_basis=from_basis)
1174
+
1175
+ def _common_coord_frame(self, other):
1176
+ r"""
1177
+ Find a common coordinate frame for the components of ``self``
1178
+ and ``other``.
1179
+
1180
+ In case of multiple common bases, the domain's default coordinate
1181
+ basis is privileged.
1182
+ If the current components of ``self`` and ``other`` are all relative to
1183
+ different frames, a common frame is searched by performing a component
1184
+ transformation, via the transformations listed in
1185
+ ``self._domain._frame_changes``, still privileging transformations to
1186
+ the domain's default frame.
1187
+
1188
+ INPUT:
1189
+
1190
+ - ``other`` -- a tensor field (instance of :class:`TensorFieldParal`)
1191
+
1192
+ OUTPUT:
1193
+
1194
+ - common coordinate frame; if no common basis is found, ``None``
1195
+ is returned
1196
+
1197
+ EXAMPLES::
1198
+
1199
+ sage: M = Manifold(2, 'M')
1200
+ sage: X.<x,y> = M.chart()
1201
+ sage: a = M.tensor_field(1,2, name='a')
1202
+ sage: a[0,1,0] = 2
1203
+ sage: b = M.vector_field(-y, x, name='b')
1204
+ sage: a._common_coord_frame(b)
1205
+ Coordinate frame (M, (∂/∂x,∂/∂y))
1206
+
1207
+ Vector field defined on a new chart::
1208
+
1209
+ sage: Y.<u,v> = M.chart()
1210
+ sage: c = M.vector_field(1+u, u*v, frame=Y.frame(), chart=Y,
1211
+ ....: name='c')
1212
+ sage: c.display(Y.frame(), Y)
1213
+ c = (u + 1) ∂/∂u + u*v ∂/∂v
1214
+
1215
+ There is no common coordinate frame::
1216
+
1217
+ sage: a._common_coord_frame(c)
1218
+
1219
+ Connecting the two coordinate charts enables to find a common frame::
1220
+
1221
+ sage: X_to_Y = X.transition_map(Y, [x+y, x-y])
1222
+ sage: Y_to_X = X_to_Y.inverse()
1223
+ sage: a._common_coord_frame(c)
1224
+ Coordinate frame (M, (∂/∂x,∂/∂y))
1225
+
1226
+ Indeed, the components of ``c`` with respect to the
1227
+ frame ``(M, (∂/∂x,∂/∂y))`` have been computed via the
1228
+ change-of-coordinate formulas::
1229
+
1230
+ sage: c.display(a._common_coord_frame(c))
1231
+ c = (1/2*x^2 - 1/2*y^2 + 1/2*x + 1/2*y + 1/2) ∂/∂x
1232
+ + (-1/2*x^2 + 1/2*y^2 + 1/2*x + 1/2*y + 1/2) ∂/∂y
1233
+ """
1234
+ from sage.manifolds.differentiable.vectorframe import CoordFrame
1235
+ # Compatibility checks:
1236
+ if not isinstance(other, TensorFieldParal):
1237
+ raise TypeError("the argument must be of type TensorFieldParal")
1238
+ dom = self._domain
1239
+ def_frame = dom._def_frame
1240
+ #
1241
+ # 1/ Search for a common frame among the existing components, i.e.
1242
+ # without performing any component transformation.
1243
+ # -------------------------------------------------------------
1244
+ # 1a/ Direct search
1245
+ if (def_frame in self._components
1246
+ and def_frame in other._components
1247
+ and isinstance(dom._def_frame, CoordFrame)):
1248
+ return def_frame # the domain's default frame is privileged
1249
+ for frame1 in self._components:
1250
+ if frame1 in other._components and isinstance(frame1, CoordFrame):
1251
+ return frame1
1252
+ # 1b/ Search involving subframes
1253
+ for frame1 in self._components:
1254
+ if not isinstance(frame1, CoordFrame):
1255
+ continue
1256
+ for frame2 in other._components:
1257
+ if not isinstance(frame2, CoordFrame):
1258
+ continue
1259
+ if frame2 in frame1._subframes:
1260
+ self.comp(frame2)
1261
+ return frame2
1262
+ if frame1 in frame2._subframes:
1263
+ other.comp(frame1)
1264
+ return frame1
1265
+ #
1266
+ # 2/ Search for a common frame via one component transformation
1267
+ # ----------------------------------------------------------
1268
+ # If this point is reached, it is indeed necessary to perform at least
1269
+ # one component transformation to get a common frame
1270
+ if isinstance(dom._def_frame, CoordFrame):
1271
+ if def_frame in self._components:
1272
+ for oframe in other._components:
1273
+ if (oframe, def_frame) in dom._frame_changes:
1274
+ other.comp(def_frame, from_basis=oframe)
1275
+ return def_frame
1276
+ if def_frame in other._components:
1277
+ for sframe in self._components:
1278
+ if (sframe, def_frame) in dom._frame_changes:
1279
+ self.comp(def_frame, from_basis=sframe)
1280
+ return def_frame
1281
+ # If this point is reached, then def_frame cannot be a common frame
1282
+ # via a single component transformation
1283
+ for sframe in self._components:
1284
+ if not isinstance(sframe, CoordFrame):
1285
+ continue
1286
+ for oframe in other._components:
1287
+ if not isinstance(oframe, CoordFrame):
1288
+ continue
1289
+ if (oframe, sframe) in dom._frame_changes:
1290
+ other.comp(sframe, from_basis=oframe)
1291
+ return sframe
1292
+ if (sframe, oframe) in dom._frame_changes:
1293
+ self.comp(oframe, from_basis=sframe)
1294
+ return oframe
1295
+ #
1296
+ # 3/ Search for a common frame via two component transformations
1297
+ # -----------------------------------------------------------
1298
+ # If this point is reached, it is indeed necessary to perform at least
1299
+ # two component transformations to get a common frame
1300
+ for sframe in self._components:
1301
+ for oframe in other._components:
1302
+ if ((sframe, def_frame) in dom._frame_changes
1303
+ and (oframe, def_frame) in dom._frame_changes
1304
+ and isinstance(def_frame, CoordFrame)):
1305
+ self.comp(def_frame, from_basis=sframe)
1306
+ other.comp(def_frame, from_basis=oframe)
1307
+ return def_frame
1308
+ for frame in dom._frames:
1309
+ if ((sframe, frame) in dom._frame_changes
1310
+ and (oframe, frame) in dom._frame_changes
1311
+ and isinstance(frame, CoordFrame)):
1312
+ self.comp(frame, from_basis=sframe)
1313
+ other.comp(frame, from_basis=oframe)
1314
+ return frame
1315
+ #
1316
+ # If this point is reached, no common frame could be found, even at
1317
+ # the price of component transformations:
1318
+ return None
1319
+
1320
+ def lie_derivative(self, vector):
1321
+ r"""
1322
+ Compute the Lie derivative with respect to a vector field.
1323
+
1324
+ INPUT:
1325
+
1326
+ - ``vector`` -- vector field with respect to which the
1327
+ Lie derivative is to be taken
1328
+
1329
+ OUTPUT:
1330
+
1331
+ - the tensor field that is the Lie derivative of ``self``
1332
+ with respect to ``vector``
1333
+
1334
+ EXAMPLES:
1335
+
1336
+ Lie derivative of a vector::
1337
+
1338
+ sage: M = Manifold(2, 'M', start_index=1)
1339
+ sage: c_xy.<x,y> = M.chart()
1340
+ sage: v = M.vector_field(-y, x, name='v')
1341
+ sage: w = M.vector_field(2*x+y, x*y)
1342
+ sage: w.lie_derivative(v)
1343
+ Vector field on the 2-dimensional differentiable manifold M
1344
+ sage: w.lie_derivative(v).display()
1345
+ ((x - 2)*y + x) ∂/∂x + (x^2 - y^2 - 2*x - y) ∂/∂y
1346
+
1347
+ The result is cached::
1348
+
1349
+ sage: w.lie_derivative(v) is w.lie_derivative(v)
1350
+ True
1351
+
1352
+ An alias is ``lie_der``::
1353
+
1354
+ sage: w.lie_der(v) is w.lie_derivative(v)
1355
+ True
1356
+
1357
+ The Lie derivative is antisymmetric::
1358
+
1359
+ sage: w.lie_der(v) == -v.lie_der(w)
1360
+ True
1361
+
1362
+ For vectors, it coincides with the commutator::
1363
+
1364
+ sage: f = M.scalar_field(x^3 + x*y^2)
1365
+ sage: w.lie_der(v)(f).display()
1366
+ M → ℝ
1367
+ (x, y) ↦ -(x + 2)*y^3 + 3*x^3 - x*y^2 + 5*(x^3 - 2*x^2)*y
1368
+ sage: w.lie_der(v)(f) == v(w(f)) - w(v(f)) # rhs = commutator [v,w] acting on f
1369
+ True
1370
+
1371
+ Lie derivative of a 1-form::
1372
+
1373
+ sage: om = M.one_form(y^2*sin(x), x^3*cos(y))
1374
+ sage: om.lie_der(v)
1375
+ 1-form on the 2-dimensional differentiable manifold M
1376
+ sage: om.lie_der(v).display()
1377
+ (-y^3*cos(x) + x^3*cos(y) + 2*x*y*sin(x)) dx
1378
+ + (-x^4*sin(y) - 3*x^2*y*cos(y) - y^2*sin(x)) dy
1379
+
1380
+ Parallel computation::
1381
+
1382
+ sage: Parallelism().set('tensor', nproc=2)
1383
+ sage: om.lie_der(v)
1384
+ 1-form on the 2-dimensional differentiable manifold M
1385
+ sage: om.lie_der(v).display()
1386
+ (-y^3*cos(x) + x^3*cos(y) + 2*x*y*sin(x)) dx
1387
+ + (-x^4*sin(y) - 3*x^2*y*cos(y) - y^2*sin(x)) dy
1388
+ sage: Parallelism().set('tensor', nproc=1) # switch off parallelization
1389
+
1390
+ Check of Cartan identity::
1391
+
1392
+ sage: om.lie_der(v) == (v.contract(0, om.exterior_derivative(), 0)
1393
+ ....: + om(v).exterior_derivative())
1394
+ True
1395
+ """
1396
+ if vector._tensor_type != (1,0):
1397
+ raise TypeError("the argument must be a vector field")
1398
+
1399
+ # The Lie derivative is stored in the dictionary
1400
+ # ``_lie_derivatives``, so that there is no need to
1401
+ # recompute it at the next call if neither ``self``
1402
+ # nor ``vector`` have been modified meanwhile.
1403
+
1404
+ if id(vector) not in self._lie_derivatives:
1405
+ # A new computation must be performed
1406
+ #
1407
+ # 1/ Search for a common coordinate frame:
1408
+ coord_frame = self._common_coord_frame(vector)
1409
+ if coord_frame is None:
1410
+ raise ValueError("no common coordinate frame found")
1411
+ chart = coord_frame._chart
1412
+
1413
+ vf_module = vector._fmodule
1414
+ resc = self._new_comp(coord_frame)
1415
+
1416
+ # get n processes
1417
+ nproc = Parallelism().get('tensor')
1418
+ if nproc != 1:
1419
+
1420
+ # Parallel computation
1421
+ lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)]
1422
+ ind_list = list(resc.non_redundant_index_generator())
1423
+ ind_step = max(1, len(ind_list) // nproc)
1424
+ local_list = lol(ind_list, ind_step)
1425
+ # list of input parameters:
1426
+ listParalInput = [(self, vector, coord_frame, chart, ind_part) for ind_part in local_list]
1427
+
1428
+ @parallel(p_iter='multiprocessing',ncpus=nproc)
1429
+ def paral_lie_deriv(a, b , coord_frame, chart_cp, local_list_ind):
1430
+ #
1431
+ # 2/ Component computation:
1432
+ tc = a._components[coord_frame]
1433
+ vc = b._components[coord_frame]
1434
+ # the result has the same tensor type and same symmetries as a:
1435
+ n_con = a._tensor_type[0]
1436
+ vf_module = b._fmodule
1437
+
1438
+ local_res = []
1439
+ for ind in local_list_ind:
1440
+ rsum = 0
1441
+ for i in vf_module.irange():
1442
+ rsum += vc[[i]].coord_function(chart_cp) * \
1443
+ tc[[ind]].coord_function(chart_cp).diff(i)
1444
+ # loop on contravariant indices:
1445
+ for k in range(n_con):
1446
+ for i in vf_module.irange():
1447
+ indk = list(ind)
1448
+ indk[k] = i
1449
+ rsum -= tc[[indk]].coord_function(chart_cp) * \
1450
+ vc[[ind[k]]].coord_function(chart_cp).diff(i)
1451
+ # loop on covariant indices:
1452
+ for k in range(n_con, a._tensor_rank):
1453
+ for i in vf_module.irange():
1454
+ indk = list(ind)
1455
+ indk[k] = i
1456
+ rsum += tc[[indk]].coord_function(chart_cp) * \
1457
+ vc[[i]].coord_function(chart_cp).diff(ind[k])
1458
+
1459
+ local_res.append([ind, rsum.scalar_field()])
1460
+
1461
+ return local_res
1462
+
1463
+ # call to parallel lie derivative
1464
+ for ii,val in paral_lie_deriv(listParalInput):
1465
+ for jj in val:
1466
+ resc[[jj[0]]] = jj[1]
1467
+
1468
+ else :
1469
+ # Sequential computation
1470
+ #
1471
+ # 2/ Component computation:
1472
+ tc = self._components[coord_frame]
1473
+ vc = vector._components[coord_frame]
1474
+ # the result has the same tensor type and same symmetries as self:
1475
+ n_con = self._tensor_type[0]
1476
+
1477
+ for ind in resc.non_redundant_index_generator():
1478
+ rsum = 0
1479
+ for i in vf_module.irange():
1480
+ rsum += vc[[i]].coord_function(chart) * \
1481
+ tc[[ind]].coord_function(chart).diff(i)
1482
+ # loop on contravariant indices:
1483
+ for k in range(n_con):
1484
+ for i in vf_module.irange():
1485
+ indk = list(ind)
1486
+ indk[k] = i
1487
+ rsum -= tc[[indk]].coord_function(chart) * \
1488
+ vc[[ind[k]]].coord_function(chart).diff(i)
1489
+ # loop on covariant indices:
1490
+ for k in range(n_con, self._tensor_rank):
1491
+ for i in vf_module.irange():
1492
+ indk = list(ind)
1493
+ indk[k] = i
1494
+ rsum += tc[[indk]].coord_function(chart) * \
1495
+ vc[[i]].coord_function(chart).diff(ind[k])
1496
+ resc[[ind]] = rsum.scalar_field()
1497
+
1498
+ #
1499
+ # 3/ Final result (the tensor)
1500
+ res = vf_module.tensor_from_comp(self._tensor_type, resc)
1501
+ self._lie_derivatives[id(vector)] = (vector, res)
1502
+ vector._lie_der_along_self[id(self)] = self
1503
+ return self._lie_derivatives[id(vector)][1]
1504
+
1505
+ lie_der = lie_derivative
1506
+
1507
+ def restrict(self, subdomain: DifferentiableManifold, dest_map: Optional[DiffMap] = None):
1508
+ r"""
1509
+ Return the restriction of ``self`` to some subdomain.
1510
+
1511
+ If the restriction has not been defined yet, it is constructed here.
1512
+
1513
+ INPUT:
1514
+
1515
+ - ``subdomain`` --
1516
+ :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`;
1517
+ open subset `U` of the tensor field domain `S`
1518
+ - ``dest_map`` --
1519
+ :class:`~sage.manifolds.differentiable.diff_map.DiffMap`
1520
+ (default: ``None``); destination map
1521
+ `\Psi:\ U \rightarrow V`, where `V` is an open subset
1522
+ of the manifold `M` where the tensor field takes it values;
1523
+ if ``None``, the restriction of `\Phi` to `U` is used, `\Phi`
1524
+ being the differentiable map `S \rightarrow M` associated
1525
+ with the tensor field
1526
+
1527
+ OUTPUT: instance of :class:`TensorFieldParal` representing the restriction
1528
+
1529
+ EXAMPLES:
1530
+
1531
+ Restriction of a vector field defined on `\RR^2` to a disk::
1532
+
1533
+ sage: M = Manifold(2, 'R^2')
1534
+ sage: c_cart.<x,y> = M.chart() # Cartesian coordinates on R^2
1535
+ sage: v = M.vector_field(x+y, -1+x^2, name='v')
1536
+ sage: D = M.open_subset('D') # the unit open disc
1537
+ sage: c_cart_D = c_cart.restrict(D, x^2+y^2<1)
1538
+ sage: v_D = v.restrict(D) ; v_D
1539
+ Vector field v on the Open subset D of the 2-dimensional
1540
+ differentiable manifold R^2
1541
+ sage: v_D.display()
1542
+ v = (x + y) ∂/∂x + (x^2 - 1) ∂/∂y
1543
+
1544
+ The symbolic expressions of the components with respect to
1545
+ Cartesian coordinates are equal::
1546
+
1547
+ sage: bool( v_D[1].expr() == v[1].expr() )
1548
+ True
1549
+
1550
+ but neither the chart functions representing the components (they are
1551
+ defined on different charts)::
1552
+
1553
+ sage: v_D[1] == v[1]
1554
+ False
1555
+
1556
+ nor the scalar fields representing the components (they are
1557
+ defined on different open subsets)::
1558
+
1559
+ sage: v_D[[1]] == v[[1]]
1560
+ False
1561
+
1562
+ The restriction of the vector field to its own domain is of
1563
+ course itself::
1564
+
1565
+ sage: v.restrict(M) is v
1566
+ True
1567
+ """
1568
+ if (subdomain == self._domain
1569
+ and (dest_map is None or dest_map == self._vmodule._dest_map)):
1570
+ return self
1571
+ if subdomain not in self._restrictions:
1572
+ if not subdomain.is_subset(self._domain):
1573
+ raise ValueError(
1574
+ f"the provided domain {subdomain} is not a subset of the field's domain {self._domain}"
1575
+ )
1576
+ if dest_map is None:
1577
+ dest_map = self._fmodule._dest_map.restrict(subdomain)
1578
+ elif not dest_map._codomain.is_subset(self._ambient_domain):
1579
+ raise ValueError("the argument 'dest_map' is not compatible " +
1580
+ "with the ambient domain of " +
1581
+ "the {}".format(self))
1582
+ # First one tries to derive the restriction from a tighter domain:
1583
+ for dom, rst in self._restrictions.items():
1584
+ if subdomain.is_subset(dom) and subdomain in rst._restrictions:
1585
+ res = rst._restrictions[subdomain]
1586
+ self._restrictions[subdomain] = res
1587
+ self._restrictions_graph[subdomain] = res
1588
+ res._extensions_graph.update(self._extensions_graph)
1589
+ for ext in self._extensions_graph.values():
1590
+ ext._restrictions[subdomain] = res
1591
+ ext._restrictions_graph[subdomain] = res
1592
+ return self._restrictions[subdomain]
1593
+
1594
+ for dom, rst in self._restrictions.items():
1595
+ if subdomain.is_subset(dom) and dom is not self._domain:
1596
+ self._restrictions[subdomain] = rst.restrict(subdomain)
1597
+ self._restrictions_graph[subdomain] = rst.restrict(subdomain)
1598
+ return self._restrictions[subdomain]
1599
+
1600
+ # Secondly one tries to get the restriction from one previously
1601
+ # defined on a larger domain:
1602
+ for dom, ext in self._extensions_graph.items():
1603
+ if subdomain in ext._restrictions_graph:
1604
+ res = ext._restrictions_graph[subdomain]
1605
+ self._restrictions[subdomain] = res
1606
+ self._restrictions_graph[subdomain] = res
1607
+ res._extensions_graph.update(self._extensions_graph)
1608
+ for ext in self._extensions_graph.values():
1609
+ ext._restrictions[subdomain] = res
1610
+ ext._restrictions_graph[subdomain] = res
1611
+ return self._restrictions[subdomain]
1612
+
1613
+ # If this fails, the restriction is created from scratch:
1614
+ smodule = subdomain.vector_field_module(dest_map=dest_map)
1615
+ res = smodule.tensor(self._tensor_type, name=self._name,
1616
+ latex_name=self._latex_name, sym=self._sym,
1617
+ antisym=self._antisym,
1618
+ specific_type=type(self))
1619
+
1620
+ for frame in self._components:
1621
+ for sframe in subdomain._frames:
1622
+ if (sframe.domain() is subdomain and
1623
+ sframe.destination_map() is dest_map and
1624
+ sframe in frame._subframes):
1625
+ comp_store = self._components[frame]._comp
1626
+ scomp = res._new_comp(sframe)
1627
+ scomp_store = scomp._comp
1628
+ # the components of the restriction are evaluated
1629
+ # index by index:
1630
+ for ind, value in comp_store.items():
1631
+ scomp_store[ind] = value.restrict(subdomain)
1632
+ res._components[sframe] = scomp
1633
+
1634
+ res._extensions_graph.update(self._extensions_graph)
1635
+ for dom, ext in self._extensions_graph.items():
1636
+ ext._restrictions[subdomain] = res
1637
+ ext._restrictions_graph[subdomain] = res
1638
+
1639
+ for dom, rst in self._restrictions.items():
1640
+ if dom.is_subset(subdomain):
1641
+ if rst is not res:
1642
+ res._restrictions.update(rst._restrictions)
1643
+ res._restrictions_graph.update(rst._restrictions_graph)
1644
+ rst._extensions_graph.update(res._extensions_graph)
1645
+
1646
+ self._restrictions[subdomain] = res
1647
+ self._restrictions_graph[subdomain] = res
1648
+
1649
+ return self._restrictions[subdomain]
1650
+
1651
+ def __call__(self, *args):
1652
+ r"""
1653
+ The tensor field acting on 1-forms and vector fields as
1654
+ a multilinear map.
1655
+
1656
+ In the particular case of tensor field of type `(1,1)`, the action
1657
+ can be on a single vector field, the tensor field being identified
1658
+ to a field of tangent-space endomorphisms. The output is then a
1659
+ vector field.
1660
+
1661
+ INPUT:
1662
+
1663
+ - ``*args`` -- list of `k` 1-forms and `l` vector fields, ``self``
1664
+ being a tensor of type `(k,l)`
1665
+
1666
+ OUTPUT:
1667
+
1668
+ - either the scalar field resulting from the action of ``self`` on
1669
+ the 1-forms and vector fields passed as arguments or the vector
1670
+ field resulting from the action of ``self`` as a field of
1671
+ tangent-space endomorphisms (case of a type-`(1,1)` tensor field)
1672
+
1673
+ TESTS:
1674
+
1675
+ Action of a tensor field of type-`(1,1)`::
1676
+
1677
+ sage: M = Manifold(2, 'M')
1678
+ sage: X.<x,y> = M.chart()
1679
+ sage: t = M.tensor_field(1,1, [[1+x, 2], [y, -x^2]], name='t')
1680
+ sage: v = M.vector_field(-y, x, name='v')
1681
+ sage: a = M.one_form(3, 1-y, name='a')
1682
+ sage: s = t.__call__(a,v); s
1683
+ Scalar field t(a,v) on the 2-dimensional differentiable manifold M
1684
+ sage: s.display()
1685
+ t(a,v): M → ℝ
1686
+ (x, y) ↦ -x^3 + y^3 + (x^3 - 3*x - 3)*y - y^2 + 6*x
1687
+ sage: s.coord_function() == sum(sum(t[i,j]*a[i]*v[j] for j in [0..1])
1688
+ ....: for i in [0..1])
1689
+ True
1690
+ sage: s == t(a,v) # indirect doctest
1691
+ True
1692
+
1693
+ The tensor field acting on vector field, as a field of tangent-space
1694
+ endomorphisms::
1695
+
1696
+ sage: s = t.__call__(v); s
1697
+ Vector field t(v) on the 2-dimensional differentiable manifold M
1698
+ sage: s.display()
1699
+ t(v) = (-(x + 1)*y + 2*x) ∂/∂x + (-x^3 - y^2) ∂/∂y
1700
+ sage: s[0] == t[0,0]*v[0] + t[0,1]*v[1]
1701
+ True
1702
+ sage: s[1] == t[1,0]*v[0] + t[1,1]*v[1]
1703
+ True
1704
+ sage: s == t(v) # indirect doctest
1705
+ True
1706
+ """
1707
+ from sage.categories.homset import End
1708
+ p = len(args)
1709
+ if p == 1 and self._tensor_type == (1,1):
1710
+ # type-(1,1) tensor acting as an endomorphism:
1711
+ vector = args[0]
1712
+ if vector._tensor_type != (1,0):
1713
+ raise TypeError("the argument must be a vector field")
1714
+ dom = self._domain.intersection(vector._domain)
1715
+ sd = self.restrict(dom)
1716
+ vd = vector.restrict(dom)
1717
+ endom = End(vd.parent())(sd)
1718
+ return endom(vd)
1719
+ # Generic case
1720
+ if p != self._tensor_rank:
1721
+ raise TypeError("{} arguments must be ".format(self._tensor_rank) +
1722
+ "provided")
1723
+ # Domain of the result
1724
+ dom_resu = self._domain
1725
+ for arg in args:
1726
+ dom_resu = dom_resu.intersection(arg._domain)
1727
+ # Restriction to the result domain
1728
+ self_r = self.restrict(dom_resu)
1729
+ args_r = [args[i].restrict(dom_resu) for i in range(p)]
1730
+ # Call of the FreeModuleTensor version
1731
+ return FreeModuleTensor.__call__(self_r, *args_r)
1732
+
1733
+ def contract(self, *args: Union[int, TensorField]) -> TensorFieldParal:
1734
+ r"""
1735
+ Contraction with another tensor field, on one or more indices.
1736
+
1737
+ INPUT:
1738
+
1739
+ - ``pos1`` -- positions of the indices in ``self`` involved in the
1740
+ contraction; ``pos1`` must be a sequence of integers, with 0 standing
1741
+ for the first index position, 1 for the second one, etc. If ``pos1``
1742
+ is not provided, a single contraction on the last index position of
1743
+ ``self`` is assumed
1744
+ - ``other`` -- the tensor field to contract with
1745
+ - ``pos2`` -- positions of the indices in ``other`` involved in the
1746
+ contraction, with the same conventions as for ``pos1``. If ``pos2``
1747
+ is not provided, a single contraction on the first index position of
1748
+ ``other`` is assumed
1749
+
1750
+ OUTPUT:
1751
+
1752
+ - tensor field resulting from the contraction at the positions
1753
+ ``pos1`` and ``pos2`` of ``self`` with ``other``
1754
+
1755
+ EXAMPLES:
1756
+
1757
+ Contraction of a tensor field of type `(2,0)` with a tensor
1758
+ field of type `(1,1)`::
1759
+
1760
+ sage: M = Manifold(2, 'M')
1761
+ sage: X.<x,y> = M.chart()
1762
+ sage: a = M.tensor_field(2,0, [[1+x, 2], [y, -x^2]], name='a')
1763
+ sage: b = M.tensor_field(1,1, [[-y, 1], [x, x+y]], name='b')
1764
+ sage: s = a.contract(0, b, 1); s
1765
+ Tensor field of type (2,0) on the 2-dimensional differentiable manifold M
1766
+ sage: s.display()
1767
+ -x*y ∂/∂x⊗∂/∂x + (x^2 + x*y + y^2 + x) ∂/∂x⊗∂/∂y
1768
+ + (-x^2 - 2*y) ∂/∂y⊗∂/∂x + (-x^3 - x^2*y + 2*x) ∂/∂y⊗∂/∂y
1769
+
1770
+ Check::
1771
+
1772
+ sage: all(s[ind] == sum(a[k, ind[0]]*b[ind[1], k] for k in [0..1])
1773
+ ....: for ind in M.index_generator(2))
1774
+ True
1775
+
1776
+ The same contraction with repeated index notation::
1777
+
1778
+ sage: s == a['^ki']*b['^j_k']
1779
+ True
1780
+
1781
+ Contraction on the second index of ``a``::
1782
+
1783
+ sage: s = a.contract(1, b, 1); s
1784
+ Tensor field of type (2,0) on the 2-dimensional differentiable manifold M
1785
+ sage: s.display()
1786
+ (-(x + 1)*y + 2) ∂/∂x⊗∂/∂x + (x^2 + 3*x + 2*y) ∂/∂x⊗∂/∂y
1787
+ + (-x^2 - y^2) ∂/∂y⊗∂/∂x + (-x^3 - (x^2 - x)*y) ∂/∂y⊗∂/∂y
1788
+
1789
+ Check::
1790
+
1791
+ sage: all(s[ind] == sum(a[ind[0], k]*b[ind[1], k] for k in [0..1])
1792
+ ....: for ind in M.index_generator(2))
1793
+ True
1794
+
1795
+ The same contraction with repeated index notation::
1796
+
1797
+ sage: s == a['^ik']*b['^j_k']
1798
+ True
1799
+
1800
+ .. SEEALSO::
1801
+
1802
+ :meth:`sage.manifolds.differentiable.tensorfield.TensorField.contract`
1803
+ for more examples.
1804
+ """
1805
+ # This is to ensure the call to the TensorField version instead of
1806
+ # the FreeModuleTensor one
1807
+ return TensorField.contract(self, *args)
1808
+
1809
+ def __mul__(self, other):
1810
+ r"""
1811
+ Tensor product (or multiplication of the right by a scalar).
1812
+
1813
+ INPUT:
1814
+
1815
+ - ``other`` -- a tensor field, on the same manifold as ``self`` (or an
1816
+ object that can be coerced to a scalar field on the same manifold
1817
+ as ``self``)
1818
+
1819
+ OUTPUT:
1820
+
1821
+ - the tensor field resulting from the tensor product of ``self``
1822
+ with ``other`` (or from the product ``other * self`` if ``other``
1823
+ is a scalar)
1824
+
1825
+ TESTS::
1826
+
1827
+ sage: M = Manifold(2, 'M')
1828
+ sage: X.<x,y> = M.chart()
1829
+ sage: a = M.tensor_field(0,2, [[1+x, 2], [y, -x^2]], name='a')
1830
+
1831
+ Tensor product with another tensor field::
1832
+
1833
+ sage: v = M.vector_field(-y, x, name='v')
1834
+ sage: s = a.__mul__(v); s
1835
+ Tensor field a⊗v of type (1,2) on the 2-dimensional differentiable
1836
+ manifold M
1837
+ sage: s.display()
1838
+ a⊗v = -(x + 1)*y ∂/∂x⊗dx⊗dx - 2*y ∂/∂x⊗dx⊗dy - y^2 ∂/∂x⊗dy⊗dx
1839
+ + x^2*y ∂/∂x⊗dy⊗dy + (x^2 + x) ∂/∂y⊗dx⊗dx + 2*x ∂/∂y⊗dx⊗dy
1840
+ + x*y ∂/∂y⊗dy⊗dx - x^3 ∂/∂y⊗dy⊗dy
1841
+ sage: all(s[ind] == v[ind[0]] * a[ind[1],ind[2]]
1842
+ ....: for ind in M.index_generator(3))
1843
+ True
1844
+
1845
+ Multiplication on the right by a scalar field::
1846
+
1847
+ sage: f = M.scalar_field({X: x+y}, name='f')
1848
+ sage: s = a.__mul__(f); s
1849
+ Tensor field f*a of type (0,2) on the 2-dimensional differentiable
1850
+ manifold M
1851
+ sage: s.display()
1852
+ f*a = (x^2 + (x + 1)*y + x) dx⊗dx + (2*x + 2*y) dx⊗dy
1853
+ + (x*y + y^2) dy⊗dx + (-x^3 - x^2*y) dy⊗dy
1854
+ sage: s == f*a
1855
+ True
1856
+ """
1857
+ # This is to ensure the call to the TensorField version instead of
1858
+ # the FreeModuleTensor one
1859
+ return TensorField.__mul__(self, other)
1860
+
1861
+ def display_comp(self, frame=None, chart=None, coordinate_labels=True,
1862
+ only_nonzero=True, only_nonredundant=False):
1863
+ r"""
1864
+ Display the tensor components with respect to a given frame,
1865
+ one per line.
1866
+
1867
+ The output is either text-formatted (console mode) or LaTeX-formatted
1868
+ (notebook mode).
1869
+
1870
+ INPUT:
1871
+
1872
+ - ``frame`` -- (default: ``None``) vector frame with respect to which
1873
+ the tensor field components are defined; if ``None``, then
1874
+
1875
+ * if ``chart`` is not ``None``, the coordinate frame associated to
1876
+ ``chart`` is used
1877
+ * otherwise, the default basis of the vector field module on which
1878
+ the tensor field is defined is used
1879
+
1880
+ - ``chart`` -- (default: ``None``) chart specifying the coordinate
1881
+ expression of the components; if ``None``, the default chart of the
1882
+ tensor field domain is used
1883
+ - ``coordinate_labels`` -- boolean (default: ``True``); if ``True``,
1884
+ coordinate symbols are used by default (instead of integers) as
1885
+ index labels whenever ``frame`` is a coordinate frame
1886
+ - ``only_nonzero`` -- boolean (default: ``True``); if ``True``, only
1887
+ nonzero components are displayed
1888
+ - ``only_nonredundant`` -- boolean (default: ``False``); if ``True``,
1889
+ only nonredundant components are displayed in case of symmetries
1890
+
1891
+ EXAMPLES:
1892
+
1893
+ Display of the components of a type-`(2,1)` tensor field on a
1894
+ 2-dimensional manifold::
1895
+
1896
+ sage: M = Manifold(2, 'M')
1897
+ sage: X.<x,y> = M.chart()
1898
+ sage: t = M.tensor_field(2, 1, name='t', sym=(0,1))
1899
+ sage: t[0,0,0], t[0,1,0], t[1,1,1] = x+y, x*y, -3
1900
+ sage: t.display_comp()
1901
+ t^xx_x = x + y
1902
+ t^xy_x = x*y
1903
+ t^yx_x = x*y
1904
+ t^yy_y = -3
1905
+
1906
+ By default, only the non-vanishing components are displayed;
1907
+ to see all the components, the argument ``only_nonzero`` must
1908
+ be set to ``False``::
1909
+
1910
+ sage: t.display_comp(only_nonzero=False)
1911
+ t^xx_x = x + y
1912
+ t^xx_y = 0
1913
+ t^xy_x = x*y
1914
+ t^xy_y = 0
1915
+ t^yx_x = x*y
1916
+ t^yx_y = 0
1917
+ t^yy_x = 0
1918
+ t^yy_y = -3
1919
+
1920
+ ``t`` being symmetric with respect to its first two indices, one
1921
+ may ask to skip the components that can be deduced by symmetry::
1922
+
1923
+ sage: t.display_comp(only_nonredundant=True)
1924
+ t^xx_x = x + y
1925
+ t^xy_x = x*y
1926
+ t^yy_y = -3
1927
+
1928
+ Instead of coordinate labels, one may ask for integers::
1929
+
1930
+ sage: t.display_comp(coordinate_labels=False)
1931
+ t^00_0 = x + y
1932
+ t^01_0 = x*y
1933
+ t^10_0 = x*y
1934
+ t^11_1 = -3
1935
+
1936
+ Display in a frame different from the default one (note that
1937
+ since ``f`` is not a coordinate frame, integer are used to
1938
+ label the indices)::
1939
+
1940
+ sage: a = M.automorphism_field()
1941
+ sage: a[:] = [[1+y^2, 0], [0, 2+x^2]]
1942
+ sage: f = X.frame().new_frame(a, 'f')
1943
+ sage: t.display_comp(frame=f)
1944
+ t^00_0 = (x + y)/(y^2 + 1)
1945
+ t^01_0 = x*y/(x^2 + 2)
1946
+ t^10_0 = x*y/(x^2 + 2)
1947
+ t^11_1 = -3/(x^2 + 2)
1948
+
1949
+ Display with respect to a chart different from the default one::
1950
+
1951
+ sage: Y.<u,v> = M.chart()
1952
+ sage: X_to_Y = X.transition_map(Y, [x+y, x-y])
1953
+ sage: Y_to_X = X_to_Y.inverse()
1954
+ sage: t.display_comp(chart=Y)
1955
+ t^uu_u = 1/4*u^2 - 1/4*v^2 + 1/2*u - 3/2
1956
+ t^uu_v = 1/4*u^2 - 1/4*v^2 + 1/2*u + 3/2
1957
+ t^uv_u = 1/2*u + 3/2
1958
+ t^uv_v = 1/2*u - 3/2
1959
+ t^vu_u = 1/2*u + 3/2
1960
+ t^vu_v = 1/2*u - 3/2
1961
+ t^vv_u = -1/4*u^2 + 1/4*v^2 + 1/2*u - 3/2
1962
+ t^vv_v = -1/4*u^2 + 1/4*v^2 + 1/2*u + 3/2
1963
+
1964
+ Note that the frame defining the components is the coordinate frame
1965
+ associated with chart ``Y``, i.e. we have::
1966
+
1967
+ sage: str(t.display_comp(chart=Y)) == str(t.display_comp(frame=Y.frame(), chart=Y))
1968
+ True
1969
+
1970
+ Display of the components with respect to a specific frame, expressed
1971
+ in terms of a specific chart::
1972
+
1973
+ sage: t.display_comp(frame=f, chart=Y)
1974
+ t^00_0 = 4*u/(u^2 - 2*u*v + v^2 + 4)
1975
+ t^01_0 = (u^2 - v^2)/(u^2 + 2*u*v + v^2 + 8)
1976
+ t^10_0 = (u^2 - v^2)/(u^2 + 2*u*v + v^2 + 8)
1977
+ t^11_1 = -12/(u^2 + 2*u*v + v^2 + 8)
1978
+ """
1979
+ from sage.manifolds.differentiable.vectorframe import CoordFrame
1980
+ from sage.misc.latex import latex
1981
+ if frame is None:
1982
+ if chart is not None:
1983
+ frame = chart.frame()
1984
+ else:
1985
+ frame = self._fmodule.default_basis()
1986
+ if chart is None:
1987
+ chart = self._domain.default_chart()
1988
+ index_labels = None
1989
+ index_latex_labels = None
1990
+ if isinstance(frame, CoordFrame) and coordinate_labels:
1991
+ ch = frame.chart()
1992
+ index_labels = list(map(str, ch[:]))
1993
+ index_latex_labels = list(map(latex, ch[:]))
1994
+ return FreeModuleTensor.display_comp(self, basis=frame,
1995
+ format_spec=chart, index_labels=index_labels,
1996
+ index_latex_labels=index_latex_labels,
1997
+ only_nonzero=only_nonzero,
1998
+ only_nonredundant=only_nonredundant)
1999
+
2000
+ def at(self, point):
2001
+ r"""
2002
+ Value of ``self`` at a point of its domain.
2003
+
2004
+ If the current tensor field is
2005
+
2006
+ .. MATH::
2007
+
2008
+ t:\ U \longrightarrow T^{(k,l)} M
2009
+
2010
+ associated with the differentiable map
2011
+
2012
+ .. MATH::
2013
+
2014
+ \Phi:\ U \longrightarrow M,
2015
+
2016
+ where `U` and `M` are two manifolds (possibly `U = M` and
2017
+ `\Phi = \mathrm{Id}_M`), then for any point `p\in U`, `t(p)` is
2018
+ a tensor on the tangent space to `M` at the point `\Phi(p)`.
2019
+
2020
+ INPUT:
2021
+
2022
+ - ``point`` -- :class:`~sage.manifolds.point.ManifoldPoint`
2023
+ point `p` in the domain of the tensor field `U`
2024
+
2025
+ OUTPUT:
2026
+
2027
+ - :class:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor`
2028
+ representing the tensor `t(p)` on the tangent vector space
2029
+ `T_{\Phi(p)} M`
2030
+
2031
+ EXAMPLES:
2032
+
2033
+ Vector in a tangent space of a 2-dimensional manifold::
2034
+
2035
+ sage: M = Manifold(2, 'M')
2036
+ sage: c_xy.<x,y> = M.chart()
2037
+ sage: p = M.point((-2,3), name='p')
2038
+ sage: v = M.vector_field(y, x^2, name='v')
2039
+ sage: v.display()
2040
+ v = y ∂/∂x + x^2 ∂/∂y
2041
+ sage: vp = v.at(p) ; vp
2042
+ Tangent vector v at Point p on the 2-dimensional differentiable
2043
+ manifold M
2044
+ sage: vp.parent()
2045
+ Tangent space at Point p on the 2-dimensional differentiable
2046
+ manifold M
2047
+ sage: vp.display()
2048
+ v = 3 ∂/∂x + 4 ∂/∂y
2049
+
2050
+ A 1-form gives birth to a linear form in the tangent space::
2051
+
2052
+ sage: w = M.one_form(-x, 1+y, name='w')
2053
+ sage: w.display()
2054
+ w = -x dx + (y + 1) dy
2055
+ sage: wp = w.at(p) ; wp
2056
+ Linear form w on the Tangent space at Point p on the 2-dimensional
2057
+ differentiable manifold M
2058
+ sage: wp.parent()
2059
+ Dual of the Tangent space at Point p on the 2-dimensional
2060
+ differentiable manifold M
2061
+ sage: wp.display()
2062
+ w = 2 dx + 4 dy
2063
+
2064
+ A tensor field of type `(1,1)` yields a tensor of type `(1,1)`
2065
+ in the tangent space::
2066
+
2067
+ sage: t = M.tensor_field(1, 1, name='t')
2068
+ sage: t[0,0], t[0,1], t[1,1] = 1+x, x*y, 1-y
2069
+ sage: t.display()
2070
+ t = (x + 1) ∂/∂x⊗dx + x*y ∂/∂x⊗dy + (-y + 1) ∂/∂y⊗dy
2071
+ sage: tp = t.at(p) ; tp
2072
+ Type-(1,1) tensor t on the Tangent space at Point p on the
2073
+ 2-dimensional differentiable manifold M
2074
+ sage: tp.parent()
2075
+ Free module of type-(1,1) tensors on the Tangent space at Point p
2076
+ on the 2-dimensional differentiable manifold M
2077
+ sage: tp.display()
2078
+ t = -∂/∂x⊗dx - 6 ∂/∂x⊗dy - 2 ∂/∂y⊗dy
2079
+
2080
+ A 2-form yields an alternating form of degree 2 in the tangent space::
2081
+
2082
+ sage: a = M.diff_form(2, name='a')
2083
+ sage: a[0,1] = x*y
2084
+ sage: a.display()
2085
+ a = x*y dx∧dy
2086
+ sage: ap = a.at(p) ; ap
2087
+ Alternating form a of degree 2 on the Tangent space at Point p on
2088
+ the 2-dimensional differentiable manifold M
2089
+ sage: ap.parent()
2090
+ 2nd exterior power of the dual of the Tangent space at Point p on
2091
+ the 2-dimensional differentiable manifold M
2092
+ sage: ap.display()
2093
+ a = -6 dx∧dy
2094
+
2095
+ Example with a non trivial map `\Phi`::
2096
+
2097
+ sage: U = Manifold(1, 'U') # (0,2*pi) as a 1-dimensional manifold
2098
+ sage: T.<t> = U.chart(r't:(0,2*pi)') # canonical chart on U
2099
+ sage: Phi = U.diff_map(M, [cos(t), sin(t)], name='Phi',
2100
+ ....: latex_name=r'\Phi')
2101
+ sage: v = U.vector_field(1+t, t^2, name='v', dest_map=Phi) ; v
2102
+ Vector field v along the 1-dimensional differentiable manifold U
2103
+ with values on the 2-dimensional differentiable manifold M
2104
+ sage: v.display()
2105
+ v = (t + 1) ∂/∂x + t^2 ∂/∂y
2106
+ sage: p = U((pi/6,))
2107
+ sage: vp = v.at(p) ; vp
2108
+ Tangent vector v at Point on the 2-dimensional differentiable
2109
+ manifold M
2110
+ sage: vp.parent() is M.tangent_space(Phi(p))
2111
+ True
2112
+ sage: vp.display()
2113
+ v = (1/6*pi + 1) ∂/∂x + 1/36*pi^2 ∂/∂y
2114
+ """
2115
+ if point not in self._domain:
2116
+ raise ValueError("the {} is not in the domain of ".format(point) +
2117
+ "the {}".format(self))
2118
+ dest_map = self._fmodule._dest_map
2119
+ if dest_map.is_identity():
2120
+ amb_point = point
2121
+ else:
2122
+ amb_point = dest_map(point) # "ambient" point
2123
+ ts = amb_point._manifold.tangent_space(amb_point)
2124
+ resu = ts.tensor(self._tensor_type, name=self._name,
2125
+ latex_name=self._latex_name, sym=self._sym,
2126
+ antisym=self._antisym)
2127
+ for frame, comp in self._components.items():
2128
+ comp_resu = resu.add_comp(frame.at(point))
2129
+ for ind, val in comp._comp.items():
2130
+ comp_resu._comp[ind] = val(point)
2131
+ return resu
2132
+
2133
+ def along(self, mapping):
2134
+ r"""
2135
+ Return the tensor field deduced from ``self`` via a differentiable map,
2136
+ the codomain of which is included in the domain of ``self``.
2137
+
2138
+ More precisely, if ``self`` is a tensor field `t` on `M` and if
2139
+ `\Phi: U \rightarrow M` is a differentiable map from some
2140
+ differentiable manifold `U` to `M`, the returned object is
2141
+ a tensor field `\tilde t` along `U` with values on `M` such that
2142
+
2143
+ .. MATH::
2144
+
2145
+ \forall p \in U,\ \tilde t(p) = t(\Phi(p)).
2146
+
2147
+ INPUT:
2148
+
2149
+ - ``mapping`` -- differentiable map `\Phi: U \rightarrow M`
2150
+
2151
+ OUTPUT: tensor field `\tilde t` along `U` defined above
2152
+
2153
+ EXAMPLES:
2154
+
2155
+ Let us consider the map `\Phi` between the interval `U=(0,2\pi)` and
2156
+ the Euclidean plane `M=\RR^2` defining the lemniscate of Gerono::
2157
+
2158
+ sage: M = Manifold(2, 'M')
2159
+ sage: X.<x,y> = M.chart()
2160
+ sage: t = var('t', domain='real')
2161
+ sage: Phi = M.curve({X: [sin(t), sin(2*t)/2]}, (t, 0, 2*pi),
2162
+ ....: name='Phi')
2163
+ sage: U = Phi.domain(); U
2164
+ Real interval (0, 2*pi)
2165
+
2166
+ and a vector field on `M`::
2167
+
2168
+ sage: v = M.vector_field(-y , x, name='v')
2169
+
2170
+ We have then::
2171
+
2172
+ sage: vU = v.along(Phi); vU
2173
+ Vector field v along the Real interval (0, 2*pi) with values on
2174
+ the 2-dimensional differentiable manifold M
2175
+ sage: vU.display()
2176
+ v = -cos(t)*sin(t) ∂/∂x + sin(t) ∂/∂y
2177
+ sage: vU.parent()
2178
+ Free module X((0, 2*pi),Phi) of vector fields along the Real
2179
+ interval (0, 2*pi) mapped into the 2-dimensional differentiable
2180
+ manifold M
2181
+ sage: vU.parent() is Phi.tangent_vector_field().parent()
2182
+ True
2183
+
2184
+ We check that the defining relation `\tilde t(p) = t(\Phi(p))` holds::
2185
+
2186
+ sage: p = U(t) # a generic point of U
2187
+ sage: vU.at(p) == v.at(Phi(p))
2188
+ True
2189
+
2190
+ Case of a tensor field of type ``(0,2)``::
2191
+
2192
+ sage: a = M.tensor_field(0, 2)
2193
+ sage: a[0,0], a[0,1], a[1,1] = x+y, x*y, x^2-y^2
2194
+ sage: aU = a.along(Phi); aU
2195
+ Tensor field of type (0,2) along the Real interval (0, 2*pi) with
2196
+ values on the 2-dimensional differentiable manifold M
2197
+ sage: aU.display()
2198
+ (cos(t) + 1)*sin(t) dx⊗dx + cos(t)*sin(t)^2 dx⊗dy + sin(t)^4 dy⊗dy
2199
+ sage: aU.parent()
2200
+ Free module T^(0,2)((0, 2*pi),Phi) of type-(0,2) tensors fields
2201
+ along the Real interval (0, 2*pi) mapped into the 2-dimensional
2202
+ differentiable manifold M
2203
+ sage: aU.at(p) == a.at(Phi(p))
2204
+ True
2205
+ """
2206
+ dom = self._domain
2207
+ if self._ambient_domain is not dom:
2208
+ raise ValueError("{} is not a tensor field ".format(self) +
2209
+ "with values in the {}".format(dom))
2210
+ if mapping.codomain().is_subset(dom):
2211
+ rmapping = mapping
2212
+ else:
2213
+ rmapping = None
2214
+ for doms, rest in mapping._restrictions.items():
2215
+ if doms[1].is_subset(dom):
2216
+ rmapping = rest
2217
+ break
2218
+ else:
2219
+ raise ValueError("the codomain of {} is not ".format(mapping) +
2220
+ "included in the domain of {}".format(self))
2221
+ dom_resu = rmapping.domain()
2222
+ vmodule = dom_resu.vector_field_module(dest_map=rmapping)
2223
+ resu = vmodule.tensor(self._tensor_type, name=self._name,
2224
+ latex_name=self._latex_name, sym=self._sym,
2225
+ antisym=self._antisym)
2226
+ for frame, comp in self._components.items():
2227
+ comp_resu = resu.add_comp(frame.along(rmapping))
2228
+ for ind, val in comp._comp.items():
2229
+ val_resu = dom_resu.scalar_field()
2230
+ for chart2, func2 in val._express.items():
2231
+ for chart1 in dom_resu.atlas():
2232
+ if (chart1, chart2) in rmapping._coord_expression:
2233
+ phi = rmapping._coord_expression[(chart1, chart2)]
2234
+ # X2 coordinates expressed in terms of X1 ones via
2235
+ # phi:
2236
+ coord2_1 = phi(*(chart1._xx))
2237
+ val_resu.add_expr(func2(*coord2_1), chart=chart1)
2238
+ if not val_resu._express:
2239
+ raise ValueError("no pair of charts has been found to " +
2240
+ "set the value of the component " +
2241
+ "{} in the {}".format(ind, frame))
2242
+ comp_resu._comp[ind] = val_resu
2243
+ return resu
2244
+
2245
+ def series_expansion(self, symbol: Expression, order: int) -> list[TensorFieldParal]:
2246
+ r"""
2247
+ Expand the tensor field in power series with respect to a small
2248
+ parameter.
2249
+
2250
+ If the small parameter is `\epsilon` and `T` is ``self``, the
2251
+ power series expansion to order `n` is
2252
+
2253
+ .. MATH::
2254
+
2255
+ T = T_0 + \epsilon T_1 + \epsilon^2 T_2 + \cdots + \epsilon^n T_n
2256
+ + O(\epsilon^{n+1}),
2257
+
2258
+ where `T_0, T_1, \ldots, T_n` are `n+1` tensor fields of the same
2259
+ tensor type as ``self`` and do not depend upon `\epsilon`.
2260
+
2261
+ INPUT:
2262
+
2263
+ - ``symbol`` -- symbolic variable (the "small parameter" `\epsilon`)
2264
+ with respect to which the components of ``self`` are expanded in
2265
+ power series
2266
+ - ``order`` -- integer; the order `n` of the expansion, defined as the
2267
+ degree of the polynomial representing the truncated power series in
2268
+ ``symbol``
2269
+
2270
+ OUTPUT:
2271
+
2272
+ - list of the tensor fields `T_i` (size ``order+1``)
2273
+
2274
+ EXAMPLES::
2275
+
2276
+ sage: M = Manifold(4, 'M', structure='Lorentzian')
2277
+ sage: C.<t,x,y,z> = M.chart()
2278
+ sage: e = var('e')
2279
+ sage: g = M.metric()
2280
+ sage: h1 = M.tensor_field(0,2,sym=(0,1))
2281
+ sage: h2 = M.tensor_field(0,2,sym=(0,1))
2282
+ sage: g[0, 0], g[1, 1], g[2, 2], g[3, 3] = -1, 1, 1, 1
2283
+ sage: h1[0, 1], h1[1, 2], h1[2, 3] = 1, 1, 1
2284
+ sage: h2[0, 2], h2[1, 3] = 1, 1
2285
+ sage: g.set(g + e*h1 + e^2*h2)
2286
+ sage: g_ser = g.series_expansion(e, 2); g_ser
2287
+ [Field of symmetric bilinear forms on the 4-dimensional Lorentzian manifold M,
2288
+ Field of symmetric bilinear forms on the 4-dimensional Lorentzian manifold M,
2289
+ Field of symmetric bilinear forms on the 4-dimensional Lorentzian manifold M]
2290
+ sage: g_ser[0][:]
2291
+ [-1 0 0 0]
2292
+ [ 0 1 0 0]
2293
+ [ 0 0 1 0]
2294
+ [ 0 0 0 1]
2295
+ sage: g_ser[1][:]
2296
+ [0 1 0 0]
2297
+ [1 0 1 0]
2298
+ [0 1 0 1]
2299
+ [0 0 1 0]
2300
+ sage: g_ser[2][:]
2301
+ [0 0 1 0]
2302
+ [0 0 0 1]
2303
+ [1 0 0 0]
2304
+ [0 1 0 0]
2305
+ sage: all([g_ser[1] == h1, g_ser[2] == h2])
2306
+ True
2307
+ """
2308
+ from sage.tensor.modules.comp import Components
2309
+ orderp1 = order + 1
2310
+ res = [0] * orderp1
2311
+ for k in range(orderp1):
2312
+ res[k] = self.domain().tensor_field(*self.tensor_type(),
2313
+ dest_map=self._fmodule._dest_map,
2314
+ sym=self._sym,
2315
+ antisym=self._antisym)
2316
+ for frame in self._components:
2317
+ decompo = {}
2318
+ comp = self.comp(frame)
2319
+ res_comp = [0] * orderp1
2320
+ for inds in comp.index_generator():
2321
+ decompo[inds] = comp[inds].expr().series(symbol,
2322
+ orderp1).truncate().coefficients(symbol)
2323
+ for k in range(orderp1):
2324
+ res_comp[k] = Components(SR, frame, self.tensor_rank())
2325
+ for inds in comp.index_generator():
2326
+ res_comp_k = [decompo[inds][l][0] for l in range(len(decompo[inds]))
2327
+ if decompo[inds][l][1] == k]
2328
+ res_comp[k][inds] = res_comp_k[0] if len(res_comp_k) >= 1 else 0
2329
+ res[k].add_comp(frame)[:] = res_comp[k][:]
2330
+ return res
2331
+
2332
+ def truncate(self, symbol, order):
2333
+ r"""
2334
+ Return the tensor field truncated at a given order in the power series
2335
+ expansion with respect to some small parameter.
2336
+
2337
+ If the small parameter is `\epsilon` and `T` is ``self``, the
2338
+ power series expansion to order `n` is
2339
+
2340
+ .. MATH::
2341
+
2342
+ T = T_0 + \epsilon T_1 + \epsilon^2 T_2 + \cdots + \epsilon^n T_n
2343
+ + O(\epsilon^{n+1}),
2344
+
2345
+ where `T_0, T_1, \ldots, T_n` are `n+1` tensor fields of the same
2346
+ tensor type as ``self`` and do not depend upon `\epsilon`.
2347
+
2348
+ INPUT:
2349
+
2350
+ - ``symbol`` -- symbolic variable (the "small parameter" `\epsilon`)
2351
+ with respect to which the components of ``self`` are expanded in
2352
+ power series
2353
+ - ``order`` -- integer; the order `n` of the expansion, defined as the
2354
+ degree of the polynomial representing the truncated power series in
2355
+ ``symbol``
2356
+
2357
+ OUTPUT:
2358
+
2359
+ - the tensor field
2360
+ `T_0 + \epsilon T_1 + \epsilon^2 T_2 + \cdots + \epsilon^n T_n`
2361
+
2362
+ EXAMPLES::
2363
+
2364
+ sage: M = Manifold(4, 'M', structure='Lorentzian')
2365
+ sage: C.<t,x,y,z> = M.chart()
2366
+ sage: e = var('e')
2367
+ sage: g = M.metric()
2368
+ sage: h1 = M.tensor_field(0,2,sym=(0,1))
2369
+ sage: h2 = M.tensor_field(0,2,sym=(0,1))
2370
+ sage: g[0, 0], g[1, 1], g[2, 2], g[3, 3] = -1, 1, 1, 1
2371
+ sage: h1[0, 1], h1[1, 2], h1[2, 3] = 1, 1, 1
2372
+ sage: h2[0, 2], h2[1, 3] = 1, 1
2373
+ sage: g.set(g + e*h1 + e^2*h2)
2374
+ sage: g[:]
2375
+ [ -1 e e^2 0]
2376
+ [ e 1 e e^2]
2377
+ [e^2 e 1 e]
2378
+ [ 0 e^2 e 1]
2379
+ sage: g.truncate(e, 1)[:]
2380
+ [-1 e 0 0]
2381
+ [ e 1 e 0]
2382
+ [ 0 e 1 e]
2383
+ [ 0 0 e 1]
2384
+ """
2385
+ series = self.series_expansion(symbol, order)
2386
+ return sum(symbol**i * s for i, s in enumerate(series))
2387
+
2388
+ def set_calc_order(self, symbol: Expression, order: int, truncate: bool = False):
2389
+ r"""
2390
+ Trigger a power series expansion with respect to a small parameter in
2391
+ computations involving the tensor field.
2392
+
2393
+ This property is propagated by usual operations. The internal
2394
+ representation must be ``SR`` for this to take effect.
2395
+
2396
+ If the small parameter is `\epsilon` and `T` is ``self``, the
2397
+ power series expansion to order `n` is
2398
+
2399
+ .. MATH::
2400
+
2401
+ T = T_0 + \epsilon T_1 + \epsilon^2 T_2 + \cdots + \epsilon^n T_n
2402
+ + O(\epsilon^{n+1}),
2403
+
2404
+ where `T_0, T_1, \ldots, T_n` are `n+1` tensor fields of the same
2405
+ tensor type as ``self`` and do not depend upon `\epsilon`.
2406
+
2407
+ INPUT:
2408
+
2409
+ - ``symbol`` -- symbolic variable (the "small parameter" `\epsilon`)
2410
+ with respect to which the components of ``self`` are expanded in
2411
+ power series
2412
+ - ``order`` -- integer; the order `n` of the expansion, defined as the
2413
+ degree of the polynomial representing the truncated power series in
2414
+ ``symbol``
2415
+ - ``truncate`` -- boolean (default: ``False``); determines whether the
2416
+ components of ``self`` are replaced by their expansions to the
2417
+ given order
2418
+
2419
+ EXAMPLES::
2420
+
2421
+ sage: M = Manifold(4, 'M', structure='Lorentzian')
2422
+ sage: C.<t,x,y,z> = M.chart()
2423
+ sage: e = var('e')
2424
+ sage: g = M.metric()
2425
+ sage: h1 = M.tensor_field(0, 2, sym=(0,1))
2426
+ sage: h2 = M.tensor_field(0, 2, sym=(0,1))
2427
+ sage: g[0, 0], g[1, 1], g[2, 2], g[3, 3] = -1, 1, 1, 1
2428
+ sage: h1[0, 1], h1[1, 2], h1[2, 3] = 1, 1, 1
2429
+ sage: h2[0, 2], h2[1, 3] = 1, 1
2430
+ sage: g.set(g + e*h1 + e^2*h2)
2431
+ sage: g.set_calc_order(e, 1)
2432
+ sage: g[:]
2433
+ [ -1 e e^2 0]
2434
+ [ e 1 e e^2]
2435
+ [e^2 e 1 e]
2436
+ [ 0 e^2 e 1]
2437
+ sage: g.set_calc_order(e, 1, truncate=True)
2438
+ sage: g[:]
2439
+ [-1 e 0 0]
2440
+ [ e 1 e 0]
2441
+ [ 0 e 1 e]
2442
+ [ 0 0 e 1]
2443
+ """
2444
+ for frame in self._components:
2445
+ for ind in self._components[frame].non_redundant_index_generator():
2446
+ self._components[frame][ind]._expansion_symbol = symbol
2447
+ self._components[frame][ind]._order = order
2448
+ if truncate:
2449
+ self._components[frame][ind].simplify()
2450
+ self._del_derived()