passagemath-symbolics 10.6.37__cp314-cp314t-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.6.37.dist-info/METADATA +187 -0
- passagemath_symbolics-10.6.37.dist-info/RECORD +172 -0
- passagemath_symbolics-10.6.37.dist-info/WHEEL +6 -0
- passagemath_symbolics-10.6.37.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2826 -0
- sage/calculus/desolvers.py +1866 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-314t-darwin.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-darwin.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +3017 -0
- sage/interfaces/magma_free.py +92 -0
- sage/interfaces/maple.py +1397 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +555 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4019 -0
- sage/manifolds/chart_func.py +3419 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
- sage/manifolds/differentiable/diff_form.py +1658 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1520 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +910 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1728 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2764 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +885 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1342 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-314t-darwin.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1022 -0
- sage/matrix/matrix_symbolic_sparse.cpython-314t-darwin.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5237 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +985 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +459 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1287 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1713 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +270 -0
- sage/symbolic/integration/integral.py +1115 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/random_tests.py +462 -0
- sage/symbolic/relation.py +1907 -0
- sage/symbolic/ring.cpython-314t-darwin.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyx +1396 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- 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()
|