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