passagemath-symbolics 10.6.40__cp314-cp314t-macosx_13_0_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.

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