passagemath-symbolics 10.8.1a1__cp314-cp314t-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. passagemath_symbolics/__init__.py +3 -0
  2. passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
  3. passagemath_symbolics-10.8.1a1.dist-info/RECORD +181 -0
  4. passagemath_symbolics-10.8.1a1.dist-info/WHEEL +5 -0
  5. passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
  6. sage/all__sagemath_symbolics.py +17 -0
  7. sage/calculus/all.py +14 -0
  8. sage/calculus/calculus.py +2838 -0
  9. sage/calculus/desolvers.py +1864 -0
  10. sage/calculus/predefined.py +51 -0
  11. sage/calculus/tests.py +225 -0
  12. sage/calculus/var.cpython-314t-aarch64-linux-musl.so +0 -0
  13. sage/calculus/var.pyx +401 -0
  14. sage/dynamics/all__sagemath_symbolics.py +6 -0
  15. sage/dynamics/complex_dynamics/all.py +5 -0
  16. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  17. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-aarch64-linux-musl.so +0 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
  19. sage/ext/all__sagemath_symbolics.py +1 -0
  20. sage/ext_data/kenzo/CP2.txt +45 -0
  21. sage/ext_data/kenzo/CP3.txt +349 -0
  22. sage/ext_data/kenzo/CP4.txt +4774 -0
  23. sage/ext_data/kenzo/README.txt +49 -0
  24. sage/ext_data/kenzo/S4.txt +20 -0
  25. sage/ext_data/magma/latex/latex.m +1021 -0
  26. sage/ext_data/magma/latex/latex.spec +1 -0
  27. sage/ext_data/magma/sage/basic.m +356 -0
  28. sage/ext_data/magma/sage/sage.spec +1 -0
  29. sage/ext_data/magma/spec +9 -0
  30. sage/geometry/all__sagemath_symbolics.py +8 -0
  31. sage/geometry/hyperbolic_space/all.py +5 -0
  32. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  39. sage/geometry/riemannian_manifolds/all.py +7 -0
  40. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  41. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  42. sage/interfaces/all__sagemath_symbolics.py +1 -0
  43. sage/interfaces/magma.py +2991 -0
  44. sage/interfaces/magma_free.py +90 -0
  45. sage/interfaces/maple.py +1402 -0
  46. sage/interfaces/mathematica.py +1345 -0
  47. sage/interfaces/mathics.py +1312 -0
  48. sage/interfaces/sympy.py +1398 -0
  49. sage/interfaces/sympy_wrapper.py +197 -0
  50. sage/interfaces/tides.py +938 -0
  51. sage/libs/all__sagemath_symbolics.py +6 -0
  52. sage/manifolds/all.py +7 -0
  53. sage/manifolds/calculus_method.py +553 -0
  54. sage/manifolds/catalog.py +437 -0
  55. sage/manifolds/chart.py +4010 -0
  56. sage/manifolds/chart_func.py +3416 -0
  57. sage/manifolds/continuous_map.py +2183 -0
  58. sage/manifolds/continuous_map_image.py +155 -0
  59. sage/manifolds/differentiable/affine_connection.py +2475 -0
  60. sage/manifolds/differentiable/all.py +1 -0
  61. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  62. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  63. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  64. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  65. sage/manifolds/differentiable/chart.py +1241 -0
  66. sage/manifolds/differentiable/curve.py +1028 -0
  67. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  68. sage/manifolds/differentiable/degenerate.py +559 -0
  69. sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
  70. sage/manifolds/differentiable/diff_form.py +1660 -0
  71. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  72. sage/manifolds/differentiable/diff_map.py +1315 -0
  73. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  74. sage/manifolds/differentiable/examples/all.py +1 -0
  75. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  76. sage/manifolds/differentiable/examples/real_line.py +897 -0
  77. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  78. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  79. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  80. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  81. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  82. sage/manifolds/differentiable/manifold.py +4254 -0
  83. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  84. sage/manifolds/differentiable/metric.py +3032 -0
  85. sage/manifolds/differentiable/mixed_form.py +1507 -0
  86. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  87. sage/manifolds/differentiable/multivector_module.py +800 -0
  88. sage/manifolds/differentiable/multivectorfield.py +1522 -0
  89. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  90. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  91. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  92. sage/manifolds/differentiable/scalarfield.py +1343 -0
  93. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  94. sage/manifolds/differentiable/symplectic_form.py +912 -0
  95. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  96. sage/manifolds/differentiable/tangent_space.py +412 -0
  97. sage/manifolds/differentiable/tangent_vector.py +616 -0
  98. sage/manifolds/differentiable/tensorfield.py +4665 -0
  99. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  100. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  101. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  102. sage/manifolds/differentiable/vector_bundle.py +1725 -0
  103. sage/manifolds/differentiable/vectorfield.py +1717 -0
  104. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  105. sage/manifolds/differentiable/vectorframe.py +1832 -0
  106. sage/manifolds/family.py +270 -0
  107. sage/manifolds/local_frame.py +1490 -0
  108. sage/manifolds/manifold.py +3090 -0
  109. sage/manifolds/manifold_homset.py +452 -0
  110. sage/manifolds/operators.py +359 -0
  111. sage/manifolds/point.py +994 -0
  112. sage/manifolds/scalarfield.py +3718 -0
  113. sage/manifolds/scalarfield_algebra.py +629 -0
  114. sage/manifolds/section.py +3111 -0
  115. sage/manifolds/section_module.py +831 -0
  116. sage/manifolds/structure.py +229 -0
  117. sage/manifolds/subset.py +2721 -0
  118. sage/manifolds/subsets/all.py +1 -0
  119. sage/manifolds/subsets/closure.py +131 -0
  120. sage/manifolds/subsets/pullback.py +883 -0
  121. sage/manifolds/topological_submanifold.py +891 -0
  122. sage/manifolds/trivialization.py +733 -0
  123. sage/manifolds/utilities.py +1348 -0
  124. sage/manifolds/vector_bundle.py +1347 -0
  125. sage/manifolds/vector_bundle_fiber.py +332 -0
  126. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  127. sage/matrix/all__sagemath_symbolics.py +1 -0
  128. sage/matrix/matrix_symbolic_dense.cpython-314t-aarch64-linux-musl.so +0 -0
  129. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  130. sage/matrix/matrix_symbolic_dense.pyx +1030 -0
  131. sage/matrix/matrix_symbolic_sparse.cpython-314t-aarch64-linux-musl.so +0 -0
  132. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  133. sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
  134. sage/modules/all__sagemath_symbolics.py +1 -0
  135. sage/modules/vector_callable_symbolic_dense.py +105 -0
  136. sage/modules/vector_symbolic_dense.py +116 -0
  137. sage/modules/vector_symbolic_sparse.py +118 -0
  138. sage/rings/all__sagemath_symbolics.py +4 -0
  139. sage/rings/asymptotic/all.py +6 -0
  140. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  141. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  142. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
  143. sage/rings/asymptotic/growth_group.py +5373 -0
  144. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  145. sage/rings/asymptotic/term_monoid.py +5205 -0
  146. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  147. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  148. sage/symbolic/all.py +15 -0
  149. sage/symbolic/assumptions.py +987 -0
  150. sage/symbolic/benchmark.py +93 -0
  151. sage/symbolic/callable.py +456 -0
  152. sage/symbolic/callable.pyi +66 -0
  153. sage/symbolic/comparison_impl.pyi +38 -0
  154. sage/symbolic/complexity_measures.py +35 -0
  155. sage/symbolic/constants.py +1286 -0
  156. sage/symbolic/constants_c_impl.pyi +10 -0
  157. sage/symbolic/expression_conversion_algebraic.py +310 -0
  158. sage/symbolic/expression_conversion_sympy.py +317 -0
  159. sage/symbolic/expression_conversions.py +1727 -0
  160. sage/symbolic/function_factory.py +355 -0
  161. sage/symbolic/function_factory.pyi +41 -0
  162. sage/symbolic/getitem_impl.pyi +24 -0
  163. sage/symbolic/integration/all.py +1 -0
  164. sage/symbolic/integration/external.py +271 -0
  165. sage/symbolic/integration/integral.py +1075 -0
  166. sage/symbolic/maxima_wrapper.py +162 -0
  167. sage/symbolic/operators.py +267 -0
  168. sage/symbolic/operators.pyi +61 -0
  169. sage/symbolic/pynac_constant_impl.pyi +13 -0
  170. sage/symbolic/pynac_function_impl.pyi +8 -0
  171. sage/symbolic/random_tests.py +461 -0
  172. sage/symbolic/relation.py +2062 -0
  173. sage/symbolic/ring.cpython-314t-aarch64-linux-musl.so +0 -0
  174. sage/symbolic/ring.pxd +5 -0
  175. sage/symbolic/ring.pyi +110 -0
  176. sage/symbolic/ring.pyx +1393 -0
  177. sage/symbolic/series_impl.pyi +10 -0
  178. sage/symbolic/subring.py +1025 -0
  179. sage/symbolic/symengine.py +19 -0
  180. sage/symbolic/tests.py +40 -0
  181. sage/symbolic/units.py +1468 -0
@@ -0,0 +1,1839 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Pseudo-Riemannian submanifolds
4
+
5
+ An *embedded (resp. immersed) submanifold of a pseudo-Riemannian manifold*
6
+ `(M,g)` is an embedded (resp. immersed) submanifold `N` of `M` as a
7
+ differentiable manifold (see
8
+ :mod:`~sage.manifolds.differentiable.differentiable_submanifold`) such that
9
+ pull back of the metric tensor `g` via the embedding (resp. immersion) endows
10
+ `N` with the structure of a pseudo-Riemannian manifold.
11
+
12
+ The following example shows how to compute the various quantities related
13
+ to the intrinsic and extrinsic geometries of a hyperbolic slicing of the
14
+ 3-dimensional Minkowski space.
15
+
16
+ We start by declaring the ambient manifold `M` and the submanifold `N`::
17
+
18
+ sage: M = Manifold(3, 'M', structure='Lorentzian')
19
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian', start_index=1)
20
+
21
+ The considered slices being spacelike hypersurfaces, they are Riemannian
22
+ manifolds.
23
+
24
+ Let us introduce the Minkowskian coordinates `(w,x,y)` on `M` and the polar
25
+ coordinates `(\rho, \theta)` on the submanifold `N`::
26
+
27
+ sage: E.<w,x,y> = M.chart()
28
+ sage: C.<rh,th> = N.chart(r'rh:(0,+oo):\rho th:(0,2*pi):\theta')
29
+
30
+ Let `b` be the hyperbola semi-major axis and `t` the parameter of the
31
+ foliation::
32
+
33
+ sage: b = var('b', domain='real')
34
+ sage: assume(b>0)
35
+ sage: t = var('t', domain='real')
36
+
37
+ One can then define the embedding `\phi_t`::
38
+
39
+ sage: phi = N.diff_map(M, {(C,E): [b*cosh(rh)+t,
40
+ ....: b*sinh(rh)*cos(th),
41
+ ....: b*sinh(rh)*sin(th)]})
42
+ sage: phi.display()
43
+ N → M
44
+ (rh, th) ↦ (w, x, y) = (b*cosh(rh) + t, b*cos(th)*sinh(rh),
45
+ b*sin(th)*sinh(rh))
46
+
47
+ as well as its inverse (when considered as a diffeomorphism onto its image)::
48
+
49
+ sage: phi_inv = M.diff_map(N, {(E,C): [log(sqrt(x^2+y^2+b^2)/b+
50
+ ....: sqrt((x^2+y^2+b^2)/b^2-1)),
51
+ ....: atan2(y,x)]})
52
+ sage: phi_inv.display()
53
+ M → N
54
+ (w, x, y) ↦ (rh, th) = (log(sqrt((b^2 + x^2 + y^2)/b^2 - 1)
55
+ + sqrt(b^2 + x^2 + y^2)/b), arctan2(y, x))
56
+
57
+ and the partial inverse expressing the foliation parameter `t` as a scalar
58
+ field on `M`::
59
+
60
+ sage: phi_inv_t = M.scalar_field({E: w-sqrt(x^2+y^2+b^2)})
61
+ sage: phi_inv_t.display()
62
+ M → ℝ
63
+ (w, x, y) ↦ w - sqrt(b^2 + x^2 + y^2)
64
+
65
+ One can check that the inverse is correct with::
66
+
67
+ sage: (phi*phi_inv).display()
68
+ M → M
69
+ (w, x, y) ↦ ((b^2 + x^2 + y^2 + sqrt(b^2 + x^2 + y^2)*(t + sqrt(x^2 +
70
+ y^2)) + sqrt(x^2 + y^2)*t)/(sqrt(b^2 + x^2 + y^2) + sqrt(x^2 + y^2)), x, y)
71
+
72
+ The first item of the 3-uple in the right-hand does not appear as `w` because
73
+ `t` has not been replaced by its value provided by ``phi_inv_t``. Once this is
74
+ done, we do get `w`::
75
+
76
+ sage: (phi*phi_inv).expr()[0].subs({t: phi_inv_t.expr()}).simplify_full()
77
+ w
78
+
79
+ The embedding can then be declared::
80
+
81
+ sage: N.set_embedding(phi, inverse=phi_inv, var=t,
82
+ ....: t_inverse = {t: phi_inv_t})
83
+
84
+ This line does not perform any calculation yet. It just check the coherence of
85
+ the arguments, but not the inverse, the user is trusted on this point.
86
+
87
+ Finally, we initialize the metric of `M` to be that of Minkowski space::
88
+
89
+ sage: g = M.metric()
90
+ sage: g[0,0], g[1,1], g[2,2] = -1, 1, 1
91
+ sage: g.display()
92
+ g = -dw⊗dw + dx⊗dx + dy⊗dy
93
+
94
+ With this, the declaration the ambient manifold and its foliation parametrized
95
+ by `t` is finished, and calculations can be performed.
96
+
97
+ The first step is always to find a chart adapted to the foliation. This is done
98
+ by the method "adapted_chart"::
99
+
100
+ sage: T = N.adapted_chart(); T
101
+ [Chart (M, (rh_M, th_M, t_M))]
102
+
103
+ ``T`` contains a new chart defined on `M`. By default, the coordinate names
104
+ are constructed from the names of the submanifold coordinates and the foliation
105
+ parameter indexed by the name of the ambient manifold. By this can be
106
+ customized, see
107
+ :meth:`~sage.manifolds.topological_submanifold.TopologicalSubmanifold.adapted_chart`.
108
+
109
+ One can check that the adapted chart has been added to `M`'s atlas, along with
110
+ some coordinates changes::
111
+
112
+ sage: M.atlas()
113
+ [Chart (M, (w, x, y)), Chart (M, (rh_M, th_M, t_M))]
114
+ sage: len(M.coord_changes())
115
+ 2
116
+
117
+ Let us compute the induced metric (or first fundamental form)::
118
+
119
+ sage: # long time
120
+ sage: gamma = N.induced_metric()
121
+ sage: gamma.display()
122
+ gamma = b^2 drh⊗drh + b^2*sinh(rh)^2 dth⊗dth
123
+ sage: gamma[:]
124
+ [ b^2 0]
125
+ [ 0 b^2*sinh(rh)^2]
126
+ sage: gamma[1,1]
127
+ b^2
128
+
129
+ the normal vector::
130
+
131
+ sage: N.normal().display() # long time
132
+ n = sqrt(b^2 + x^2 + y^2)/b ∂/∂w + x/b ∂/∂x + y/b ∂/∂y
133
+
134
+ Check that the hypersurface is indeed spacelike, i.e. that its normal is
135
+ timelike::
136
+
137
+ sage: N.ambient_metric()(N.normal(), N.normal()).display() # long time
138
+ g(n,n): M → ℝ
139
+ (w, x, y) ↦ -1
140
+ (rh_M, th_M, t_M) ↦ -1
141
+
142
+ The lapse function is::
143
+
144
+ sage: N.lapse().display() # long time
145
+ N: M → ℝ
146
+ (w, x, y) ↦ sqrt(b^2 + x^2 + y^2)/b
147
+ (rh_M, th_M, t_M) ↦ cosh(rh_M)
148
+
149
+ while the shift vector is::
150
+
151
+ sage: N.shift().display() # long time
152
+ beta = -(x^2 + y^2)/b^2 ∂/∂w - sqrt(b^2 + x^2 + y^2)*x/b^2 ∂/∂x
153
+ - sqrt(b^2 + x^2 + y^2)*y/b^2 ∂/∂y
154
+
155
+ The extrinsic curvature (or second fundamental form) as a tensor field on the
156
+ ambient manifold::
157
+
158
+ sage: N.ambient_extrinsic_curvature()[:] # long time
159
+ [ -(x^2 + y^2)/b^3 (b^2*x + x^3 + x*y^2)/(sqrt(b^2 + x^2 + y^2)*b^3) (y^3 + (b^2 + x^2)*y)/(sqrt(b^2 + x^2 + y^2)*b^3)]
160
+ [ sqrt(b^2 + x^2 + y^2)*x/b^3 -(b^2 + x^2)/b^3 -x*y/b^3]
161
+ [ sqrt(b^2 + x^2 + y^2)*y/b^3 -x*y/b^3 -(b^2 + y^2)/b^3]
162
+
163
+ The extrinsic curvature as a tensor field on the submanifold::
164
+
165
+ sage: N.extrinsic_curvature()[:] # long time
166
+ [ -b 0]
167
+ [ 0 -b*sinh(rh)^2]
168
+
169
+
170
+ AUTHORS:
171
+
172
+ - Florentin Jaffredo (2018): initial version
173
+ - Eric Gourgoulhon (2018-2019): add documentation
174
+ - Matthias Koeppe (2021): open subsets of submanifolds
175
+
176
+ REFERENCES:
177
+
178
+ - \B. O'Neill : *Semi-Riemannian Geometry* [ONe1983]_
179
+ - \J. M. Lee : *Riemannian Manifolds* [Lee1997]_
180
+ """
181
+
182
+ # *****************************************************************************
183
+ # Copyright (C) 2018 Florentin Jaffredo <florentin.jaffredo@polytechnique.edu>
184
+ # Copyright (C) 2018-2019 Eric Gourgoulhon <eric.gourgoulhon@obspm.fr>
185
+ # Copyright (C) 2021 Matthias Koeppe <mkoeppe@math.ucdavis.edu>
186
+ #
187
+ # This program is free software: you can redistribute it and/or modify
188
+ # it under the terms of the GNU General Public License as published by
189
+ # the Free Software Foundation, either version 2 of the License, or
190
+ # (at your option) any later version.
191
+ # http://www.gnu.org/licenses/
192
+ # *****************************************************************************
193
+
194
+ from queue import Queue
195
+
196
+ from sage.functions.other import factorial
197
+ from sage.manifolds.differentiable.degenerate import DegenerateManifold
198
+ from sage.manifolds.differentiable.differentiable_submanifold import (
199
+ DifferentiableSubmanifold,
200
+ )
201
+ from sage.manifolds.differentiable.pseudo_riemannian import PseudoRiemannianManifold
202
+ from sage.matrix.constructor import matrix
203
+ from sage.misc.cachefunc import cached_method
204
+ from sage.rings.infinity import infinity
205
+ from sage.rings.integer import Integer
206
+ from sage.symbolic.ring import SR
207
+
208
+
209
+ class PseudoRiemannianSubmanifold(PseudoRiemannianManifold,
210
+ DifferentiableSubmanifold):
211
+ r"""
212
+ Pseudo-Riemannian submanifold.
213
+
214
+ An *embedded (resp. immersed) submanifold of a pseudo-Riemannian manifold*
215
+ `(M,g)` is an embedded (resp. immersed) submanifold `N` of `M` as a
216
+ differentiable manifold such that pull back of the metric tensor `g` via
217
+ the embedding (resp. immersion) endows `N` with the structure of a
218
+ pseudo-Riemannian manifold.
219
+
220
+ INPUT:
221
+
222
+ - ``n`` -- positive integer; dimension of the submanifold
223
+ - ``name`` -- string; name (symbol) given to the submanifold
224
+ - ``ambient`` -- (default: ``None``) pseudo-Riemannian manifold `M` in
225
+ which the submanifold is embedded (or immersed). If ``None``, it is set
226
+ to ``self``
227
+ - ``metric_name`` -- (default: ``None``) string; name (symbol) given to the
228
+ metric; if ``None``, ``'gamma'`` is used
229
+ - ``signature`` -- (default: ``None``) signature `S` of the metric as a
230
+ single integer: `S = n_+ - n_-`, where `n_+` (resp. `n_-`) is the
231
+ number of positive terms (resp. number of negative terms) in any
232
+ diagonal writing of the metric components; if ``signature`` is not
233
+ provided, `S` is set to the submanifold's dimension (Riemannian
234
+ signature)
235
+ - ``base_manifold`` -- (default: ``None``) if not ``None``, must be a
236
+ differentiable manifold; the created object is then an open subset of
237
+ ``base_manifold``
238
+ - ``diff_degree`` -- (default: ``infinity``) degree of differentiability
239
+ - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to
240
+ denote the submanifold; if none is provided, it is set to ``name``
241
+ - ``metric_latex_name`` -- (default: ``None``) string; LaTeX symbol to
242
+ denote the metric; if none is provided, it is set to ``metric_name`` if
243
+ the latter is not ``None`` and to ``r'\gamma'`` otherwise
244
+ - ``start_index`` -- (default: 0) integer; lower value of the range of
245
+ indices used for "indexed objects" on the submanifold, e.g. coordinates
246
+ in a chart
247
+ - ``category`` -- (default: ``None``) to specify the category; if ``None``,
248
+ ``Manifolds(RR).Differentiable()`` (or ``Manifolds(RR).Smooth()``
249
+ if ``diff_degree`` = ``infinity``) is assumed (see the category
250
+ :class:`~sage.categories.manifolds.Manifolds`)
251
+ - ``unique_tag`` -- (default: ``None``) tag used to force the construction
252
+ of a new object when all the other arguments have been used previously
253
+ (without ``unique_tag``, the
254
+ :class:`~sage.structure.unique_representation.UniqueRepresentation`
255
+ behavior inherited from
256
+ :class:`~sage.manifolds.subset.ManifoldSubset`, via
257
+ :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`
258
+ and :class:`~sage.manifolds.manifold.TopologicalManifold`,
259
+ would return the previously constructed object corresponding to these
260
+ arguments).
261
+
262
+ EXAMPLES:
263
+
264
+ Let `N` be a 2-dimensional submanifold of a 3-dimensional Riemannian
265
+ manifold `M`::
266
+
267
+ sage: M = Manifold(3, 'M', structure ='Riemannian')
268
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
269
+ sage: N
270
+ 2-dimensional Riemannian submanifold N immersed in the 3-dimensional
271
+ Riemannian manifold M
272
+ sage: CM.<x,y,z> = M.chart()
273
+ sage: CN.<u,v> = N.chart()
274
+
275
+ Let us define a 1-dimension foliation indexed by `t`. The inverse map is
276
+ needed in order to compute the adapted chart in the ambient manifold::
277
+
278
+ sage: t = var('t')
279
+ sage: phi = N.diff_map(M, {(CN,CM):[u, v, t+u^2+v^2]}); phi
280
+ Differentiable map from the 2-dimensional Riemannian submanifold N
281
+ immersed in the 3-dimensional Riemannian manifold M to the
282
+ 3-dimensional Riemannian manifold M
283
+ sage: phi_inv = M.diff_map(N,{(CM, CN): [x,y]})
284
+ sage: phi_inv_t = M.scalar_field({CM: z-x^2-y^2})
285
+
286
+ `\phi` can then be declared as an embedding `N\to M`::
287
+
288
+ sage: N.set_embedding(phi, inverse=phi_inv, var=t,
289
+ ....: t_inverse={t: phi_inv_t})
290
+
291
+ The foliation can also be used to find new charts on the ambient manifold
292
+ that are adapted to the foliation, ie in which the expression of the
293
+ immersion is trivial. At the same time, the appropriate coordinate changes
294
+ are computed::
295
+
296
+ sage: N.adapted_chart()
297
+ [Chart (M, (u_M, v_M, t_M))]
298
+ sage: len(M.coord_changes())
299
+ 2
300
+
301
+ .. SEEALSO::
302
+
303
+ :mod:`~sage.manifolds.manifold` and
304
+ :mod:`~sage.manifolds.differentiable.differentiable_submanifold`
305
+ """
306
+ def __init__(self, n, name, ambient=None, metric_name=None,
307
+ signature=None, base_manifold=None, diff_degree=infinity,
308
+ latex_name=None, metric_latex_name=None, start_index=0,
309
+ category=None, unique_tag=None):
310
+ r"""
311
+ Construct a pseudo-Riemannian submanifold.
312
+
313
+ TESTS::
314
+
315
+ sage: M = Manifold(3, 'M', structure='Lorentzian')
316
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
317
+ sage: N
318
+ 2-dimensional Riemannian submanifold N immersed in the
319
+ 3-dimensional Lorentzian manifold M
320
+ sage: phi = N.diff_map(M)
321
+ sage: N.set_embedding(phi)
322
+ sage: N
323
+ 2-dimensional Riemannian submanifold N embedded in the
324
+ 3-dimensional Lorentzian manifold M
325
+ sage: S = Manifold(2, 'S', latex_name=r"\Sigma", ambient=M,
326
+ ....: structure='Riemannian', start_index=1)
327
+ sage: latex(S)
328
+ \Sigma
329
+ sage: S.start_index()
330
+ 1
331
+
332
+ ::
333
+
334
+ sage: M = Manifold(5, 'M', structure='pseudo-Riemannian',
335
+ ....: signature=1)
336
+ sage: N = Manifold(4, 'N', ambient=M,
337
+ ....: structure='pseudo-Riemannian', signature=0)
338
+ sage: N
339
+ 4-dimensional pseudo-Riemannian submanifold N immersed in the
340
+ 5-dimensional pseudo-Riemannian manifold M
341
+ """
342
+ if metric_name is None:
343
+ metric_name = 'gamma'
344
+ metric_latex_name = r'\gamma'
345
+ PseudoRiemannianManifold.__init__(self, n, name=name,
346
+ metric_name=metric_name,
347
+ signature=signature,
348
+ base_manifold=base_manifold,
349
+ diff_degree=diff_degree,
350
+ latex_name=latex_name,
351
+ metric_latex_name=metric_latex_name,
352
+ start_index=start_index,
353
+ category=category)
354
+ if not (ambient is None
355
+ or isinstance(ambient, (PseudoRiemannianManifold, DegenerateManifold))):
356
+ raise TypeError("ambient must be a pseudo-Riemannian manifold")
357
+ self._init_immersion(ambient=ambient)
358
+ self._difft = None
359
+ self._gradt = None
360
+ self._normal = None
361
+ self._lapse = None
362
+ self._shift = None
363
+ self._first_fundamental_form = None
364
+ self._ambient_first_fundamental_form = None
365
+ self._second_fundamental_form = None
366
+ self._ambient_second_fundamental_form = None
367
+ self._ambient_metric = None
368
+ self._projector = None
369
+ self._gauss_curvature = None
370
+ self._principal_directions = {}
371
+ self._principal_curvatures = {}
372
+ self._mean_curvature = None
373
+ self._shape_operator = None
374
+ self._sgn = 1 if ambient._structure.name == "Riemannian" else -1
375
+
376
+ def _repr_(self):
377
+ r"""
378
+ Return a string representation of the submanifold.
379
+
380
+ If no ambient manifold is specified, the submanifold is considered
381
+ as a manifold.
382
+
383
+ TESTS::
384
+
385
+ sage: M = Manifold(3, 'M', structure='Lorentzian')
386
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
387
+ sage: N
388
+ 2-dimensional Riemannian submanifold N immersed in the
389
+ 3-dimensional Lorentzian manifold M
390
+ sage: phi = N.diff_map(M)
391
+ sage: N.set_embedding(phi)
392
+ sage: N
393
+ 2-dimensional Riemannian submanifold N embedded in the
394
+ 3-dimensional Lorentzian manifold M
395
+ """
396
+ if self is not self._manifold:
397
+ return "Open subset {} of the {}".format(self._name, self._manifold)
398
+ if self._ambient is None:
399
+ return super(PseudoRiemannianManifold, self).__repr__()
400
+ if self._embedded:
401
+ return "{}-dimensional {} submanifold {} embedded in the {}".format(
402
+ self._dim, self._structure.name, self._name, self._ambient)
403
+ return "{}-dimensional {} submanifold {} immersed in the {}".format(
404
+ self._dim, self._structure.name, self._name, self._ambient)
405
+
406
+ def open_subset(self, name, latex_name=None, coord_def={}, supersets=None):
407
+ r"""
408
+ Create an open subset of ``self``.
409
+
410
+ An open subset is a set that is (i) included in the manifold and (ii)
411
+ open with respect to the manifold's topology. It is a differentiable
412
+ manifold by itself. Moreover, equipped with the restriction of the
413
+ manifold metric to itself, it is a pseudo-Riemannian manifold.
414
+
415
+ As ``self`` is a submanifold of its ambient manifold,
416
+ the new open subset is also considered a submanifold of that.
417
+ Hence the returned object is an instance of
418
+ :class:`PseudoRiemannianSubmanifold`.
419
+
420
+ INPUT:
421
+
422
+ - ``name`` -- name given to the open subset
423
+ - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the
424
+ subset; if none is provided, it is set to ``name``
425
+ - ``coord_def`` -- (default: {}) definition of the subset in
426
+ terms of coordinates; ``coord_def`` must a be dictionary with keys
427
+ charts in the manifold's atlas and values the symbolic expressions
428
+ formed by the coordinates to define the subset.
429
+ - ``supersets`` -- (default: only ``self``) list of sets that the
430
+ new open subset is a subset of
431
+
432
+ OUTPUT:
433
+
434
+ - instance of :class:`PseudoRiemannianSubmanifold` representing the
435
+ created open subset
436
+
437
+ EXAMPLES::
438
+
439
+ sage: M = Manifold(3, 'M', structure='Riemannian')
440
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian'); N
441
+ 2-dimensional Riemannian submanifold N immersed in the
442
+ 3-dimensional Riemannian manifold M
443
+ sage: S = N.subset('S'); S
444
+ Subset S of the
445
+ 2-dimensional Riemannian submanifold N immersed in the
446
+ 3-dimensional Riemannian manifold M
447
+ sage: O = N.subset('O', is_open=True); O # indirect doctest
448
+ Open subset O of the
449
+ 2-dimensional Riemannian submanifold N immersed in the
450
+ 3-dimensional Riemannian manifold M
451
+
452
+ sage: phi = N.diff_map(M)
453
+ sage: N.set_embedding(phi)
454
+ sage: N
455
+ 2-dimensional Riemannian submanifold N embedded in the
456
+ 3-dimensional Riemannian manifold M
457
+ sage: S = N.subset('S'); S
458
+ Subset S of the
459
+ 2-dimensional Riemannian submanifold N embedded in the
460
+ 3-dimensional Riemannian manifold M
461
+ sage: O = N.subset('O', is_open=True); O # indirect doctest
462
+ Open subset O of the
463
+ 2-dimensional Riemannian submanifold N embedded in the
464
+ 3-dimensional Riemannian manifold M
465
+ """
466
+ resu = PseudoRiemannianSubmanifold(self._dim, name,
467
+ ambient=self._ambient,
468
+ metric_name=self._metric_name,
469
+ signature=self._metric_signature,
470
+ base_manifold=self._manifold,
471
+ diff_degree=self._diff_degree,
472
+ latex_name=latex_name,
473
+ metric_latex_name=self._metric_latex_name,
474
+ start_index=self._sindex)
475
+ if supersets is None:
476
+ supersets = [self]
477
+ for superset in supersets:
478
+ superset._init_open_subset(resu, coord_def=coord_def)
479
+ return resu
480
+
481
+ def ambient_metric(self):
482
+ r"""
483
+ Return the metric of the ambient manifold.
484
+
485
+ OUTPUT: the metric of the ambient manifold
486
+
487
+ EXAMPLES::
488
+
489
+ sage: M.<x,y,z> = EuclideanSpace()
490
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
491
+ sage: N.ambient_metric()
492
+ Riemannian metric g on the Euclidean space E^3
493
+ sage: N.ambient_metric().display()
494
+ g = dx⊗dx + dy⊗dy + dz⊗dz
495
+ sage: N.ambient_metric() is M.metric()
496
+ True
497
+ """
498
+ if self._ambient_metric is None:
499
+ self._ambient_metric = self._ambient.metric()
500
+ return self._ambient_metric
501
+
502
+ def first_fundamental_form(self):
503
+ r"""
504
+ Return the first fundamental form of the submanifold.
505
+
506
+ The result is cached, so calling this method multiple times always
507
+ returns the same result at no additional cost.
508
+
509
+ OUTPUT:
510
+
511
+ - the first fundamental form, as an instance of
512
+ :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric`
513
+
514
+ EXAMPLES:
515
+
516
+ A sphere embedded in Euclidean space::
517
+
518
+ sage: M.<x,y,z> = EuclideanSpace()
519
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
520
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
521
+ sage: r = var('r', domain='real')
522
+ sage: assume(r>0)
523
+ sage: E = M.cartesian_coordinates()
524
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
525
+ ....: r*sin(th)*sin(ph),
526
+ ....: r*cos(th)]})
527
+ sage: N.set_embedding(phi)
528
+ sage: N.first_fundamental_form() # long time
529
+ Riemannian metric gamma on the 2-dimensional Riemannian
530
+ submanifold N embedded in the Euclidean space E^3
531
+ sage: N.first_fundamental_form()[:] # long time
532
+ [ r^2 0]
533
+ [ 0 r^2*sin(th)^2]
534
+
535
+ An alias is ``induced_metric``::
536
+
537
+ sage: N.induced_metric()[:] # long time
538
+ [ r^2 0]
539
+ [ 0 r^2*sin(th)^2]
540
+
541
+ By default, the first fundamental form is named ``gamma``, but this
542
+ can be customized by means of the argument ``metric_name`` when
543
+ declaring the submanifold::
544
+
545
+ sage: P = Manifold(1, 'P', ambient=M, structure='Riemannian',
546
+ ....: metric_name='g')
547
+ sage: CP.<t> = P.chart()
548
+ sage: F = P.diff_map(M, [t, 2*t, 3*t])
549
+ sage: P.set_embedding(F)
550
+ sage: P.induced_metric()
551
+ Riemannian metric g on the 1-dimensional Riemannian submanifold P
552
+ embedded in the Euclidean space E^3
553
+ sage: P.induced_metric().display()
554
+ g = 14 dt⊗dt
555
+ """
556
+ if self._first_fundamental_form is None:
557
+ self._first_fundamental_form = super().metric()
558
+ self._first_fundamental_form.set(
559
+ self._immersion.pullback(self.ambient_metric()))
560
+ return self._first_fundamental_form
561
+
562
+ induced_metric = first_fundamental_form
563
+
564
+ def metric(self, name=None, signature=None, latex_name=None,
565
+ dest_map=None):
566
+ r"""
567
+ Return the induced metric (first fundamental form) or define a new
568
+ metric tensor on the submanifold.
569
+
570
+ A new (uninitialized) metric is returned only if the argument ``name``
571
+ is provided and differs from the metric name declared at the
572
+ construction of the submanifold; otherwise, the first fundamental
573
+ form is returned.
574
+
575
+ INPUT:
576
+
577
+ - ``name`` -- (default: ``None``) name given to the metric; if ``name``
578
+ is ``None`` or equals the metric name declared when constructing
579
+ the submanifold, the first fundamental form is returned (see
580
+ :meth:`first_fundamental_form`)
581
+ - ``signature`` -- (default: ``None``; ignored if ``name`` is ``None``)
582
+ signature `S` of the metric as a single integer: `S = n_+ - n_-`,
583
+ where `n_+` (resp. `n_-`) is the number of positive terms (resp.
584
+ number of negative terms) in any diagonal writing of the metric
585
+ components; if ``signature`` is not provided, `S` is set to the
586
+ submanifold's dimension (Riemannian signature)
587
+ - ``latex_name`` -- (default: ``None``; ignored if ``name`` is ``None``)
588
+ LaTeX symbol to denote the metric; if ``None``, it is formed from
589
+ ``name``
590
+ - ``dest_map`` -- (default: ``None``; ignored if ``name`` is ``None``)
591
+ instance of
592
+ class :class:`~sage.manifolds.differentiable.diff_map.DiffMap`
593
+ representing the destination map `\Phi:\ U \rightarrow M`, where `U`
594
+ is the current submanifold; if ``None``, the identity map is assumed
595
+ (case of a metric tensor field *on* `U`)
596
+
597
+ OUTPUT:
598
+
599
+ - instance of
600
+ :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric`
601
+
602
+ EXAMPLES:
603
+
604
+ Induced metric on a straight line of the Euclidean plane::
605
+
606
+ sage: M.<x,y> = EuclideanSpace()
607
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
608
+ sage: CN.<t> = N.chart()
609
+ sage: F = N.diff_map(M, [t, 2*t])
610
+ sage: N.set_embedding(F)
611
+ sage: N.metric()
612
+ Riemannian metric gamma on the 1-dimensional Riemannian
613
+ submanifold N embedded in the Euclidean plane E^2
614
+ sage: N.metric().display()
615
+ gamma = 5 dt⊗dt
616
+
617
+ Setting the argument ``name`` to that declared while constructing
618
+ the submanifold (default: ``'gamma'``) yields the same result::
619
+
620
+ sage: N.metric(name='gamma') is N.metric()
621
+ True
622
+
623
+ while using a different name allows one to define a new metric on the
624
+ submanifold::
625
+
626
+ sage: h = N.metric(name='h'); h
627
+ Riemannian metric h on the 1-dimensional Riemannian submanifold N
628
+ embedded in the Euclidean plane E^2
629
+ sage: h[0, 0] = 1 # initialization
630
+ sage: h.display()
631
+ h = dt⊗dt
632
+ """
633
+ if name is None or name == self._metric_name:
634
+ return self.first_fundamental_form()
635
+ return super().metric(name=name, signature=signature,
636
+ latex_name=latex_name, dest_map=dest_map)
637
+
638
+ @cached_method
639
+ def difft(self):
640
+ r"""
641
+ Return the differential of the scalar field on the ambient manifold
642
+ representing the first parameter of the foliation associated to
643
+ ``self``.
644
+
645
+ The result is cached, so calling this method multiple times always
646
+ returns the same result at no additional cost.
647
+
648
+ OUTPUT: 1-form field on the ambient manifold
649
+
650
+ EXAMPLES:
651
+
652
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
653
+ radii::
654
+
655
+ sage: M.<x,y,z> = EuclideanSpace()
656
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
657
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
658
+ sage: r = var('r', domain='real')
659
+ sage: assume(r>0)
660
+ sage: E = M.cartesian_coordinates()
661
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
662
+ ....: r*sin(th)*sin(ph),
663
+ ....: r*cos(th)]})
664
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
665
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
666
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
667
+ ....: t_inverse={r: phi_inv_r})
668
+ sage: N.difft()
669
+ 1-form dr on the Euclidean space E^3
670
+ sage: N.difft().display()
671
+ dr = x/sqrt(x^2 + y^2 + z^2) dx + y/sqrt(x^2 + y^2 + z^2) dy +
672
+ z/sqrt(x^2 + y^2 + z^2) dz
673
+ """
674
+ if self._dim_foliation == 0:
675
+ raise ValueError("A foliation is needed to "
676
+ "perform this calculation")
677
+ self._difft = self._t_inverse[self._var[0]].differential()
678
+ self._difft.set_name("d" + self._var[0]._repr_(),
679
+ r"\mathrm{d}" + self._var[0]._latex_())
680
+ return self._difft
681
+
682
+ @cached_method
683
+ def gradt(self):
684
+ r"""
685
+ Return the gradient of the scalar field on the ambient manifold
686
+ representing the first parameter of the foliation associated to
687
+ ``self``.
688
+
689
+ The result is cached, so calling this method multiple times always
690
+ returns the same result at no additional cost.
691
+
692
+ OUTPUT: vector field on the ambient manifold
693
+
694
+ EXAMPLES:
695
+
696
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
697
+ radii::
698
+
699
+ sage: M.<x,y,z> = EuclideanSpace()
700
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
701
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
702
+ sage: r = var('r', domain='real')
703
+ sage: assume(r>0)
704
+ sage: E = M.cartesian_coordinates()
705
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
706
+ ....: r*sin(th)*sin(ph),
707
+ ....: r*cos(th)]})
708
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
709
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
710
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
711
+ ....: t_inverse={r: phi_inv_r})
712
+ sage: N.gradt()
713
+ Vector field grad(r) on the Euclidean space E^3
714
+ sage: N.gradt().display()
715
+ grad(r) = x/sqrt(x^2 + y^2 + z^2) e_x + y/sqrt(x^2 + y^2 + z^2) e_y
716
+ + z/sqrt(x^2 + y^2 + z^2) e_z
717
+ """
718
+ if self._dim_foliation == 0:
719
+ raise ValueError("A foliation is needed to perform "
720
+ "this calculation")
721
+ param = self._var[0]
722
+ self._gradt = self._t_inverse[param].gradient()
723
+ self._gradt.set_name("grad({})".format(param),
724
+ r"\mathrm{grad}\left(" + param._latex_()
725
+ + r"\right)")
726
+ return self._gradt
727
+
728
+ @cached_method
729
+ def normal(self):
730
+ r"""
731
+ Return a normal unit vector to the submanifold.
732
+
733
+ If a foliation is defined, it is used to compute the gradient of the
734
+ foliation parameter and then the normal vector. If not, the normal
735
+ vector is computed using the following formula:
736
+
737
+ .. MATH::
738
+
739
+ n = \vec{*}(\mathrm{d}x_0\wedge\mathrm{d}x_1\wedge\cdots
740
+ \wedge\mathrm{d}x_{n-1})
741
+
742
+ where the star stands for the Hodge dual operator and the wedge for the
743
+ exterior product.
744
+
745
+ This formula does not always define a proper vector field when
746
+ multiple charts overlap, because of the arbitrariness of the direction
747
+ of the normal vector. To avoid this problem, the method ``normal()``
748
+ considers the graph defined by the atlas of the submanifold and the
749
+ changes of coordinates, and only calculate the normal vector once by
750
+ connected component. The expression is then propagate by restriction,
751
+ continuation, or change of coordinates using a breadth-first
752
+ exploration of the graph.
753
+
754
+ The result is cached, so calling this method multiple times always
755
+ returns the same result at no additional cost.
756
+
757
+ OUTPUT:
758
+
759
+ - vector field on the ambient manifold (case of a foliation) or along
760
+ the submanifold with values in the ambient manifold (case of a
761
+ single submanifold)
762
+
763
+ EXAMPLES:
764
+
765
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
766
+ radii::
767
+
768
+ sage: M.<x,y,z> = EuclideanSpace()
769
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
770
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
771
+ sage: r = var('r', domain='real') # foliation parameter
772
+ sage: assume(r>0)
773
+ sage: E = M.cartesian_coordinates()
774
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
775
+ ....: r*sin(th)*sin(ph),
776
+ ....: r*cos(th)]})
777
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
778
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
779
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
780
+ ....: t_inverse={r: phi_inv_r})
781
+ sage: T = N.adapted_chart()
782
+ sage: N.normal() # long time
783
+ Vector field n on the Euclidean space E^3
784
+ sage: N.normal().display() # long time
785
+ n = x/sqrt(x^2 + y^2 + z^2) e_x + y/sqrt(x^2 + y^2 + z^2) e_y
786
+ + z/sqrt(x^2 + y^2 + z^2) e_z
787
+
788
+ Or in spherical coordinates::
789
+
790
+ sage: N.normal().display(T[0].frame(),T[0]) # long time
791
+ n = ∂/∂r_E3
792
+
793
+ Let us now consider a sphere of constant radius, i.e. not assumed to be
794
+ part of a foliation, in stereographic coordinates::
795
+
796
+ sage: M.<X,Y,Z> = EuclideanSpace()
797
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
798
+ sage: U = N.open_subset('U')
799
+ sage: V = N.open_subset('V')
800
+ sage: N.declare_union(U, V)
801
+ sage: stereoN.<x,y> = U.chart()
802
+ sage: stereoS.<xp,yp> = V.chart("xp:x' yp:y'")
803
+ sage: stereoN_to_S = stereoN.transition_map(stereoS,
804
+ ....: (x/(x^2+y^2), y/(x^2+y^2)),
805
+ ....: intersection_name='W',
806
+ ....: restrictions1= x^2+y^2!=0,
807
+ ....: restrictions2= xp^2+yp^2!=0)
808
+ sage: stereoS_to_N = stereoN_to_S.inverse()
809
+ sage: W = U.intersection(V)
810
+ sage: stereoN_W = stereoN.restrict(W)
811
+ sage: stereoS_W = stereoS.restrict(W)
812
+ sage: A = W.open_subset('A', coord_def={stereoN_W: (y!=0, x<0),
813
+ ....: stereoS_W: (yp!=0, xp<0)})
814
+ sage: spher.<the,phi> = A.chart(r'the:(0,pi):\theta phi:(0,2*pi):\phi')
815
+ sage: stereoN_A = stereoN_W.restrict(A)
816
+ sage: spher_to_stereoN = spher.transition_map(stereoN_A,
817
+ ....: (sin(the)*cos(phi)/(1-cos(the)),
818
+ ....: sin(the)*sin(phi)/(1-cos(the))))
819
+ sage: spher_to_stereoN.set_inverse(2*atan(1/sqrt(x^2+y^2)),
820
+ ....: atan2(-y,-x)+pi)
821
+ Check of the inverse coordinate transformation:
822
+ the == 2*arctan(sqrt(-cos(the) + 1)/sqrt(cos(the) + 1)) **failed**
823
+ phi == pi + arctan2(sin(phi)*sin(the)/(cos(the) - 1),
824
+ cos(phi)*sin(the)/(cos(the) - 1)) **failed**
825
+ x == x *passed*
826
+ y == y *passed*
827
+ NB: a failed report can reflect a mere lack of simplification.
828
+ sage: stereoN_to_S_A = stereoN_to_S.restrict(A)
829
+ sage: spher_to_stereoS = stereoN_to_S_A * spher_to_stereoN
830
+ sage: stereoS_to_N_A = stereoN_to_S.inverse().restrict(A)
831
+ sage: stereoS_to_spher = spher_to_stereoN.inverse() * stereoS_to_N_A
832
+ sage: E = M.cartesian_coordinates()
833
+ sage: phi = N.diff_map(M, {(stereoN, E): [2*x/(1+x^2+y^2),
834
+ ....: 2*y/(1+x^2+y^2),
835
+ ....: (x^2+y^2-1)/(1+x^2+y^2)],
836
+ ....: (stereoS, E): [2*xp/(1+xp^2+yp^2),
837
+ ....: 2*yp/(1+xp^2+yp^2),
838
+ ....: (1-xp^2-yp^2)/(1+xp^2+yp^2)]},
839
+ ....: name='Phi', latex_name=r'\Phi')
840
+ sage: N.set_embedding(phi)
841
+
842
+ The method ``normal()`` now returns a tensor field along ``N``::
843
+
844
+ sage: n = N.normal() # long time
845
+ sage: n # long time
846
+ Vector field n along the 2-dimensional Riemannian submanifold N
847
+ embedded in the Euclidean space E^3 with values on the Euclidean
848
+ space E^3
849
+
850
+ Let us check that the choice of orientation is coherent on the two top
851
+ frames::
852
+
853
+ sage: n.restrict(V).display(format_spec=spher) # long time
854
+ n = -cos(phi)*sin(the) e_X - sin(phi)*sin(the) e_Y - cos(the) e_Z
855
+ sage: n.restrict(U).display(format_spec=spher) # long time
856
+ n = -cos(phi)*sin(the) e_X - sin(phi)*sin(the) e_Y - cos(the) e_Z
857
+ """
858
+ if self._dim_foliation != 0: # case of a foliation
859
+ self._normal = self._sgn * self.lapse() * self.gradt()
860
+ self._normal.set_name("n")
861
+ return self._normal
862
+ # case of no foliation:
863
+ max_frame = self._ambient.default_frame().along(self._immersion)
864
+ self._normal = self.multivector_field(self._ambient._dim - self._dim,
865
+ name='n',
866
+ dest_map=self._immersion)
867
+
868
+ # an auxiliary function:
869
+ def calc_normal(chart):
870
+ """
871
+ Calculate the normal vector field according to the formula in the
872
+ documentation in a given chart.
873
+ """
874
+ eps = self.ambient_metric().volume_form(self._dim).along(
875
+ self._immersion).restrict(chart.domain())
876
+ args = list(range(self._dim)) + [eps] + list(range(self._dim))
877
+ r = self.irange()
878
+ n_form = self._immersion.restrict(chart.domain()).pushforward(
879
+ chart.frame()[next(r)]).down(
880
+ self.ambient_metric().along(self._immersion).restrict(
881
+ chart.domain()))
882
+ for i in r:
883
+ n_form = n_form.wedge(
884
+ self._immersion.restrict(chart.domain()).pushforward(
885
+ chart.frame()[i]).down(
886
+ self.ambient_metric().along(
887
+ self._immersion).restrict(
888
+ chart.domain())))
889
+ n_comp = (n_form.contract(*args) / factorial(self._dim)).contract(
890
+ self.ambient_metric().inverse().along(self._immersion))
891
+ if self._ambient._dim - self._dim == 1:
892
+ n_comp = n_comp / n_comp.norm(self.ambient_metric())
893
+
894
+ norm_rst = self._normal.restrict(chart.domain())
895
+ norm_rst.add_comp(max_frame.restrict(chart.domain()))[:] = n_comp[:]
896
+ self._normal.add_comp_by_continuation(max_frame, chart.domain(),
897
+ chart)
898
+
899
+ # start breadth-first graph exploration
900
+ marked = set()
901
+ f = Queue()
902
+
903
+ for v in self.top_charts():
904
+ if v not in marked:
905
+ f.put(v)
906
+ calc_normal(v) # initial calculus
907
+ marked.add(v)
908
+ while not f.empty():
909
+ v = f.get()
910
+ # for each neighbor:
911
+ for vp in self.atlas():
912
+ # case restriction
913
+ if vp in v._subcharts and vp not in marked:
914
+ f.put(vp)
915
+ self._normal.restrict(vp.domain())
916
+ marked.add(vp)
917
+
918
+ # case continuation
919
+ if vp in v._supercharts and vp not in marked:
920
+ f.put(vp)
921
+ self._normal.add_comp_by_continuation(
922
+ max_frame.restrict(vp.domain()), v.domain(), vp)
923
+ marked.add(vp)
924
+
925
+ # case coordinates change
926
+ if (v, vp) in self.coord_changes() and vp not in marked:
927
+ f.put(vp)
928
+ self._normal.comp(max_frame, vp)
929
+ marked.add(vp)
930
+
931
+ # Going up from each top_chart to the full manifold :
932
+ for v in self.top_charts():
933
+ self._normal.add_expr_from_subdomain(max_frame, v.domain())
934
+
935
+ return self._normal
936
+
937
+ def ambient_first_fundamental_form(self):
938
+ r"""
939
+ Return the first fundamental form of the submanifold as a tensor of the
940
+ ambient manifold.
941
+
942
+ The result is cached, so calling this method multiple times always
943
+ returns the same result at no additional cost.
944
+
945
+ OUTPUT:
946
+
947
+ - (0,2) tensor field on the ambient manifold describing the induced
948
+ metric before projection on the submanifold
949
+
950
+ EXAMPLES:
951
+
952
+ A unit circle embedded in the Euclidean plane::
953
+
954
+ sage: M.<X,Y> = EuclideanSpace()
955
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
956
+ sage: U = N.open_subset('U')
957
+ sage: V = N.open_subset('V')
958
+ sage: N.declare_union(U,V)
959
+ sage: stereoN.<x> = U.chart()
960
+ sage: stereoS.<y> = V.chart()
961
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
962
+ ....: intersection_name='W',
963
+ ....: restrictions1=x!=0, restrictions2=y!=0)
964
+ sage: stereoS_to_N = stereoN_to_S.inverse()
965
+ sage: E = M.cartesian_coordinates()
966
+ sage: phi = N.diff_map(M,
967
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
968
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
969
+ sage: N.set_embedding(phi)
970
+ sage: N.ambient_first_fundamental_form()
971
+ Tensor field gamma of type (0,2) along the 1-dimensional Riemannian
972
+ submanifold N embedded in the Euclidean plane E^2 with values on
973
+ the Euclidean plane E^2
974
+ sage: N.ambient_first_fundamental_form()[:]
975
+ [ x^2/(x^2 + 4) -2*x/(x^2 + 4)]
976
+ [-2*x/(x^2 + 4) 4/(x^2 + 4)]
977
+
978
+ An alias is ``ambient_induced_metric``::
979
+
980
+ sage: N.ambient_induced_metric()[:]
981
+ [ x^2/(x^2 + 4) -2*x/(x^2 + 4)]
982
+ [-2*x/(x^2 + 4) 4/(x^2 + 4)]
983
+ """
984
+ if self._ambient._dim - self._dim != 1:
985
+ raise NotImplementedError("ambient_first_fundamental_form() is "
986
+ "implemented only for hypersurfaces")
987
+ if self._ambient_first_fundamental_form is None:
988
+ g = self.ambient_metric()
989
+ if self._dim_foliation == 0: # case no foliation
990
+ g = g.along(self._immersion)
991
+ self._ambient_first_fundamental_form = g - self._sgn * g.contract(
992
+ self.normal()) * g.contract(self.normal())
993
+ self._ambient_first_fundamental_form.set_name("gamma", r"\gamma")
994
+ return self._ambient_first_fundamental_form
995
+
996
+ ambient_induced_metric = ambient_first_fundamental_form
997
+
998
+ @cached_method
999
+ def lapse(self):
1000
+ r"""
1001
+ Return the lapse function of the foliation.
1002
+
1003
+ The result is cached, so calling this method multiple times always
1004
+ returns the same result at no additional cost.
1005
+
1006
+ OUTPUT: the lapse function, as a scalar field on the ambient manifold
1007
+
1008
+ EXAMPLES:
1009
+
1010
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
1011
+ radii::
1012
+
1013
+ sage: M.<x,y,z> = EuclideanSpace()
1014
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
1015
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
1016
+ sage: r = var('r', domain='real') # foliation parameter
1017
+ sage: assume(r>0)
1018
+ sage: E = M.cartesian_coordinates()
1019
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
1020
+ ....: r*sin(th)*sin(ph),
1021
+ ....: r*cos(th)]})
1022
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
1023
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
1024
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
1025
+ ....: t_inverse={r: phi_inv_r})
1026
+ sage: T = N.adapted_chart()
1027
+ sage: N.lapse()
1028
+ Scalar field N on the Euclidean space E^3
1029
+ sage: N.lapse().display()
1030
+ N: E^3 → ℝ
1031
+ (x, y, z) ↦ 1
1032
+ (th_E3, ph_E3, r_E3) ↦ 1
1033
+ """
1034
+ if self._dim_foliation == 0:
1035
+ raise ValueError("A foliation is needed "
1036
+ "to perform this calculation")
1037
+ self._lapse = 1 / (self._sgn * self.ambient_metric()(
1038
+ self.gradt(), self.gradt())).sqrt()
1039
+ self._lapse.set_name("N")
1040
+ return self._lapse
1041
+
1042
+ @cached_method
1043
+ def shift(self):
1044
+ r"""
1045
+ Return the shift vector associated with the first adapted chart of the
1046
+ foliation.
1047
+
1048
+ The result is cached, so calling this method multiple times always
1049
+ returns the same result at no additional cost.
1050
+
1051
+ OUTPUT: shift vector field on the ambient manifold
1052
+
1053
+ EXAMPLES:
1054
+
1055
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
1056
+ radii::
1057
+
1058
+ sage: M.<x,y,z> = EuclideanSpace()
1059
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
1060
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
1061
+ sage: r = var('r', domain='real') # foliation parameter
1062
+ sage: assume(r>0)
1063
+ sage: E = M.cartesian_coordinates()
1064
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
1065
+ ....: r*sin(th)*sin(ph),
1066
+ ....: r*cos(th)]})
1067
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
1068
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
1069
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
1070
+ ....: t_inverse={r: phi_inv_r})
1071
+ sage: T = N.adapted_chart()
1072
+ sage: N.shift() # long time
1073
+ Vector field beta on the Euclidean space E^3
1074
+ sage: N.shift().display() # long time
1075
+ beta = 0
1076
+ """
1077
+ if self._dim_foliation == 0:
1078
+ raise ValueError("A foliation is needed "
1079
+ "to perform this calculation")
1080
+ sia = self._ambient._sindex
1081
+ self._shift = self._adapted_charts[0].frame()[self._dim + sia]\
1082
+ - self.lapse() * self.normal()
1083
+ self._shift.set_name("beta", r"\beta")
1084
+ return self._shift
1085
+
1086
+ def ambient_second_fundamental_form(self):
1087
+ r"""
1088
+ Return the second fundamental form of the submanifold as a tensor field
1089
+ on the ambient manifold.
1090
+
1091
+ The result is cached, so calling this method multiple times always
1092
+ returns the same result at no additional cost.
1093
+
1094
+ OUTPUT:
1095
+
1096
+ - (0,2) tensor field on the ambient manifold equal to the second
1097
+ fundamental form once orthogonally projected onto the submanifold
1098
+
1099
+ EXAMPLES:
1100
+
1101
+ A unit circle embedded in the Euclidean plane::
1102
+
1103
+ sage: M.<X,Y> = EuclideanSpace()
1104
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1105
+ sage: U = N.open_subset('U')
1106
+ sage: V = N.open_subset('V')
1107
+ sage: N.declare_union(U,V)
1108
+ sage: stereoN.<x> = U.chart()
1109
+ sage: stereoS.<y> = V.chart()
1110
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1111
+ ....: intersection_name='W',
1112
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1113
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1114
+ sage: E = M.cartesian_coordinates()
1115
+ sage: phi = N.diff_map(M,
1116
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1117
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1118
+ sage: N.set_embedding(phi)
1119
+ sage: N.ambient_second_fundamental_form() # long time
1120
+ Field of symmetric bilinear forms K along the 1-dimensional
1121
+ Riemannian submanifold N embedded in the Euclidean plane E^2 with
1122
+ values on the Euclidean plane E^2
1123
+ sage: N.ambient_second_fundamental_form()[:] # long time
1124
+ [-x^2/(x^2 + 4) 2*x/(x^2 + 4)]
1125
+ [ 2*x/(x^2 + 4) -4/(x^2 + 4)]
1126
+
1127
+ An alias is ``ambient_extrinsic_curvature``::
1128
+
1129
+ sage: N.ambient_extrinsic_curvature()[:] # long time
1130
+ [-x^2/(x^2 + 4) 2*x/(x^2 + 4)]
1131
+ [ 2*x/(x^2 + 4) -4/(x^2 + 4)]
1132
+ """
1133
+ if self._ambient._dim - self._dim != 1:
1134
+ raise ValueError("ambient_second_fundamental_form is defined only "
1135
+ "for hypersurfaces")
1136
+ if self._ambient_second_fundamental_form is None:
1137
+ if self._dim_foliation == 0:
1138
+ self._ambient_second_fundamental_form = self.tensor_field(0, 2,
1139
+ sym=[(0, 1)], dest_map=self._immersion)
1140
+ k = self.second_fundamental_form()
1141
+ g = self.ambient_metric().along(self._immersion)
1142
+ max_frame = self._ambient.default_frame().along(self._immersion)
1143
+ for chart in self.top_charts():
1144
+ pf = [self._immersion.restrict(chart.domain()).pushforward(
1145
+ chart.frame()[i]) for i in self.irange()]
1146
+ for i in range(self._dim):
1147
+ pf[i] = pf[i] / g(pf[i], pf[i])
1148
+ gam_rst = sum(
1149
+ g.restrict(chart.domain()).contract(pf[i]) *
1150
+ g.restrict(chart.domain()).contract(pf[j]) *
1151
+ self.scalar_field({chart: k.comp(chart.frame())[:][i, j]})
1152
+ for i in range(self._dim) for j in range(self._dim))
1153
+ gam_rst._sym = ((0, 1),)
1154
+ self._ambient_second_fundamental_form.set_restriction(gam_rst)
1155
+
1156
+ charts = iter(self.top_charts())
1157
+ self._ambient_second_fundamental_form.add_comp_by_continuation(
1158
+ max_frame, next(charts).domain())
1159
+ for chart in charts:
1160
+ self._ambient_second_fundamental_form.add_expr_from_subdomain(
1161
+ max_frame, chart.domain())
1162
+ else:
1163
+ nab = self.ambient_metric().connection('nabla', r'\nabla')
1164
+ self._ambient_second_fundamental_form = \
1165
+ -self.ambient_metric().contract(nab(self.normal())) \
1166
+ - nab(self.normal()).contract(self.normal())\
1167
+ .contract(self.ambient_metric())\
1168
+ * self.normal().contract(self.ambient_metric())
1169
+ self._ambient_second_fundamental_form.set_name("K")
1170
+ return self._ambient_second_fundamental_form
1171
+
1172
+ ambient_extrinsic_curvature = ambient_second_fundamental_form
1173
+
1174
+ def second_fundamental_form(self):
1175
+ r"""
1176
+ Return the second fundamental form of the submanifold.
1177
+
1178
+ The result is cached, so calling this method multiple times always
1179
+ returns the same result at no additional cost.
1180
+
1181
+ OUTPUT:
1182
+
1183
+ - the second fundamental form, as a symmetric tensor field of type
1184
+ (0,2) on the submanifold
1185
+
1186
+ EXAMPLES:
1187
+
1188
+ A unit circle embedded in the Euclidean plane::
1189
+
1190
+ sage: M.<X,Y> = EuclideanSpace()
1191
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1192
+ sage: U = N.open_subset('U')
1193
+ sage: V = N.open_subset('V')
1194
+ sage: N.declare_union(U,V)
1195
+ sage: stereoN.<x> = U.chart()
1196
+ sage: stereoS.<y> = V.chart()
1197
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1198
+ ....: intersection_name='W',
1199
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1200
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1201
+ sage: E = M.cartesian_coordinates()
1202
+ sage: phi = N.diff_map(M,
1203
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1204
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1205
+ sage: N.set_embedding(phi)
1206
+ sage: N.second_fundamental_form() # long time
1207
+ Field of symmetric bilinear forms K on the 1-dimensional Riemannian
1208
+ submanifold N embedded in the Euclidean plane E^2
1209
+ sage: N.second_fundamental_form().display() # long time
1210
+ K = -4/(x^4 + 8*x^2 + 16) dx⊗dx
1211
+
1212
+ An alias is ``extrinsic_curvature``::
1213
+
1214
+ sage: N.extrinsic_curvature().display() # long time
1215
+ K = -4/(x^4 + 8*x^2 + 16) dx⊗dx
1216
+
1217
+ An example with a non-Euclidean ambient metric::
1218
+
1219
+ sage: M = Manifold(2, 'M', structure='Riemannian')
1220
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian',
1221
+ ....: start_index=1)
1222
+ sage: CM.<x,y> = M.chart()
1223
+ sage: CN.<u> = N.chart()
1224
+ sage: g = M.metric()
1225
+ sage: g[0, 0], g[1, 1] = 1, 1/(1 + y^2)^2
1226
+ sage: phi = N.diff_map(M, (u, u))
1227
+ sage: N.set_embedding(phi)
1228
+ sage: N.second_fundamental_form()
1229
+ Field of symmetric bilinear forms K on the 1-dimensional Riemannian
1230
+ submanifold N embedded in the 2-dimensional Riemannian manifold M
1231
+ sage: N.second_fundamental_form().display()
1232
+ K = 2*sqrt(u^4 + 2*u^2 + 2)*u/(u^6 + 3*u^4 + 4*u^2 + 2) du⊗du
1233
+ """
1234
+ if self._ambient._dim - self._dim != 1:
1235
+ raise ValueError("second_fundamental_form is defined only for"
1236
+ + " hypersurfaces")
1237
+ if self._second_fundamental_form is None:
1238
+ resu = self.vector_field_module().tensor((0, 2), name='K',
1239
+ sym=[(0, 1)])
1240
+ if self._dim_foliation != 0:
1241
+ inverse_subs = {v: k for k, v in self._subs[0].items()}
1242
+ asff = self.ambient_second_fundamental_form()
1243
+ adapted_chart = self._adapted_charts[0]
1244
+ dsi = self._ambient._sindex - self._sindex
1245
+ for i in self.irange():
1246
+ for j in self.irange(start=i):
1247
+ resu[i, j] = asff[adapted_chart.frame(),
1248
+ i + dsi, j + dsi,
1249
+ adapted_chart].expr().subs(inverse_subs)
1250
+ else:
1251
+ nab = self.ambient_metric().connection('nabla', r'\nabla')
1252
+ n = self.normal()
1253
+
1254
+ for chart in self.atlas():
1255
+ gamma_n = matrix(SR, self._dim + 1, self._dim + 1)
1256
+ subs = dict(zip(self._ambient.default_chart()[:],
1257
+ self._immersion.expression(chart)))
1258
+ for i in range(self._dim + 1):
1259
+ for j in range(self._dim + 1):
1260
+ Gam_ij = [nab[self._ambient.frames()[0],
1261
+ :][i][j][k].expr().subs(subs)
1262
+ for k in range(self._dim + 1)]
1263
+ gamma_n[i, j] = chart.simplify(sum(
1264
+ Gam_ij[k] *
1265
+ n.restrict(chart.domain()).comp(
1266
+ n.restrict(chart.domain())._fmodule.bases()[0])
1267
+ [:][k].expr() for k in range(self._dim + 1)))
1268
+ dXdu = self._immersion.differential_functions(chart)
1269
+ dNdu = matrix(SR, self._dim + 1, self._dim)
1270
+ for i in range(self._dim + 1):
1271
+ for j in range(self._dim):
1272
+ dNdu[i, j] = n.restrict(chart.domain()).comp(
1273
+ n.restrict(chart.domain())._fmodule.bases()[0])[:,
1274
+ chart][i].diff(chart[:][j]).expr()
1275
+ g = self.ambient_metric().along(
1276
+ self._immersion.restrict(chart.domain())).restrict(
1277
+ chart.domain())[:, chart]
1278
+ K = dXdu.transpose() * g * (dNdu + gamma_n * dXdu)
1279
+ si = self._sindex
1280
+ for i in self.irange():
1281
+ for j in self.irange(i): # since K is symmetric
1282
+ resu[chart.frame(), i, j, chart] = chart.simplify(
1283
+ K[i - si, j - si].expr())
1284
+
1285
+ self._second_fundamental_form = resu
1286
+ return self._second_fundamental_form
1287
+
1288
+ extrinsic_curvature = second_fundamental_form
1289
+
1290
+ @cached_method
1291
+ def projector(self):
1292
+ r"""
1293
+ Return the orthogonal projector onto the submanifold.
1294
+
1295
+ The result is cached, so calling this method multiple times always
1296
+ returns the same result at no additional cost.
1297
+
1298
+ OUTPUT:
1299
+
1300
+ - the orthogonal projector onto the submanifold, as tensor field of
1301
+ type (1,1) on the ambient manifold
1302
+
1303
+ EXAMPLES:
1304
+
1305
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
1306
+ radii::
1307
+
1308
+ sage: M.<x,y,z> = EuclideanSpace()
1309
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
1310
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
1311
+ sage: r = var('r', domain='real') # foliation parameter
1312
+ sage: assume(r>0)
1313
+ sage: E = M.cartesian_coordinates()
1314
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
1315
+ ....: r*sin(th)*sin(ph),
1316
+ ....: r*cos(th)]})
1317
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
1318
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
1319
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
1320
+ ....: t_inverse={r: phi_inv_r})
1321
+ sage: T = N.adapted_chart()
1322
+
1323
+ The orthogonal projector onto ``N`` is a type-(1,1) tensor field on
1324
+ ``M``::
1325
+
1326
+ sage: N.projector() # long time
1327
+ Tensor field gamma of type (1,1) on the Euclidean space E^3
1328
+
1329
+ Check that the orthogonal projector applied to the normal vector is
1330
+ zero::
1331
+
1332
+ sage: N.projector().contract(N.normal()).display() # long time
1333
+ 0
1334
+ """
1335
+ if self._ambient._dim - self._dim != 1:
1336
+ raise NotImplementedError("projector() is implemented only for "
1337
+ "hypersurfaces")
1338
+ g = self.ambient_metric().inverse()
1339
+ if self._dim_foliation == 0:
1340
+ g = g.along(self._immersion)
1341
+
1342
+ self._projector = self.ambient_first_fundamental_form().contract(0, g)
1343
+ self._projector.set_name("gamma", r"\vec{\gamma}")
1344
+ return self._projector
1345
+
1346
+ def project(self, tensor):
1347
+ r"""
1348
+ Return the orthogonal projection of a tensor field onto the submanifold.
1349
+
1350
+ INPUT:
1351
+
1352
+ - ``tensor`` -- any tensor field to be projected onto the submanifold.
1353
+ If no foliation is provided, must be a tensor field along the
1354
+ submanifold.
1355
+
1356
+ OUTPUT:
1357
+
1358
+ - orthogonal projection of ``tensor`` onto the submanifold, as a
1359
+ tensor field of the *ambient* manifold
1360
+
1361
+ EXAMPLES:
1362
+
1363
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
1364
+ radii::
1365
+
1366
+ sage: M.<x,y,z> = EuclideanSpace()
1367
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
1368
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
1369
+ sage: r = var('r', domain='real') # foliation parameter
1370
+ sage: assume(r>0)
1371
+ sage: E = M.cartesian_coordinates()
1372
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
1373
+ ....: r*sin(th)*sin(ph),
1374
+ ....: r*cos(th)]})
1375
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
1376
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
1377
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
1378
+ ....: t_inverse={r: phi_inv_r})
1379
+ sage: T = N.adapted_chart()
1380
+
1381
+ Let us perform the projection of the ambient metric and check that it
1382
+ is equal to the first fundamental form::
1383
+
1384
+ sage: pg = N.project(M.metric()); pg # long time
1385
+ Tensor field of type (0,2) on the Euclidean space E^3
1386
+ sage: pg == N.ambient_first_fundamental_form() # long time
1387
+ True
1388
+
1389
+ Note that the output of ``project()`` is not cached.
1390
+ """
1391
+ if self._ambient._dim - self._dim != 1:
1392
+ raise NotImplementedError("project() is implemented only for "
1393
+ "hypersurfaces")
1394
+ resu = tensor.copy()
1395
+ resu.set_name(tensor._name + "_" + self._name,
1396
+ r"{" + tensor._latex_() + r"}_{" + self._latex_() + r"}")
1397
+ for i in range(tensor.tensor_type()[0]):
1398
+ resu = self.projector().contract(1, resu, i)
1399
+ for i in range(tensor.tensor_type()[1]):
1400
+ resu = self.projector().contract(0, resu, i)
1401
+ return resu
1402
+
1403
+ def mixed_projection(self, tensor, indices=0):
1404
+ r"""
1405
+ Return de n+1 decomposition of a tensor on the submanifold and the
1406
+ normal vector.
1407
+
1408
+ The n+1 decomposition of a tensor of rank `k` can be obtained by
1409
+ contracting each index either with the normal vector or the projection
1410
+ operator of the submanifold (see
1411
+ :meth:`~sage.manifolds.differentiable.pseudo_riemannian_submanifold.PseudoRiemannianSubmanifold.projector`).
1412
+
1413
+ INPUT:
1414
+
1415
+ - ``tensor`` -- any tensor field, eventually along the submanifold if
1416
+ no foliation is provided
1417
+ - ``indices`` -- (default: ``0``) list of integers containing the
1418
+ indices on which the projection is made on the normal vector.
1419
+ By default, all projections are made on the submanifold. If
1420
+ an integer `n` is provided, the `n` first contractions are made with
1421
+ the normal vector, all the other ones with the orthogonal projection
1422
+ operator.
1423
+
1424
+ OUTPUT: tensor field of rank `k`-``len(indices)``
1425
+
1426
+ EXAMPLES:
1427
+
1428
+ Foliation of the Euclidean 3-space by 2-spheres parametrized by their
1429
+ radii::
1430
+
1431
+ sage: M.<x,y,z> = EuclideanSpace()
1432
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
1433
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
1434
+ sage: r = var('r', domain='real') # foliation parameter
1435
+ sage: assume(r>0)
1436
+ sage: E = M.cartesian_coordinates()
1437
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
1438
+ ....: r*sin(th)*sin(ph),
1439
+ ....: r*cos(th)]})
1440
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
1441
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
1442
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
1443
+ ....: t_inverse={r: phi_inv_r})
1444
+ sage: T = N.adapted_chart()
1445
+
1446
+ If ``indices`` is not specified, the mixed projection of the ambient
1447
+ metric coincides with the first fundamental form::
1448
+
1449
+ sage: g = M.metric()
1450
+ sage: gpp = N.mixed_projection(g); gpp # long time
1451
+ Tensor field of type (0,2) on the Euclidean space E^3
1452
+ sage: gpp == N.ambient_first_fundamental_form() # long time
1453
+ True
1454
+
1455
+ The other non-redundant projections are::
1456
+
1457
+ sage: gnp = N.mixed_projection(g, [0]); gnp # long time
1458
+ 1-form on the Euclidean space E^3
1459
+
1460
+ and::
1461
+
1462
+ sage: gnn = N.mixed_projection(g, [0,1]); gnn
1463
+ Scalar field on the Euclidean space E^3
1464
+
1465
+ which is constant and equal to 1 (the norm of the unit normal vector)::
1466
+
1467
+ sage: gnn.display()
1468
+ E^3 → ℝ
1469
+ (x, y, z) ↦ 1
1470
+ (th_E3, ph_E3, r_E3) ↦ 1
1471
+ """
1472
+ if self._ambient._dim - self._dim != 1:
1473
+ raise NotImplementedError("mixed_projection() is implemented only "
1474
+ "for hypersurfaces")
1475
+ if isinstance(indices, (Integer, int)):
1476
+ indices = list(range(indices))
1477
+
1478
+ if len(indices) > tensor.tensor_rank():
1479
+ raise ValueError("Too much contractions")
1480
+
1481
+ g = self.ambient_metric()
1482
+ if self._dim_foliation == 0:
1483
+ g = g.along(self._immersion)
1484
+
1485
+ multiprojector = 1
1486
+ k = tensor.tensor_rank() # order of the tensor
1487
+ kp = 2 * k - len(indices) # order of the multiprojector
1488
+ for i in range(tensor.tensor_type()[1]):
1489
+ if i in indices:
1490
+ multiprojector = multiprojector * self.normal()
1491
+ else:
1492
+ multiprojector = multiprojector * self.projector()
1493
+ for i in range(tensor.tensor_type()[0]):
1494
+ if i in indices:
1495
+ multiprojector = multiprojector * self.normal().contract(g)
1496
+ else:
1497
+ multiprojector = multiprojector * self.projector()
1498
+ args = list(range(kp - tensor.tensor_type()[0], kp)) + list(range(
1499
+ tensor.tensor_type()[1])) + [tensor] + list(range(k))
1500
+ return multiprojector.contract(*args)
1501
+
1502
+ @cached_method
1503
+ def gauss_curvature(self):
1504
+ r"""
1505
+ Return the Gauss curvature of the submanifold.
1506
+
1507
+ The *Gauss curvature* is the product or the principal curvatures, or
1508
+ equivalently the determinant of the projection operator.
1509
+
1510
+ The result is cached, so calling this method multiple times always
1511
+ returns the same result at no additional cost.
1512
+
1513
+ OUTPUT: the Gauss curvature as a scalar field on the submanifold
1514
+
1515
+ EXAMPLES:
1516
+
1517
+ A unit circle embedded in the Euclidean plane::
1518
+
1519
+ sage: M.<X,Y> = EuclideanSpace()
1520
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1521
+ sage: U = N.open_subset('U')
1522
+ sage: V = N.open_subset('V')
1523
+ sage: N.declare_union(U,V)
1524
+ sage: stereoN.<x> = U.chart()
1525
+ sage: stereoS.<y> = V.chart()
1526
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1527
+ ....: intersection_name='W',
1528
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1529
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1530
+ sage: E = M.cartesian_coordinates()
1531
+ sage: phi = N.diff_map(M,
1532
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1533
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1534
+ sage: N.set_embedding(phi)
1535
+ sage: N.gauss_curvature() # long time
1536
+ Scalar field on the 1-dimensional Riemannian submanifold N embedded
1537
+ in the Euclidean plane E^2
1538
+ sage: N.gauss_curvature().display() # long time
1539
+ N → ℝ
1540
+ on U: x ↦ -1
1541
+ on V: y ↦ -1
1542
+ """
1543
+ if self._ambient._dim - self._dim != 1:
1544
+ raise ValueError("gauss_curvature is defined only for "
1545
+ "hypersurfaces")
1546
+ a = self.shape_operator()
1547
+ self._gauss_curvature = self.scalar_field(
1548
+ {chart: a[chart.frame(), :, chart].determinant()
1549
+ for chart in self.top_charts()})
1550
+ return self._gauss_curvature
1551
+
1552
+ @cached_method
1553
+ def principal_directions(self, chart):
1554
+ r"""
1555
+ Return the principal directions of the submanifold.
1556
+
1557
+ The *principal directions* are the eigenvectors of the projection
1558
+ operator. The result is formatted as a list of pairs
1559
+ (eigenvector, eigenvalue).
1560
+
1561
+ The result is cached, so calling this method multiple times always
1562
+ returns the same result at no additional cost.
1563
+
1564
+ INPUT:
1565
+
1566
+ - ``chart`` -- chart in which the principal directions are to be
1567
+ computed
1568
+
1569
+ OUTPUT:
1570
+
1571
+ - list of pairs (vector field, scalar field) representing the
1572
+ principal directions and the associated principal curvatures
1573
+
1574
+ EXAMPLES:
1575
+
1576
+ A unit circle embedded in the Euclidean plane::
1577
+
1578
+ sage: M.<X,Y> = EuclideanSpace()
1579
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1580
+ sage: U = N.open_subset('U')
1581
+ sage: V = N.open_subset('V')
1582
+ sage: N.declare_union(U,V)
1583
+ sage: stereoN.<x> = U.chart()
1584
+ sage: stereoS.<y> = V.chart()
1585
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1586
+ ....: intersection_name='W',
1587
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1588
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1589
+ sage: E = M.cartesian_coordinates()
1590
+ sage: phi = N.diff_map(M,
1591
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1592
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1593
+ sage: N.set_embedding(phi)
1594
+ sage: N.principal_directions(stereoN) # long time
1595
+ [(Vector field e_0 on the 1-dimensional Riemannian submanifold N
1596
+ embedded in the Euclidean plane E^2, -1)]
1597
+ sage: N.principal_directions(stereoN)[0][0].display() # long time
1598
+ e_0 = ∂/∂x
1599
+ """
1600
+ if self._ambient._dim - self._dim != 1:
1601
+ raise ValueError("principal directions is defined only for "
1602
+ "hypersurfaces")
1603
+ a = self.shape_operator()
1604
+ pr_d = matrix(
1605
+ [[a[chart.frame(), :, chart][i, j].expr() for i in self.irange()]
1606
+ for j in self.irange()]).eigenvectors_right()
1607
+ res = []
1608
+ v = self.vector_field()
1609
+ counter = self.irange()
1610
+ for eigen_space in pr_d:
1611
+ for eigen_vector in eigen_space[1]:
1612
+ v[chart.frame(), :] = eigen_vector
1613
+ res.append((v.copy(), eigen_space[0]))
1614
+ res[-1][0].set_name("e_{}".format(next(counter)))
1615
+ self._principal_directions[chart] = res
1616
+ return res
1617
+
1618
+ @cached_method
1619
+ def principal_curvatures(self, chart):
1620
+ r"""
1621
+ Return the principal curvatures of the submanifold.
1622
+
1623
+ The *principal curvatures* are the eigenvalues of the projection
1624
+ operator. The resulting scalar fields are named ``k_i`` with the
1625
+ index ``i`` ranging from 0 to the submanifold dimension minus one.
1626
+
1627
+ The result is cached, so calling this method multiple times always
1628
+ returns the same result at no additional cost.
1629
+
1630
+ INPUT:
1631
+
1632
+ - ``chart`` -- chart in which the principal curvatures are to be
1633
+ computed
1634
+
1635
+ OUTPUT:
1636
+
1637
+ - the principal curvatures, as a list of scalar fields on the
1638
+ submanifold
1639
+
1640
+ EXAMPLES:
1641
+
1642
+ A unit circle embedded in the Euclidean plane::
1643
+
1644
+ sage: M.<X,Y> = EuclideanSpace()
1645
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1646
+ sage: U = N.open_subset('U')
1647
+ sage: V = N.open_subset('V')
1648
+ sage: N.declare_union(U,V)
1649
+ sage: stereoN.<x> = U.chart()
1650
+ sage: stereoS.<y> = V.chart()
1651
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1652
+ ....: intersection_name='W',
1653
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1654
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1655
+ sage: E = M.cartesian_coordinates()
1656
+ sage: phi = N.diff_map(M,
1657
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1658
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1659
+ sage: N.set_embedding(phi)
1660
+ sage: N.principal_curvatures(stereoN) # long time
1661
+ [Scalar field k_0 on the 1-dimensional Riemannian submanifold N
1662
+ embedded in the Euclidean plane E^2]
1663
+ sage: N.principal_curvatures(stereoN)[0].display() # long time
1664
+ k_0: N → ℝ
1665
+ on U: x ↦ -1
1666
+ on W: y ↦ -1
1667
+ """
1668
+ if self._ambient._dim - self._dim != 1:
1669
+ raise ValueError("principal_curvatures is defined only for "
1670
+ "hypersurfaces")
1671
+ a = self.shape_operator()
1672
+ res = matrix(
1673
+ [[a[chart.frame(), :, chart][i, j].expr() for i in self.irange()]
1674
+ for j in self.irange()]).eigenvalues()
1675
+ counter = self.irange()
1676
+ for i in range(self._dim):
1677
+ res[i] = self.scalar_field({chart: res[i]},
1678
+ name="k_{}".format(next(counter)))
1679
+ self._principal_curvatures[chart] = res
1680
+ return res
1681
+
1682
+ @cached_method
1683
+ def mean_curvature(self):
1684
+ r"""
1685
+ Return the mean curvature of the submanifold.
1686
+
1687
+ The *mean curvature* is the arithmetic mean of the principal curvatures,
1688
+ or equivalently the trace of the projection operator.
1689
+
1690
+ The result is cached, so calling this method multiple times always
1691
+ returns the same result at no additional cost.
1692
+
1693
+ OUTPUT: the mean curvature, as a scalar field on the submanifold
1694
+
1695
+ EXAMPLES:
1696
+
1697
+ A unit circle embedded in the Euclidean plane::
1698
+
1699
+ sage: M.<X,Y> = EuclideanSpace()
1700
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1701
+ sage: U = N.open_subset('U')
1702
+ sage: V = N.open_subset('V')
1703
+ sage: N.declare_union(U,V)
1704
+ sage: stereoN.<x> = U.chart()
1705
+ sage: stereoS.<y> = V.chart()
1706
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1707
+ ....: intersection_name='W',
1708
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1709
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1710
+ sage: E = M.cartesian_coordinates()
1711
+ sage: phi = N.diff_map(M,
1712
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1713
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1714
+ sage: N.set_embedding(phi)
1715
+ sage: N.mean_curvature() # long time
1716
+ Scalar field on the 1-dimensional Riemannian submanifold N
1717
+ embedded in the Euclidean plane E^2
1718
+ sage: N.mean_curvature().display() # long time
1719
+ N → ℝ
1720
+ on U: x ↦ -1
1721
+ on V: y ↦ -1
1722
+ """
1723
+ if self._ambient._dim - self._dim != 1:
1724
+ raise ValueError("mean_curvature is defined only for "
1725
+ "hypersurfaces")
1726
+ self._shape_operator = self.scalar_field({chart: self._sgn * sum(
1727
+ self.principal_curvatures(chart)).expr(chart) / self._dim
1728
+ for chart in
1729
+ self.top_charts()})
1730
+ return self._shape_operator
1731
+
1732
+ @cached_method
1733
+ def shape_operator(self):
1734
+ r"""
1735
+ Return the shape operator of the submanifold.
1736
+
1737
+ The shape operator is equal to the second fundamental form with one of
1738
+ the indices upped.
1739
+
1740
+ The result is cached, so calling this method multiple times always
1741
+ returns the same result at no additional cost.
1742
+
1743
+ OUTPUT:
1744
+
1745
+ - the shape operator, as a tensor field of type (1,1) on the
1746
+ submanifold
1747
+
1748
+ EXAMPLES:
1749
+
1750
+ A unit circle embedded in the Euclidean plane::
1751
+
1752
+ sage: M.<X,Y> = EuclideanSpace()
1753
+ sage: N = Manifold(1, 'N', ambient=M, structure='Riemannian')
1754
+ sage: U = N.open_subset('U')
1755
+ sage: V = N.open_subset('V')
1756
+ sage: N.declare_union(U,V)
1757
+ sage: stereoN.<x> = U.chart()
1758
+ sage: stereoS.<y> = V.chart()
1759
+ sage: stereoN_to_S = stereoN.transition_map(stereoS, (4/x),
1760
+ ....: intersection_name='W',
1761
+ ....: restrictions1=x!=0, restrictions2=y!=0)
1762
+ sage: stereoS_to_N = stereoN_to_S.inverse()
1763
+ sage: E = M.cartesian_coordinates()
1764
+ sage: phi = N.diff_map(M,
1765
+ ....: {(stereoN, E): [1/sqrt(1+x^2/4), x/2/sqrt(1+x^2/4)],
1766
+ ....: (stereoS, E): [1/sqrt(1+4/y^2), 2/y/sqrt(1+4/y^2)]})
1767
+ sage: N.set_embedding(phi)
1768
+ sage: N.shape_operator() # long time
1769
+ Tensor field of type (1,1) on the 1-dimensional Riemannian
1770
+ submanifold N embedded in the Euclidean plane E^2
1771
+ sage: N.shape_operator().display() # long time
1772
+ -∂/∂x⊗dx
1773
+ """
1774
+ if self._ambient._dim - self._dim != 1:
1775
+ raise ValueError("shape_operator is defined only for "
1776
+ "hypersurfaces")
1777
+ self._shape_operator = self.second_fundamental_form().contract(
1778
+ self.induced_metric().inverse())
1779
+ return self._shape_operator
1780
+
1781
+ def clear_cache(self):
1782
+ r"""
1783
+ Reset all the cached functions and the derived quantities.
1784
+
1785
+ Use this function if you modified the immersion (or embedding) of the
1786
+ submanifold. Note that when calling a calculus function after clearing,
1787
+ new Python objects will be created.
1788
+
1789
+ EXAMPLES::
1790
+
1791
+ sage: M.<x,y,z> = EuclideanSpace()
1792
+ sage: N = Manifold(2, 'N', ambient=M, structure='Riemannian')
1793
+ sage: C.<th,ph> = N.chart(r'th:(0,pi):\theta ph:(-pi,pi):\phi')
1794
+ sage: r = var('r', domain='real') # foliation parameter
1795
+ sage: assume(r>0)
1796
+ sage: E = M.cartesian_coordinates()
1797
+ sage: phi = N.diff_map(M, {(C,E): [r*sin(th)*cos(ph),
1798
+ ....: r*sin(th)*sin(ph),
1799
+ ....: r*cos(th)]})
1800
+ sage: phi_inv = M.diff_map(N, {(E,C): [arccos(z/r), atan2(y,x)]})
1801
+ sage: phi_inv_r = M.scalar_field({E: sqrt(x^2+y^2+z^2)})
1802
+ sage: N.set_embedding(phi, inverse=phi_inv, var=r,
1803
+ ....: t_inverse={r: phi_inv_r})
1804
+ sage: T = N.adapted_chart()
1805
+ sage: n = N.normal()
1806
+ sage: n is N.normal()
1807
+ True
1808
+ sage: N.clear_cache()
1809
+ sage: n is N.normal()
1810
+ False
1811
+ sage: n == N.normal()
1812
+ True
1813
+ """
1814
+ self.difft.clear_cache()
1815
+ self.gradt.clear_cache()
1816
+ self.normal.clear_cache()
1817
+ self.lapse.clear_cache()
1818
+ self.shift.clear_cache()
1819
+ self.projector.clear_cache()
1820
+ self.gauss_curvature.clear_cache()
1821
+ self.principal_directions.clear_cache()
1822
+ self.principal_curvatures.clear_cache()
1823
+ self.shape_operator.clear_cache()
1824
+ self._difft = None
1825
+ self._gradt = None
1826
+ self._normal = None
1827
+ self._lapse = None
1828
+ self._shift = None
1829
+ self._first_fundamental_form = None
1830
+ self._ambient_first_fundamental_form = None
1831
+ self._second_fundamental_form = None
1832
+ self._ambient_second_fundamental_form = None
1833
+ self._ambient_metric = None
1834
+ self._projector = None
1835
+ self._gauss_curvature = None
1836
+ self._principal_directions = {}
1837
+ self._principal_curvatures = {}
1838
+ self._mean_curvature = None
1839
+ self._shape_operator = None