passagemath-symbolics 10.6.43__cp314-cp314t-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-symbolics might be problematic. Click here for more details.

Files changed (171) hide show
  1. passagemath_symbolics/__init__.py +3 -0
  2. passagemath_symbolics-10.6.43.dist-info/METADATA +187 -0
  3. passagemath_symbolics-10.6.43.dist-info/RECORD +171 -0
  4. passagemath_symbolics-10.6.43.dist-info/WHEEL +5 -0
  5. passagemath_symbolics-10.6.43.dist-info/top_level.txt +3 -0
  6. sage/all__sagemath_symbolics.py +17 -0
  7. sage/calculus/all.py +14 -0
  8. sage/calculus/calculus.py +2826 -0
  9. sage/calculus/desolvers.py +1866 -0
  10. sage/calculus/predefined.py +51 -0
  11. sage/calculus/tests.py +225 -0
  12. sage/calculus/var.cpython-314t-x86_64-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-x86_64-linux-musl.so +0 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
  19. sage/ext/all__sagemath_symbolics.py +1 -0
  20. sage/ext_data/kenzo/CP2.txt +45 -0
  21. sage/ext_data/kenzo/CP3.txt +349 -0
  22. sage/ext_data/kenzo/CP4.txt +4774 -0
  23. sage/ext_data/kenzo/README.txt +49 -0
  24. sage/ext_data/kenzo/S4.txt +20 -0
  25. sage/ext_data/magma/latex/latex.m +1021 -0
  26. sage/ext_data/magma/latex/latex.spec +1 -0
  27. sage/ext_data/magma/sage/basic.m +356 -0
  28. sage/ext_data/magma/sage/sage.spec +1 -0
  29. sage/ext_data/magma/spec +9 -0
  30. sage/geometry/all__sagemath_symbolics.py +8 -0
  31. sage/geometry/hyperbolic_space/all.py +5 -0
  32. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  39. sage/geometry/riemannian_manifolds/all.py +7 -0
  40. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  41. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  42. sage/interfaces/all__sagemath_symbolics.py +1 -0
  43. sage/interfaces/magma.py +3017 -0
  44. sage/interfaces/magma_free.py +92 -0
  45. sage/interfaces/maple.py +1397 -0
  46. sage/interfaces/mathematica.py +1345 -0
  47. sage/interfaces/mathics.py +1312 -0
  48. sage/interfaces/sympy.py +1398 -0
  49. sage/interfaces/sympy_wrapper.py +197 -0
  50. sage/interfaces/tides.py +938 -0
  51. sage/libs/all__sagemath_symbolics.py +6 -0
  52. sage/manifolds/all.py +7 -0
  53. sage/manifolds/calculus_method.py +555 -0
  54. sage/manifolds/catalog.py +437 -0
  55. sage/manifolds/chart.py +4019 -0
  56. sage/manifolds/chart_func.py +3419 -0
  57. sage/manifolds/continuous_map.py +2183 -0
  58. sage/manifolds/continuous_map_image.py +155 -0
  59. sage/manifolds/differentiable/affine_connection.py +2475 -0
  60. sage/manifolds/differentiable/all.py +1 -0
  61. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  62. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  63. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  64. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  65. sage/manifolds/differentiable/chart.py +1241 -0
  66. sage/manifolds/differentiable/curve.py +1028 -0
  67. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  68. sage/manifolds/differentiable/degenerate.py +559 -0
  69. sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
  70. sage/manifolds/differentiable/diff_form.py +1658 -0
  71. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  72. sage/manifolds/differentiable/diff_map.py +1315 -0
  73. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  74. sage/manifolds/differentiable/examples/all.py +1 -0
  75. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  76. sage/manifolds/differentiable/examples/real_line.py +897 -0
  77. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  78. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  79. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  80. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  81. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  82. sage/manifolds/differentiable/manifold.py +4254 -0
  83. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  84. sage/manifolds/differentiable/metric.py +3032 -0
  85. sage/manifolds/differentiable/mixed_form.py +1507 -0
  86. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  87. sage/manifolds/differentiable/multivector_module.py +800 -0
  88. sage/manifolds/differentiable/multivectorfield.py +1520 -0
  89. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  90. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  91. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  92. sage/manifolds/differentiable/scalarfield.py +1343 -0
  93. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  94. sage/manifolds/differentiable/symplectic_form.py +910 -0
  95. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  96. sage/manifolds/differentiable/tangent_space.py +412 -0
  97. sage/manifolds/differentiable/tangent_vector.py +616 -0
  98. sage/manifolds/differentiable/tensorfield.py +4665 -0
  99. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  100. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  101. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  102. sage/manifolds/differentiable/vector_bundle.py +1728 -0
  103. sage/manifolds/differentiable/vectorfield.py +1717 -0
  104. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  105. sage/manifolds/differentiable/vectorframe.py +1832 -0
  106. sage/manifolds/family.py +270 -0
  107. sage/manifolds/local_frame.py +1490 -0
  108. sage/manifolds/manifold.py +3090 -0
  109. sage/manifolds/manifold_homset.py +452 -0
  110. sage/manifolds/operators.py +359 -0
  111. sage/manifolds/point.py +994 -0
  112. sage/manifolds/scalarfield.py +3718 -0
  113. sage/manifolds/scalarfield_algebra.py +629 -0
  114. sage/manifolds/section.py +3111 -0
  115. sage/manifolds/section_module.py +831 -0
  116. sage/manifolds/structure.py +229 -0
  117. sage/manifolds/subset.py +2764 -0
  118. sage/manifolds/subsets/all.py +1 -0
  119. sage/manifolds/subsets/closure.py +131 -0
  120. sage/manifolds/subsets/pullback.py +885 -0
  121. sage/manifolds/topological_submanifold.py +891 -0
  122. sage/manifolds/trivialization.py +733 -0
  123. sage/manifolds/utilities.py +1348 -0
  124. sage/manifolds/vector_bundle.py +1342 -0
  125. sage/manifolds/vector_bundle_fiber.py +332 -0
  126. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  127. sage/matrix/all__sagemath_symbolics.py +1 -0
  128. sage/matrix/matrix_symbolic_dense.cpython-314t-x86_64-linux-musl.so +0 -0
  129. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  130. sage/matrix/matrix_symbolic_dense.pyx +1022 -0
  131. sage/matrix/matrix_symbolic_sparse.cpython-314t-x86_64-linux-musl.so +0 -0
  132. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  133. sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
  134. sage/modules/all__sagemath_symbolics.py +1 -0
  135. sage/modules/vector_callable_symbolic_dense.py +105 -0
  136. sage/modules/vector_symbolic_dense.py +116 -0
  137. sage/modules/vector_symbolic_sparse.py +118 -0
  138. sage/rings/all__sagemath_symbolics.py +4 -0
  139. sage/rings/asymptotic/all.py +6 -0
  140. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  141. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  142. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
  143. sage/rings/asymptotic/growth_group.py +5373 -0
  144. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  145. sage/rings/asymptotic/term_monoid.py +5237 -0
  146. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  147. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  148. sage/symbolic/all.py +15 -0
  149. sage/symbolic/assumptions.py +985 -0
  150. sage/symbolic/benchmark.py +93 -0
  151. sage/symbolic/callable.py +459 -0
  152. sage/symbolic/complexity_measures.py +35 -0
  153. sage/symbolic/constants.py +1287 -0
  154. sage/symbolic/expression_conversion_algebraic.py +310 -0
  155. sage/symbolic/expression_conversion_sympy.py +317 -0
  156. sage/symbolic/expression_conversions.py +1713 -0
  157. sage/symbolic/function_factory.py +355 -0
  158. sage/symbolic/integration/all.py +1 -0
  159. sage/symbolic/integration/external.py +270 -0
  160. sage/symbolic/integration/integral.py +1115 -0
  161. sage/symbolic/maxima_wrapper.py +162 -0
  162. sage/symbolic/operators.py +267 -0
  163. sage/symbolic/random_tests.py +462 -0
  164. sage/symbolic/relation.py +1907 -0
  165. sage/symbolic/ring.cpython-314t-x86_64-linux-musl.so +0 -0
  166. sage/symbolic/ring.pxd +5 -0
  167. sage/symbolic/ring.pyx +1396 -0
  168. sage/symbolic/subring.py +1025 -0
  169. sage/symbolic/symengine.py +19 -0
  170. sage/symbolic/tests.py +40 -0
  171. sage/symbolic/units.py +1470 -0
@@ -0,0 +1,1632 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ """
3
+ Differential Geometry of Parametrized Surfaces
4
+
5
+ AUTHORS:
6
+
7
+ - Mikhail Malakhaltsev (2010-09-25): initial version
8
+ - Joris Vankerschaver (2010-10-25): implementation, doctests
9
+ """
10
+ # ****************************************************************************
11
+ # Copyright (C) 2010 Mikhail Malakhaltsev <mikarm@gmail.com>
12
+ # Copyright (C) 2010 Joris Vankerschaver <joris.vankerschaver@gmail.com>
13
+ #
14
+ # Distributed under the terms of the GNU General Public License (GPL)
15
+ # https://www.gnu.org/licenses/
16
+ # ****************************************************************************
17
+
18
+ from itertools import product
19
+
20
+ from sage.structure.sage_object import SageObject
21
+ from sage.modules.free_module_element import vector
22
+ from sage.matrix.constructor import matrix
23
+ from sage.calculus.functional import diff
24
+ from sage.misc.functional import sqrt
25
+ from sage.misc.cachefunc import cached_method
26
+ from sage.symbolic.ring import SR
27
+ from sage.symbolic.constants import pi
28
+
29
+
30
+ def _simplify_full_rad(f):
31
+ """
32
+ Helper function to conveniently call :meth:`simplify_full` and
33
+ :meth:`canonicalize_radical` in succession.
34
+
35
+ INPUT:
36
+
37
+ - ``f`` -- a symbolic expression
38
+
39
+ EXAMPLES::
40
+
41
+ sage: from sage.geometry.riemannian_manifolds.parametrized_surface3d import _simplify_full_rad
42
+ sage: _simplify_full_rad(sqrt(x^2)/x)
43
+ 1
44
+ """
45
+ return f.simplify_full().canonicalize_radical()
46
+
47
+
48
+ class ParametrizedSurface3D(SageObject):
49
+ r"""
50
+ Class representing a parametrized two-dimensional surface in
51
+ Euclidian three-space. Provides methods for calculating the main
52
+ geometrical objects related to such a surface, such as the first
53
+ and the second fundamental form, the total (Gaussian) and the mean
54
+ curvature, the geodesic curves, parallel transport, etc.
55
+
56
+
57
+ INPUT:
58
+
59
+ - ``surface_equation`` -- a 3-tuple of functions specifying a parametric
60
+ representation of the surface
61
+
62
+ - ``variables`` -- a 2-tuple of intrinsic coordinates `(u, v)` on the
63
+ surface, with `u` and `v` symbolic variables, or a 2-tuple of triples
64
+ `(u, u_{min}, u_{max})`, `(v, v_{min}, v_{max})` when the parameter range
65
+ for the coordinates is known
66
+
67
+ - ``name`` -- name of the surface (optional)
68
+
69
+
70
+ .. NOTE::
71
+
72
+ Throughout the documentation, we use the Einstein summation
73
+ convention: whenever an index appears twice, once as a
74
+ subscript, and once as a superscript, summation over that index
75
+ is implied. For instance, `g_{ij} g^{jk}` stands for `\sum_j
76
+ g_{ij}g^{jk}`.
77
+
78
+ EXAMPLES:
79
+
80
+ We give several examples of standard surfaces in differential
81
+ geometry. First, let's construct an elliptic paraboloid by
82
+ explicitly specifying its parametric equation::
83
+
84
+ sage: u, v = var('u,v', domain='real')
85
+ sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v),
86
+ ....: 'elliptic paraboloid'); eparaboloid
87
+ Parametrized surface ('elliptic paraboloid') with equation (u, v, u^2 + v^2)
88
+
89
+ When the ranges for the intrinsic coordinates are known, they can be
90
+ specified explicitly. This is mainly useful for plotting. Here we
91
+ construct half of an ellipsoid::
92
+
93
+ sage: u1, u2 = var ('u1, u2', domain='real')
94
+ sage: coords = ((u1, -pi/2, pi/2), (u2, 0, pi))
95
+ sage: ellipsoid_eq = (cos(u1)*cos(u2), 2*sin(u1)*cos(u2), 3*sin(u2))
96
+ sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, coords, 'ellipsoid'); ellipsoid
97
+ Parametrized surface ('ellipsoid') with equation
98
+ (cos(u1)*cos(u2), 2*cos(u2)*sin(u1), 3*sin(u2))
99
+ sage: ellipsoid.plot() # needs sage.plot
100
+ Graphics3d Object
101
+
102
+ Standard surfaces can be constructed using the ``surfaces`` generator::
103
+
104
+ sage: klein = surfaces.Klein(); klein
105
+ Parametrized surface ('Klein bottle') with equation
106
+ (-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u),
107
+ -(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u),
108
+ cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v))
109
+
110
+ Latex representation of the surfaces::
111
+
112
+ sage: u, v = var('u, v', domain='real')
113
+ sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),
114
+ ....: 'sphere')
115
+ sage: print(latex(sphere))
116
+ \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
117
+ sage: print(sphere._latex_())
118
+ \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
119
+ sage: print(sphere)
120
+ Parametrized surface ('sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v))
121
+
122
+ To plot a parametric surface, use the :meth:`plot` member function::
123
+
124
+ sage: enneper = surfaces.Enneper(); enneper
125
+ Parametrized surface ('Enneper's surface') with equation
126
+ (-1/9*(u^2 - 3*v^2 - 3)*u, -1/9*(3*u^2 - v^2 + 3)*v, 1/3*u^2 - 1/3*v^2)
127
+ sage: enneper.plot(aspect_ratio='automatic') # needs sage.plot
128
+ Graphics3d Object
129
+
130
+ We construct an ellipsoid whose axes are given by symbolic variables `a`,
131
+ `b` and `c`, and find the natural frame of tangent vectors,
132
+ expressed in intrinsic coordinates. Note that the result is a
133
+ dictionary of vector fields::
134
+
135
+ sage: a, b, c = var('a, b, c', domain='real')
136
+ sage: u1, u2 = var('u1, u2', domain='real')
137
+ sage: ellipsoid_eq = (a*cos(u1)*cos(u2), b*sin(u1)*cos(u2), c*sin(u2))
138
+ sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, (u1, u2),
139
+ ....: 'Symbolic ellipsoid'); ellipsoid
140
+ Parametrized surface ('Symbolic ellipsoid') with equation
141
+ (a*cos(u1)*cos(u2), b*cos(u2)*sin(u1), c*sin(u2))
142
+
143
+ sage: ellipsoid.natural_frame()
144
+ {1: (-a*cos(u2)*sin(u1), b*cos(u1)*cos(u2), 0),
145
+ 2: (-a*cos(u1)*sin(u2), -b*sin(u1)*sin(u2), c*cos(u2))}
146
+
147
+ We find the normal vector field to the surface. The normal vector
148
+ field is the vector product of the vectors of the natural frame,
149
+ and is given by::
150
+
151
+ sage: ellipsoid.normal_vector()
152
+ (b*c*cos(u1)*cos(u2)^2, a*c*cos(u2)^2*sin(u1), a*b*cos(u2)*sin(u2))
153
+
154
+ By default, the normal vector field is not normalized. To obtain
155
+ the unit normal vector field of the elliptic paraboloid, we put::
156
+
157
+ sage: u, v = var('u,v', domain='real')
158
+ sage: eparaboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v],
159
+ ....: 'elliptic paraboloid')
160
+ sage: eparaboloid.normal_vector(normalized=True)
161
+ (-2*u/sqrt(4*u^2 + 4*v^2 + 1), -2*v/sqrt(4*u^2 + 4*v^2 + 1), 1/sqrt(4*u^2 + 4*v^2 + 1))
162
+
163
+ Now let us compute the coefficients of the first fundamental form of the torus::
164
+
165
+ sage: u, v = var('u, v', domain='real')
166
+ sage: a, b = var('a, b', domain='real')
167
+ sage: torus = ParametrizedSurface3D(((a + b*cos(u))*cos(v),
168
+ ....: (a + b*cos(u))*sin(v),
169
+ ....: b*sin(u)), [u,v], 'torus')
170
+ sage: torus.first_fundamental_form_coefficients()
171
+ {(1, 1): b^2, (1, 2): 0, (2, 1): 0, (2, 2): b^2*cos(u)^2 + 2*a*b*cos(u) + a^2}
172
+
173
+ The first fundamental form can be used to compute the length of a
174
+ curve on the surface. For example, let us find the length of the
175
+ curve `u^1 = t`, `u^2 = t`, `t \in [0,2\pi]`, on the ellipsoid
176
+ with axes `a=1`, `b=1.5` and `c=1`. So we take the curve::
177
+
178
+ sage: t = var('t', domain='real')
179
+ sage: u1 = t
180
+ sage: u2 = t
181
+
182
+ Then find the tangent vector::
183
+
184
+ sage: du1 = diff(u1,t)
185
+ sage: du2 = diff(u2,t)
186
+ sage: du = vector([du1, du2]); du
187
+ (1, 1)
188
+
189
+ Once we specify numerical values for the axes of the ellipsoid, we can
190
+ determine the numerical value of the length integral::
191
+
192
+ sage: L = sqrt(ellipsoid.first_fundamental_form(du, du).substitute(u1=u1, u2=u2))
193
+ sage: numerical_integral(L.substitute(a=2, b=1.5, c=1),0,1)[0] # rel tol 1e-11
194
+ 2.00127905972
195
+
196
+ We find the area of the sphere of radius `R`::
197
+
198
+ sage: R = var('R', domain='real')
199
+ sage: u, v = var('u,v', domain='real')
200
+ sage: assume(R>0)
201
+ sage: assume(cos(v)>0)
202
+ sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v), R*sin(u)*cos(v), R*sin(v)],
203
+ ....: [u,v], 'sphere')
204
+ sage: integral(integral(sphere.area_form(), u, 0, 2*pi), v, -pi/2, pi/2)
205
+ 4*pi*R^2
206
+
207
+ We can find an orthonormal frame field `\{e_1, e_2\}` of a surface
208
+ and calculate its structure functions. Let us first determine the
209
+ orthonormal frame field for the elliptic paraboloid::
210
+
211
+ sage: u, v = var('u,v', domain='real')
212
+ sage: eparaboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v],
213
+ ....: 'elliptic paraboloid')
214
+ sage: eparaboloid.orthonormal_frame()
215
+ {1: (1/sqrt(4*u^2 + 1), 0, 2*u/sqrt(4*u^2 + 1)),
216
+ 2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)),
217
+ sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1),
218
+ 2*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)))}
219
+
220
+ We can express the orthogonal frame field both in exterior
221
+ coordinates (i.e. expressed as vector field fields in the ambient
222
+ space `\RR^3`, the default) or in intrinsic coordinates
223
+ (with respect to the natural frame). Here we use intrinsic
224
+ coordinates::
225
+
226
+ sage: eparaboloid.orthonormal_frame(coordinates='int')
227
+ {1: (1/sqrt(4*u^2 + 1), 0),
228
+ 2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)),
229
+ sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1))}
230
+
231
+ Using the orthonormal frame in interior coordinates, we can calculate
232
+ the structure functions `c^k_{ij}` of the surface, defined by
233
+ `[e_i,e_j] = c^k_{ij} e_k`, where `[e_i, e_j]` represents the Lie
234
+ bracket of two frame vector fields `e_i, e_j`. For the
235
+ elliptic paraboloid, we get::
236
+
237
+ sage: EE = eparaboloid.orthonormal_frame(coordinates='int')
238
+ sage: E1 = EE[1]; E2 = EE[2]
239
+ sage: CC = eparaboloid.frame_structure_functions(E1,E2)
240
+ sage: CC[1,2,1].simplify_full()
241
+ 4*sqrt(4*u^2 + 4*v^2 + 1)*v/((16*u^4 + 4*(4*u^2 + 1)*v^2 + 8*u^2 + 1)*sqrt(4*u^2 + 1))
242
+
243
+ We compute the Gaussian and mean curvatures of the sphere::
244
+
245
+ sage: sphere = surfaces.Sphere(); sphere
246
+ Parametrized surface ('Sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v))
247
+ sage: K = sphere.gauss_curvature(); K # Not tested -- see trac 12737
248
+ 1
249
+ sage: H = sphere.mean_curvature(); H # Not tested -- see trac 12737
250
+ -1
251
+
252
+ We can easily generate a color plot of the Gaussian curvature of a surface.
253
+ Here we deal with the ellipsoid::
254
+
255
+ sage: # needs numpy
256
+ sage: u1, u2 = var('u1,u2', domain='real')
257
+ sage: u = [u1,u2]
258
+ sage: ellipsoid_equation(u1,u2) = [2*cos(u1)*cos(u2),1.5*cos(u1)*sin(u2),sin(u1)]
259
+ sage: ellipsoid = ParametrizedSurface3D(ellipsoid_equation(u1,u2), [u1, u2], 'ellipsoid')
260
+ sage: # set intervals for variables and the number of division points
261
+ sage: u1min, u1max = -1.5, 1.5
262
+ sage: u2min, u2max = 0, 6.28
263
+ sage: u1num, u2num = 10, 20
264
+ sage: # make the arguments array
265
+ sage: from numpy import linspace
266
+ sage: u1_array = linspace(u1min, u1max, u1num)
267
+ sage: u2_array = linspace(u2min, u2max, u2num)
268
+ sage: u_array = [(uu1,uu2) for uu1 in u1_array for uu2 in u2_array]
269
+ sage: # Find the gaussian curvature
270
+ sage: K(u1,u2) = ellipsoid.gauss_curvature()
271
+ sage: # Make array of K values
272
+ sage: K_array = [K(uu[0],uu[1]) for uu in u_array]
273
+ sage: # Find minimum and max of the Gauss curvature
274
+ sage: K_max = max(K_array)
275
+ sage: K_min = min(K_array)
276
+ sage: # Make the array of color coefficients
277
+ sage: cc_array = [(ccc - K_min)/(K_max - K_min) for ccc in K_array]
278
+ sage: points_array = [ellipsoid_equation(u_array[counter][0],
279
+ ....: u_array[counter][1])
280
+ ....: for counter in range(len(u_array))]
281
+ sage: curvature_ellipsoid_plot = sum(point([xx # needs sage.plot
282
+ ....: for xx in points_array[counter]],
283
+ ....: color=hue(cc_array[counter]/2))
284
+ ....: for counter in range(len(u_array)))
285
+ sage: curvature_ellipsoid_plot.show(aspect_ratio=1) # needs sage.plot
286
+
287
+ We can find the principal curvatures and principal directions of the
288
+ elliptic paraboloid::
289
+
290
+ sage: u, v = var('u, v', domain='real')
291
+ sage: eparaboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v],
292
+ ....: 'elliptic paraboloid')
293
+ sage: pd = eparaboloid.principal_directions(); pd
294
+ [(2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1), [(1, v/u)], 1),
295
+ (2/sqrt(4*u^2 + 4*v^2 + 1), [(1, -u/v)], 1)]
296
+
297
+ We extract the principal curvatures::
298
+
299
+ sage: k1 = pd[0][0].simplify_full(); k1
300
+ 2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1)
301
+ sage: k2 = pd[1][0].simplify_full(); k2
302
+ 2/sqrt(4*u^2 + 4*v^2 + 1)
303
+
304
+ and check them by comparison with the Gaussian and mean curvature
305
+ expressed in terms of the principal curvatures::
306
+
307
+ sage: K = eparaboloid.gauss_curvature().simplify_full(); K
308
+ 4/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1)
309
+ sage: H = eparaboloid.mean_curvature().simplify_full(); H
310
+ 2*(2*u^2 + 2*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2)
311
+ sage: (K - k1*k2).simplify_full()
312
+ 0
313
+ sage: (2*H - k1 - k2).simplify_full()
314
+ 0
315
+
316
+ We can find the intrinsic (local coordinates) of the principal directions::
317
+
318
+ sage: pd[0][1]
319
+ [(1, v/u)]
320
+ sage: pd[1][1]
321
+ [(1, -u/v)]
322
+
323
+ The ParametrizedSurface3D class also contains functionality to
324
+ compute the coefficients of the second fundamental form, the shape
325
+ operator, the rotation on the surface at a given angle, the
326
+ connection coefficients. One can also calculate numerically the
327
+ geodesics and the parallel translation along a curve.
328
+
329
+ Here we compute a number of geodesics on the sphere emanating
330
+ from the point ``(1, 0, 0)``, in various directions. The geodesics
331
+ intersect again in the antipodal point ``(-1, 0, 0)``, indicating
332
+ that these points are conjugate::
333
+
334
+ sage: S = surfaces.Sphere()
335
+ sage: g1 = [c[-1] for c in S.geodesics_numerical((0,0), (1,0), (0,2*pi,100))]
336
+ sage: g2 = [c[-1] for c in S.geodesics_numerical((0,0),
337
+ ....: (cos(pi/3),sin(pi/3)),
338
+ ....: (0,2*pi,100))]
339
+ sage: g3 = [c[-1] for c in S.geodesics_numerical((0,0),
340
+ ....: (cos(2*pi/3),sin(2*pi/3)),
341
+ ....: (0,2*pi,100))]
342
+ sage: (S.plot(opacity=0.3) + line3d(g1, color='red') # needs sage.plot
343
+ ....: + line3d(g2, color='red') + line3d(g3, color='red')).show()
344
+ """
345
+
346
+ def __init__(self, equation, variables, name=None):
347
+ r"""
348
+ See ``ParametrizedSurface3D`` for full documentation.
349
+
350
+ .. NOTE::
351
+
352
+ The orientation of the surface is determined by the
353
+ parametrization, that is, the natural frame with positive
354
+ orientation is given by `\partial_1 \vec r`, `\partial_2 \vec
355
+ r`.
356
+
357
+ EXAMPLES::
358
+
359
+ sage: u, v = var('u,v', domain='real')
360
+ sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
361
+ sage: enneper = ParametrizedSurface3D(eq, (u, v),'Enneper Surface'); enneper
362
+ Parametrized surface ('Enneper Surface') with equation
363
+ (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)
364
+ """
365
+ self.equation = tuple(equation)
366
+
367
+ if isinstance(variables[0], (list, tuple)):
368
+ self.variables_range = (variables[0][1:3], variables[1][1:3])
369
+ self.variables_list = (variables[0][0], variables[1][0])
370
+ else:
371
+ self.variables_range = None
372
+ self.variables_list = variables
373
+
374
+ self.variables = {1:self.variables_list[0], 2:self.variables_list[1]}
375
+ self.name = name
376
+
377
+ def _latex_(self):
378
+ r"""
379
+ Return the LaTeX representation of this parametrized surface.
380
+
381
+ EXAMPLES::
382
+
383
+ sage: u, v = var('u, v')
384
+ sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere')
385
+ sage: latex(sphere)
386
+ \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
387
+ sage: sphere._latex_()
388
+ \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right)
389
+ """
390
+ from sage.misc.latex import latex
391
+ return latex(self.equation)
392
+
393
+ def _repr_(self):
394
+ r"""
395
+ Return the string representation of this parametrized surface.
396
+
397
+ EXAMPLES::
398
+
399
+ sage: u, v = var('u, v', domain='real')
400
+ sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
401
+ sage: enneper = ParametrizedSurface3D(eq,[u,v],'enneper_surface')
402
+ sage: print(enneper)
403
+ Parametrized surface ('enneper_surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)
404
+ sage: enneper._repr_()
405
+ "Parametrized surface ('enneper_surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)"
406
+ """
407
+ name = 'Parametrized surface'
408
+ if self.name is not None:
409
+ name += " ('%s')" % self.name
410
+ s = '%(designation)s with equation %(eq)s' % \
411
+ {'designation': name, 'eq': str(self.equation)}
412
+ return s
413
+
414
+ def point(self, coords):
415
+ r"""
416
+ Return a point on the surface given its intrinsic coordinates.
417
+
418
+ INPUT:
419
+
420
+ - ``coords`` -- 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point
421
+
422
+ OUTPUT: 3-vector specifying the coordinates in `\RR^3` of the point
423
+
424
+ EXAMPLES::
425
+
426
+ sage: u, v = var('u, v', domain='real')
427
+ sage: torus = ParametrizedSurface3D(((2 + cos(u))*cos(v),
428
+ ....: (2 + cos(u))*sin(v),
429
+ ....: sin(u)), [u,v], 'torus')
430
+ sage: torus.point((0, pi/2))
431
+ (0, 3, 0)
432
+ sage: torus.point((pi/2, pi))
433
+ (-2, 0, 1)
434
+ sage: torus.point((pi, pi/2))
435
+ (0, 1, 0)
436
+ """
437
+
438
+ d = dict(zip(self.variables_list, coords))
439
+ return vector([f.subs(d) for f in self.equation])
440
+
441
+ def tangent_vector(self, coords, components):
442
+ r"""
443
+ Return the components of a tangent vector given the intrinsic
444
+ coordinates of the base point and the components of the vector
445
+ in the intrinsic frame.
446
+
447
+ INPUT:
448
+
449
+ - ``coords`` -- 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point
450
+
451
+ - ``components`` -- 2-tuple specifying the components of the tangent
452
+ vector in the intrinsic coordinate frame
453
+
454
+ OUTPUT: 3-vector specifying the components in `\RR^3` of the vector
455
+
456
+ EXAMPLES:
457
+
458
+ We compute two tangent vectors to Enneper's surface along the
459
+ coordinate lines and check that their cross product gives the
460
+ normal vector::
461
+
462
+ sage: u, v = var('u,v', domain='real')
463
+ sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
464
+ sage: e = ParametrizedSurface3D(eq, (u, v), 'Enneper Surface')
465
+
466
+ sage: w1 = e.tangent_vector((1, 2), (1, 0)); w1
467
+ (12, 12, 6)
468
+ sage: w2 = e.tangent_vector((1, 2), (0, 1)); w2
469
+ (12, -6, -12)
470
+ sage: w1.cross_product(w2)
471
+ (-108, 216, -216)
472
+
473
+ sage: n = e.normal_vector().subs({u: 1, v: 2}); n
474
+ (-108, 216, -216)
475
+ sage: n == w1.cross_product(w2)
476
+ True
477
+ """
478
+
479
+ components = vector(components)
480
+ d = dict(zip(self.variables_list, coords))
481
+ jacobian = matrix([[f.diff(u).subs(d) for u in self.variables_list]
482
+ for f in self.equation])
483
+ return jacobian * components
484
+
485
+ def plot(self, urange=None, vrange=None, **kwds):
486
+ r"""
487
+ Enable easy plotting directly from the surface class.
488
+
489
+ The optional keywords ``urange`` and ``vrange`` specify the range for
490
+ the surface parameters `u` and `v`. If either of these parameters
491
+ is ``None``, the method checks whether a parameter range was
492
+ specified when the surface was created. If not, the default of
493
+ `(0, 2 \pi)` is used.
494
+
495
+ INPUT:
496
+
497
+ - ``urange`` -- 2-tuple specifying the parameter range for `u`
498
+ - ``vrange`` -- 2-tuple specifying the parameter range for `v`
499
+
500
+ EXAMPLES::
501
+
502
+ sage: u, v = var('u, v', domain='real')
503
+ sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2))
504
+ sage: enneper = ParametrizedSurface3D(eq, (u, v), 'Enneper Surface')
505
+ sage: enneper.plot((-5, 5), (-5, 5)) # needs sage.plot
506
+ Graphics3d Object
507
+ """
508
+
509
+ from sage.plot.plot3d.parametric_plot3d import parametric_plot3d
510
+
511
+ if self.variables_range is None:
512
+ if urange is None:
513
+ urange = (0, 2*pi)
514
+ if vrange is None:
515
+ vrange = (0, 2*pi)
516
+ else:
517
+ if urange is None:
518
+ urange = self.variables_range[0]
519
+ if vrange is None:
520
+ vrange = self.variables_range[1]
521
+
522
+ urange3 = (self.variables[1],) + tuple(urange)
523
+ vrange3 = (self.variables[2],) + tuple(vrange)
524
+ P = parametric_plot3d(self.equation, urange3, vrange3, **kwds)
525
+
526
+ return P
527
+
528
+ @cached_method
529
+ def natural_frame(self):
530
+ """
531
+ Return the natural tangent frame on the parametrized surface.
532
+ The vectors of this frame are tangent to the coordinate lines
533
+ on the surface.
534
+
535
+ OUTPUT: the natural frame as a dictionary
536
+
537
+ EXAMPLES::
538
+
539
+ sage: u, v = var('u, v', domain='real')
540
+ sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v),
541
+ ....: 'elliptic paraboloid')
542
+ sage: eparaboloid.natural_frame()
543
+ {1: (1, 0, 2*u), 2: (0, 1, 2*v)}
544
+ """
545
+
546
+ dr1 = \
547
+ vector([_simplify_full_rad( diff(f,self.variables[1]) )
548
+ for f in self.equation])
549
+ dr2 = \
550
+ vector([_simplify_full_rad( diff(f,self.variables[2]) )
551
+ for f in self.equation])
552
+
553
+ return {1:dr1, 2:dr2}
554
+
555
+ @cached_method
556
+ def normal_vector(self, normalized=False):
557
+ """
558
+ Return the normal vector field of the parametrized surface.
559
+
560
+ INPUT:
561
+
562
+ - ``normalized`` -- boolean (default: ``False``); specifies whether
563
+ the normal vector should be normalized
564
+
565
+ OUTPUT: normal vector field
566
+
567
+ EXAMPLES::
568
+
569
+ sage: u, v = var('u, v', domain='real')
570
+ sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v),
571
+ ....: 'elliptic paraboloid')
572
+ sage: eparaboloid.normal_vector(normalized=False)
573
+ (-2*u, -2*v, 1)
574
+ sage: eparaboloid.normal_vector(normalized=True)
575
+ (-2*u/sqrt(4*u^2 + 4*v^2 + 1),
576
+ -2*v/sqrt(4*u^2 + 4*v^2 + 1),
577
+ 1/sqrt(4*u^2 + 4*v^2 + 1))
578
+ """
579
+
580
+ dr = self.natural_frame()
581
+ normal = dr[1].cross_product(dr[2])
582
+
583
+ if normalized:
584
+ normal /= normal.norm()
585
+ return _simplify_full_rad(normal)
586
+
587
+ @cached_method
588
+ def _compute_first_fundamental_form_coefficient(self, index):
589
+ """
590
+ Helper function to compute coefficients of the first fundamental form.
591
+
592
+ Do not call this method directly; instead use
593
+ ``first_fundamental_form_coefficient``.
594
+ This method is cached, and expects its argument to be a list.
595
+
596
+ EXAMPLES::
597
+
598
+ sage: u, v = var('u, v', domain='real')
599
+ sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))
600
+ sage: eparaboloid._compute_first_fundamental_form_coefficient((1,2))
601
+ 4*u*v
602
+ """
603
+ dr = self.natural_frame()
604
+ return _simplify_full_rad(dr[index[0]]*dr[index[1]])
605
+
606
+ def first_fundamental_form_coefficient(self, index):
607
+ r"""
608
+ Compute a single component `g_{ij}` of the first fundamental form. If
609
+ the parametric representation of the surface is given by the vector
610
+ function `\vec r(u^i)`, where `u^i`, `i = 1, 2` are curvilinear
611
+ coordinates, then `g_{ij} = \frac{\partial \vec r}{\partial u^i} \cdot \frac{\partial \vec r}{\partial u^j}`.
612
+
613
+ INPUT:
614
+
615
+ - ``index`` -- tuple ``(i, j)`` specifying the index of the component `g_{ij}`
616
+
617
+ OUTPUT: component `g_{ij}` of the first fundamental form
618
+
619
+ EXAMPLES::
620
+
621
+ sage: u, v = var('u, v', domain='real')
622
+ sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))
623
+ sage: eparaboloid.first_fundamental_form_coefficient((1,2))
624
+ 4*u*v
625
+
626
+ When the index is invalid, an error is raised::
627
+
628
+ sage: u, v = var('u, v', domain='real')
629
+ sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v))
630
+ sage: eparaboloid.first_fundamental_form_coefficient((1,5))
631
+ Traceback (most recent call last):
632
+ ...
633
+ ValueError: Index (1, 5) out of bounds.
634
+ """
635
+ index = tuple(sorted(index))
636
+ if len(index) == 2 and all(i == 1 or i == 2 for i in index):
637
+ return self._compute_first_fundamental_form_coefficient(index)
638
+ else:
639
+ raise ValueError("Index %s out of bounds." % str(index))
640
+
641
+ def first_fundamental_form_coefficients(self):
642
+ r"""
643
+ Return the coefficients of the first fundamental form as a dictionary.
644
+ The keys are tuples `(i, j)`, where `i` and `j` range over `1, 2`,
645
+ while the values are the corresponding coefficients `g_{ij}`.
646
+
647
+ OUTPUT: dictionary of first fundamental form coefficients
648
+
649
+ EXAMPLES::
650
+
651
+ sage: u, v = var('u,v', domain='real')
652
+ sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v), 'sphere')
653
+ sage: sphere.first_fundamental_form_coefficients()
654
+ {(1, 1): cos(v)^2, (1, 2): 0, (2, 1): 0, (2, 2): 1}
655
+ """
656
+ coefficients = {}
657
+ for index in product((1, 2), repeat=2):
658
+ coefficients[index] = \
659
+ self._compute_first_fundamental_form_coefficient(index)
660
+ return coefficients
661
+
662
+ def first_fundamental_form(self, vector1, vector2):
663
+ r"""
664
+ Evaluate the first fundamental form on two vectors expressed with
665
+ respect to the natural coordinate frame on the surface. In other words,
666
+ if the vectors are `v = (v^1, v^2)` and `w = (w^1, w^2)`, calculate
667
+ `g_{11} v^1 w^1 + g_{12}(v^1 w^2 + v^2 w^1) + g_{22} v^2 w^2`, with
668
+ `g_{ij}` the coefficients of the first fundamental form.
669
+
670
+ INPUT:
671
+
672
+ - ``vector1``, ``vector2`` -- vectors on the surface
673
+
674
+ OUTPUT: first fundamental form evaluated on the input vectors
675
+
676
+ EXAMPLES::
677
+
678
+ sage: u, v = var('u, v', domain='real')
679
+ sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real')
680
+ sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere')
681
+ sage: sphere.first_fundamental_form(vector([v1,v2]),vector([w1,w2]))
682
+ v1*w1*cos(v)^2 + v2*w2
683
+
684
+ sage: vv = vector([1,2])
685
+ sage: sphere.first_fundamental_form(vv,vv)
686
+ cos(v)^2 + 4
687
+
688
+ sage: sphere.first_fundamental_form([1,1],[2,1])
689
+ 2*cos(v)^2 + 1
690
+ """
691
+ gamma = self.first_fundamental_form_coefficients()
692
+ return sum(gamma[(i,j)] * vector1[i - 1] * vector2[j - 1]
693
+ for i, j in product((1, 2), repeat=2))
694
+
695
+ def area_form_squared(self):
696
+ """
697
+ Return the square of the coefficient of the area form on the surface.
698
+ In terms of the coefficients `g_{ij}` (where `i, j = 1, 2`) of the
699
+ first fundamental form, this invariant is given by
700
+ `A^2 = g_{11}g_{22} - g_{12}^2`.
701
+
702
+ See also :meth:`.area_form`.
703
+
704
+ OUTPUT: square of the area form
705
+
706
+ EXAMPLES::
707
+
708
+ sage: u, v = var('u, v', domain='real')
709
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
710
+ sage: sphere.area_form_squared()
711
+ cos(v)^2
712
+ """
713
+ gamma = self.first_fundamental_form_coefficients()
714
+ sq = gamma[(1,1)] * gamma[(2,2)] - gamma[(1,2)]**2
715
+ return _simplify_full_rad(sq)
716
+
717
+ def area_form(self):
718
+ r"""
719
+ Return the coefficient of the area form on the surface. In terms of
720
+ the coefficients `g_{ij}` (where `i, j = 1, 2`) of the first
721
+ fundamental form, the coefficient of the area form is given by
722
+ `A = \sqrt{g_{11}g_{22} - g_{12}^2}`.
723
+
724
+ See also :meth:`.area_form_squared`.
725
+
726
+ OUTPUT: coefficient of the area form
727
+
728
+ EXAMPLES::
729
+
730
+ sage: u, v = var('u,v', domain='real')
731
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
732
+ sage: sphere.area_form()
733
+ cos(v)
734
+ """
735
+ f = abs(sqrt(self.area_form_squared()))
736
+ return _simplify_full_rad(f)
737
+
738
+ def first_fundamental_form_inverse_coefficients(self):
739
+ r"""
740
+ Return the coefficients `g^{ij}` of the inverse of the fundamental
741
+ form, as a dictionary. The inverse coefficients are defined by
742
+ `g^{ij} g_{jk} = \delta^i_k` with `\delta^i_k` the Kronecker
743
+ delta.
744
+
745
+ OUTPUT: dictionary of the inverse coefficients
746
+
747
+ EXAMPLES::
748
+
749
+ sage: u, v = var('u, v', domain='real')
750
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
751
+ sage: sphere.first_fundamental_form_inverse_coefficients()
752
+ {(1, 1): cos(v)^(-2), (1, 2): 0, (2, 1): 0, (2, 2): 1}
753
+ """
754
+
755
+ g = self.first_fundamental_form_coefficients()
756
+ D = g[(1,1)] * g[(2,2)] - g[(1,2)]**2
757
+
758
+ gi11 = _simplify_full_rad(g[(2,2)]/D)
759
+ gi12 = _simplify_full_rad(-g[(1,2)]/D)
760
+ gi21 = gi12
761
+ gi22 = _simplify_full_rad(g[(1,1)]/D)
762
+
763
+ return {(1,1): gi11, (1,2): gi12, (2,1): gi21, (2,2): gi22}
764
+
765
+ def first_fundamental_form_inverse_coefficient(self, index):
766
+ r"""
767
+ Return a specific component `g^{ij}` of the inverse of the fundamental
768
+ form.
769
+
770
+ INPUT:
771
+
772
+ - ``index`` -- tuple ``(i, j)`` specifying the index of the component `g^{ij}`
773
+
774
+ OUTPUT: component of the inverse of the fundamental form
775
+
776
+ EXAMPLES::
777
+
778
+ sage: u, v = var('u, v', domain='real')
779
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
780
+ sage: sphere.first_fundamental_form_inverse_coefficient((1, 2))
781
+ 0
782
+ sage: sphere.first_fundamental_form_inverse_coefficient((1, 1))
783
+ cos(v)^(-2)
784
+ """
785
+
786
+ index = tuple(sorted(index))
787
+ if len(index) == 2 and all(i == 1 or i == 2 for i in index):
788
+ return self.first_fundamental_form_inverse_coefficients()[index]
789
+ else:
790
+ raise ValueError("Index %s out of bounds." % str(index))
791
+
792
+ @cached_method
793
+ def rotation(self, theta):
794
+ r"""
795
+ Give the matrix of the rotation operator over a given angle `\theta`
796
+ with respect to the natural frame.
797
+
798
+ INPUT:
799
+
800
+ - ``theta`` -- rotation angle
801
+
802
+ OUTPUT: rotation matrix with respect to the natural frame
803
+
804
+ ALGORITHM:
805
+
806
+ The operator of rotation over `\pi/2` is `J^i_j = g^{ik}\omega_{jk}`,
807
+ where `\omega` is the area form. The operator of rotation over an
808
+ angle `\theta` is `\cos(\theta) I + \sin(\theta) J`.
809
+
810
+ EXAMPLES::
811
+
812
+ sage: u, v = var('u, v', domain='real')
813
+ sage: assume(cos(v)>0)
814
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
815
+
816
+ We first compute the matrix of rotation over `\pi/3`::
817
+
818
+ sage: rotation = sphere.rotation(pi/3); rotation
819
+ [ 1/2 -1/2*sqrt(3)/cos(v)]
820
+ [ 1/2*sqrt(3)*cos(v) 1/2]
821
+
822
+ We verify that three successive rotations over `\pi/3` yield minus the identity::
823
+
824
+ sage: rotation^3
825
+ [-1 0]
826
+ [ 0 -1]
827
+ """
828
+
829
+ from sage.functions.trig import sin, cos
830
+
831
+ gi = self.first_fundamental_form_inverse_coefficients()
832
+ w12 = self.area_form()
833
+ R11 = (cos(theta) + sin(theta)*gi[1,2]*w12).simplify_full()
834
+ R12 = (- sin(theta)*gi[1,1]*w12).simplify_full()
835
+ R21 = (sin(theta)*gi[2,2]*w12).simplify_full()
836
+ R22 = (cos(theta) - sin(theta)*gi[2,1]*w12).simplify_full()
837
+ return matrix([[R11,R12],[R21,R22]])
838
+
839
+ @cached_method
840
+ def orthonormal_frame(self, coordinates='ext'):
841
+ r"""
842
+ Return the orthonormal frame field on the surface, expressed either
843
+ in exterior coordinates (i.e. expressed as vector fields in the
844
+ ambient space `\mathbb{R}^3`, the default) or interior coordinates
845
+ (with respect to the natural frame)
846
+
847
+ INPUT:
848
+
849
+ - ``coordinates`` -- either ``ext`` (default) or ``int``
850
+
851
+ OUTPUT: orthogonal frame field as a dictionary
852
+
853
+ ALGORITHM:
854
+
855
+ We normalize the first vector `\vec e_1` of the natural frame and then
856
+ get the second frame vector as `\vec e_2 = [\vec n, \vec e_1]`, where
857
+ `\vec n` is the unit normal to the surface.
858
+
859
+ EXAMPLES::
860
+
861
+ sage: u, v = var('u,v', domain='real')
862
+ sage: assume(cos(v)>0)
863
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v],'sphere')
864
+ sage: frame = sphere.orthonormal_frame(); frame
865
+ {1: (-sin(u), cos(u), 0), 2: (-cos(u)*sin(v), -sin(u)*sin(v), cos(v))}
866
+ sage: (frame[1]*frame[1]).simplify_full()
867
+ 1
868
+ sage: (frame[1]*frame[2]).simplify_full()
869
+ 0
870
+ sage: frame[1] == sphere.orthonormal_frame_vector(1)
871
+ True
872
+
873
+ We compute the orthonormal frame with respect to the natural frame on
874
+ the surface::
875
+
876
+ sage: frame_int = sphere.orthonormal_frame(coordinates='int'); frame_int
877
+ {1: (1/cos(v), 0), 2: (0, 1)}
878
+ sage: sphere.first_fundamental_form(frame_int[1], frame_int[1])
879
+ 1
880
+ sage: sphere.first_fundamental_form(frame_int[1], frame_int[2])
881
+ 0
882
+ sage: sphere.first_fundamental_form(frame_int[2], frame_int[2])
883
+ 1
884
+ """
885
+ from sage.symbolic.constants import pi
886
+
887
+ if coordinates not in ['ext', 'int']:
888
+ raise ValueError("Coordinate system must be exterior ('ext') "
889
+ "or interior ('int').")
890
+
891
+ c = self.first_fundamental_form_coefficient([1, 1])
892
+ if coordinates == 'ext':
893
+ f1 = self.natural_frame()[1]
894
+
895
+ E1 = _simplify_full_rad(f1 / sqrt(c))
896
+ E2 = _simplify_full_rad(
897
+ self.normal_vector(normalized=True).cross_product(E1))
898
+ else:
899
+ E1 = vector([_simplify_full_rad(1 / sqrt(c)), 0])
900
+ E2 = (self.rotation(pi / 2) * E1).simplify_full()
901
+ return {1: E1, 2: E2}
902
+
903
+ def orthonormal_frame_vector(self, index, coordinates='ext'):
904
+ r"""
905
+ Return a specific basis vector field of the orthonormal frame field on
906
+ the surface, expressed in exterior or interior coordinates. See
907
+ :meth:`orthogonal_frame` for more details.
908
+
909
+ INPUT:
910
+
911
+ - ``index`` -- index of the basis vector
912
+ - ``coordinates`` -- either ``ext`` (default) or ``int``
913
+
914
+ OUTPUT: orthonormal frame vector field
915
+
916
+ EXAMPLES::
917
+
918
+ sage: u, v = var('u, v', domain='real')
919
+ sage: assume(cos(v)>0)
920
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
921
+ sage: V1 = sphere.orthonormal_frame_vector(1); V1
922
+ (-sin(u), cos(u), 0)
923
+ sage: V2 = sphere.orthonormal_frame_vector(2); V2
924
+ (-cos(u)*sin(v), -sin(u)*sin(v), cos(v))
925
+ sage: (V1*V1).simplify_full()
926
+ 1
927
+ sage: (V1*V2).simplify_full()
928
+ 0
929
+
930
+ sage: n = sphere.normal_vector(normalized=True)
931
+ sage: (V1.cross_product(V2) - n).simplify_full()
932
+ (0, 0, 0)
933
+ """
934
+
935
+ return self.orthonormal_frame(coordinates)[index]
936
+
937
+ def lie_bracket(self, v, w):
938
+ r"""
939
+ Return the Lie bracket of two vector fields that are tangent
940
+ to the surface. The vector fields should be given in intrinsic
941
+ coordinates, i.e. with respect to the natural frame.
942
+
943
+ INPUT:
944
+
945
+ - ``v``, ``w`` -- vector fields on the surface, expressed
946
+ as pairs of functions or as vectors of length 2
947
+
948
+ OUTPUT: the Lie bracket `[v, w]`
949
+
950
+ EXAMPLES::
951
+
952
+ sage: u, v = var('u, v', domain='real')
953
+ sage: assume(cos(v)>0)
954
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
955
+ sage: sphere.lie_bracket([u,v],[-v,u])
956
+ (0, 0)
957
+
958
+ sage: EE_int = sphere.orthonormal_frame(coordinates='int')
959
+ sage: sphere.lie_bracket(EE_int[1],EE_int[2])
960
+ (sin(v)/cos(v)^2, 0)
961
+ """
962
+ v = vector(SR, v)
963
+ w = vector(SR, w)
964
+
965
+ variables = self.variables_list
966
+ Dv = matrix([[_simplify_full_rad(diff(component, u))
967
+ for u in variables] for component in v])
968
+ Dw = matrix([[_simplify_full_rad(diff(component, u))
969
+ for u in variables] for component in w])
970
+ return vector(Dv*w - Dw*v).simplify_full()
971
+
972
+ def frame_structure_functions(self, e1, e2):
973
+ r"""
974
+ Return the structure functions `c^k_{ij}` for a frame field
975
+ `e_1, e_2`, i.e. a pair of vector fields on the surface which are
976
+ linearly independent at each point. The structure functions are
977
+ defined using the Lie bracket by `[e_i,e_j] = c^k_{ij}e_k`.
978
+
979
+ INPUT:
980
+
981
+ - ``e1``, ``e2`` -- vector fields in intrinsic coordinates on
982
+ the surface, expressed as pairs of functions, or as vectors of
983
+ length 2
984
+
985
+ OUTPUT:
986
+
987
+ Dictionary of structure functions, where the key ``(i, j, k)`` refers to
988
+ the structure function `c_{i,j}^k`.
989
+
990
+ EXAMPLES::
991
+
992
+ sage: u, v = var('u, v', domain='real')
993
+ sage: assume(cos(v) > 0)
994
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v], 'sphere')
995
+ sage: sphere.frame_structure_functions([u, v], [-v, u])
996
+ {(1, 1, 1): 0,
997
+ (1, 1, 2): 0,
998
+ (1, 2, 1): 0,
999
+ (1, 2, 2): 0,
1000
+ (2, 1, 1): 0,
1001
+ (2, 1, 2): 0,
1002
+ (2, 2, 1): 0,
1003
+ (2, 2, 2): 0}
1004
+
1005
+ We construct the structure functions of the orthonormal frame on the
1006
+ surface::
1007
+
1008
+ sage: EE_int = sphere.orthonormal_frame(coordinates='int')
1009
+ sage: CC = sphere.frame_structure_functions(EE_int[1],EE_int[2]); CC
1010
+ {(1, 1, 1): 0,
1011
+ (1, 1, 2): 0,
1012
+ (1, 2, 1): sin(v)/cos(v),
1013
+ (1, 2, 2): 0,
1014
+ (2, 1, 1): -sin(v)/cos(v),
1015
+ (2, 1, 2): 0,
1016
+ (2, 2, 1): 0,
1017
+ (2, 2, 2): 0}
1018
+ sage: sphere.lie_bracket(EE_int[1],EE_int[2]) - CC[(1,2,1)]*EE_int[1] - CC[(1,2,2)]*EE_int[2]
1019
+ (0, 0)
1020
+ """
1021
+ e1 = vector(SR, e1)
1022
+ e2 = vector(SR, e2)
1023
+
1024
+ lie_bracket = self.lie_bracket(e1, e2).simplify_full()
1025
+ transformation = matrix(SR, [e1, e2]).transpose()
1026
+
1027
+ w = (transformation.inverse()*lie_bracket).simplify_full()
1028
+
1029
+ return {(1,1,1): 0, (1,1,2): 0, (1,2,1): w[0], (1,2,2): w[1],
1030
+ (2,1,1): -w[0], (2,1,2): -w[1], (2,2,1): 0, (2,2,2): 0}
1031
+
1032
+ @cached_method
1033
+ def _compute_second_order_frame_element(self, index):
1034
+ """
1035
+ Compute an element of the second order frame of the surface. See
1036
+ :meth:`second_order_natural_frame` for more details.
1037
+
1038
+ This method expects its arguments in tuple form for caching.
1039
+ As it does no input checking, it should not be called directly.
1040
+
1041
+ EXAMPLES::
1042
+
1043
+ sage: u, v = var('u, v', domain='real')
1044
+ sage: paraboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v], 'paraboloid')
1045
+ sage: paraboloid._compute_second_order_frame_element((1, 2))
1046
+ (0, 0, 0)
1047
+ sage: paraboloid._compute_second_order_frame_element((2, 2))
1048
+ (0, 0, 2)
1049
+ """
1050
+ variables = [self.variables[i] for i in index]
1051
+ ddr_element = vector([_simplify_full_rad(diff(f, variables))
1052
+ for f in self.equation])
1053
+
1054
+ return ddr_element
1055
+
1056
+ def second_order_natural_frame(self):
1057
+ r"""
1058
+ Return the second-order frame of the surface, i.e. computes the
1059
+ second-order derivatives (with respect to the parameters on the
1060
+ surface) of the parametric expression `\vec r = \vec r(u^1,u^2)`
1061
+ of the surface.
1062
+
1063
+ OUTPUT:
1064
+
1065
+ - Dictionary where the keys are 2-tuples ``(i, j)`` and the values are the corresponding derivatives `r_{ij}`.
1066
+
1067
+ EXAMPLES:
1068
+
1069
+ We compute the second-order natural frame of the sphere::
1070
+
1071
+ sage: u, v = var('u, v', domain='real')
1072
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
1073
+ sage: sphere.second_order_natural_frame()
1074
+ {(1, 1): (-cos(u)*cos(v), -cos(v)*sin(u), 0),
1075
+ (1, 2): (sin(u)*sin(v), -cos(u)*sin(v), 0),
1076
+ (2, 1): (sin(u)*sin(v), -cos(u)*sin(v), 0),
1077
+ (2, 2): (-cos(u)*cos(v), -cos(v)*sin(u), -sin(v))}
1078
+ """
1079
+
1080
+ vectors = {}
1081
+ for index in product((1, 2), repeat=2):
1082
+ sorted_index = tuple(sorted(index))
1083
+ vectors[index] = \
1084
+ self._compute_second_order_frame_element(sorted_index)
1085
+ return vectors
1086
+
1087
+ def second_order_natural_frame_element(self, index):
1088
+ r"""
1089
+ Return a vector in the second-order frame of the surface, i.e.
1090
+ computes the second-order derivatives of the parametric expression
1091
+ `\vec{r}` of the surface with respect to the parameters listed in the
1092
+ argument.
1093
+
1094
+ INPUT:
1095
+
1096
+ - ``index`` -- a 2-tuple ``(i, j)`` specifying the element of the second-order frame
1097
+
1098
+ OUTPUT: the second-order derivative `r_{ij}`
1099
+
1100
+ EXAMPLES::
1101
+
1102
+ sage: u, v = var('u, v', domain='real')
1103
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
1104
+ sage: sphere.second_order_natural_frame_element((1, 2))
1105
+ (sin(u)*sin(v), -cos(u)*sin(v), 0)
1106
+ """
1107
+
1108
+ index = tuple(sorted(index))
1109
+ if len(index) == 2 and all(i == 1 or i == 2 for i in index):
1110
+ return self._compute_second_order_frame_element(index)
1111
+ else:
1112
+ raise ValueError("Index %s out of bounds." % str(index))
1113
+
1114
+ @cached_method
1115
+ def _compute_second_fundamental_form_coefficient(self, index):
1116
+ """
1117
+ Compute a coefficient of the second fundamental form of the surface.
1118
+ See ``second_fundamental_form_coefficient`` for more details.
1119
+
1120
+ This method expects its arguments in tuple form for caching. As it
1121
+ does no input checking, it should not be called directly.
1122
+
1123
+ EXAMPLES::
1124
+
1125
+ sage: u, v = var('u,v', domain='real')
1126
+ sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid')
1127
+ sage: paraboloid._compute_second_fundamental_form_coefficient((1,1))
1128
+ 2/sqrt(4*u^2 + 4*v^2 + 1)
1129
+ """
1130
+ N = self.normal_vector(normalized=True)
1131
+ v = self.second_order_natural_frame_element(index)
1132
+ return _simplify_full_rad(v*N)
1133
+
1134
+ def second_fundamental_form_coefficient(self, index):
1135
+ r"""
1136
+ Return the coefficient `h_{ij}` of the second fundamental form
1137
+ corresponding to the index `(i, j)`. If the equation of the surface
1138
+ is `\vec{r}(u^1, u^2)`, then `h_{ij} = \vec{r}_{u^i u^j} \cdot \vec{n}`,
1139
+ where `\vec{n}` is the unit normal.
1140
+
1141
+ INPUT:
1142
+
1143
+ - ``index`` -- a 2-tuple ``(i, j)``
1144
+
1145
+ OUTPUT: component `h_{ij}` of the second fundamental form
1146
+
1147
+ EXAMPLES::
1148
+
1149
+ sage: u, v = var('u,v', domain='real')
1150
+ sage: assume(cos(v)>0)
1151
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
1152
+ sage: sphere.second_fundamental_form_coefficient((1, 1))
1153
+ -cos(v)^2
1154
+ sage: sphere.second_fundamental_form_coefficient((2, 1))
1155
+ 0
1156
+ """
1157
+ index = tuple(index)
1158
+ if len(index) == 2 and all(i == 1 or i == 2 for i in index):
1159
+ return self._compute_second_fundamental_form_coefficient(index)
1160
+ else:
1161
+ raise ValueError("Index %s out of bounds." % str(index))
1162
+
1163
+ def second_fundamental_form_coefficients(self):
1164
+ """
1165
+ Return the coefficients `h_{ij}` of the second fundamental form as
1166
+ a dictionary, where the keys are the indices `(i, j)` and the values
1167
+ are the corresponding components `h_{ij}`.
1168
+
1169
+ When only one component is needed, consider instead the function
1170
+ :meth:`second_fundamental_form_coefficient`.
1171
+
1172
+ OUTPUT: dictionary of second fundamental form coefficients
1173
+
1174
+ EXAMPLES::
1175
+
1176
+ sage: u, v = var('u, v', domain='real')
1177
+ sage: assume(cos(v)>0)
1178
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
1179
+ sage: sphere.second_fundamental_form_coefficients()
1180
+ {(1, 1): -cos(v)^2, (1, 2): 0, (2, 1): 0, (2, 2): -1}
1181
+ """
1182
+
1183
+ coefficients = {}
1184
+ for index in product((1, 2), repeat=2):
1185
+ coefficients[index] = \
1186
+ self._compute_second_fundamental_form_coefficient(index)
1187
+ return coefficients
1188
+
1189
+ def second_fundamental_form(self, vector1, vector2):
1190
+ r"""
1191
+ Evaluates the second fundamental form on two vectors on the surface.
1192
+ If the vectors are given by `v=(v^1,v^2)` and `w=(w^1,w^2)`, the
1193
+ result of this function is `h_{11} v^1 w^1 + h_{12}(v^1 w^2 + v^2 w^1) + h_{22} v^2 w^2`.
1194
+
1195
+ INPUT:
1196
+
1197
+ - ``vector1``, ``vector2`` -- 2-tuples representing the input vectors
1198
+
1199
+ OUTPUT: value of the second fundamental form evaluated on the given vectors
1200
+
1201
+ EXAMPLES:
1202
+
1203
+ We evaluate the second fundamental form on two symbolic vectors::
1204
+
1205
+ sage: u, v = var('u, v', domain='real')
1206
+ sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real')
1207
+ sage: assume(cos(v) > 0)
1208
+ sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere')
1209
+ sage: sphere.second_fundamental_form(vector([v1, v2]), vector([w1, w2]))
1210
+ -v1*w1*cos(v)^2 - v2*w2
1211
+
1212
+ We evaluate the second fundamental form on vectors with numerical
1213
+ components::
1214
+
1215
+ sage: vect = vector([1,2])
1216
+ sage: sphere.second_fundamental_form(vect, vect)
1217
+ -cos(v)^2 - 4
1218
+ sage: sphere.second_fundamental_form([1,1], [2,1])
1219
+ -2*cos(v)^2 - 1
1220
+ """
1221
+ hh = self.second_fundamental_form_coefficients()
1222
+ return sum(hh[(i, j)] * vector1[i - 1] * vector2[j - 1]
1223
+ for (i, j) in product((1, 2), repeat=2))
1224
+
1225
+ def gauss_curvature(self):
1226
+ r"""
1227
+ Finds the gaussian curvature of the surface, given by
1228
+ `K = \frac{h_{11}h_{22} - h_{12}^2}{g_{11}g_{22} - g_{12}^2}`,
1229
+ where `g_{ij}` and `h_{ij}` are the coefficients of the first
1230
+ and second fundamental form, respectively.
1231
+
1232
+ OUTPUT: Gaussian curvature of the surface.
1233
+
1234
+ EXAMPLES::
1235
+
1236
+ sage: R = var('R')
1237
+ sage: assume(R>0)
1238
+ sage: u, v = var('u,v', domain='real')
1239
+ sage: assume(cos(v)>0)
1240
+ sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
1241
+ sage: sphere.gauss_curvature()
1242
+ R^(-2)
1243
+ """
1244
+ hh = self.second_fundamental_form_coefficients()
1245
+ return _simplify_full_rad(
1246
+ (hh[(1,1)] * hh[(2,2)] - hh[(1,2)]**2)/self.area_form_squared())
1247
+
1248
+ def mean_curvature(self):
1249
+ r"""
1250
+ Finds the mean curvature of the surface, given by
1251
+ `H = \frac{1}{2}\frac{g_{22}h_{11} - 2g_{12}h_{12} + g_{11}h_{22}}{g_{11}g_{22} - g_{12}^2}`,
1252
+ where `g_{ij}` and `h_{ij}` are the components of the first and second
1253
+ fundamental forms, respectively.
1254
+
1255
+ OUTPUT: mean curvature of the surface
1256
+
1257
+ EXAMPLES::
1258
+
1259
+ sage: R = var('R')
1260
+ sage: assume(R>0)
1261
+ sage: u, v = var('u,v', domain='real')
1262
+ sage: assume(cos(v)>0)
1263
+ sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
1264
+ sage: sphere.mean_curvature()
1265
+ -1/R
1266
+ """
1267
+ gg = self.first_fundamental_form_coefficients()
1268
+ hh = self.second_fundamental_form_coefficients()
1269
+ denom = 2*self.area_form_squared()
1270
+ numer = (gg[(2,2)]*hh[(1,1)] - 2*gg[(1,2)]*hh[(1,2)] +
1271
+ gg[(1,1)]*hh[(2,2)]).simplify_full()
1272
+ return _simplify_full_rad(numer/denom)
1273
+
1274
+ @cached_method
1275
+ def shape_operator_coefficients(self):
1276
+ r"""
1277
+ Return the components of the shape operator of the surface as a
1278
+ dictionary. See ``shape_operator`` for more information.
1279
+
1280
+ OUTPUT:
1281
+
1282
+ Dictionary where the keys are two-tuples ``(i, j)``, with values the
1283
+ corresponding component of the shape operator.
1284
+
1285
+ EXAMPLES::
1286
+
1287
+ sage: R = var('R')
1288
+ sage: u, v = var('u,v', domain='real')
1289
+ sage: assume(cos(v)>0)
1290
+ sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
1291
+ sage: sphere.shape_operator_coefficients()
1292
+ {(1, 1): -1/R, (1, 2): 0, (2, 1): 0, (2, 2): -1/R}
1293
+ """
1294
+
1295
+ gi = self.first_fundamental_form_inverse_coefficients()
1296
+ hh = self.second_fundamental_form_coefficients()
1297
+
1298
+ sh_op11 = _simplify_full_rad(gi[(1,1)]*hh[(1,1)] + gi[(1,2)]*hh[(1,2)])
1299
+ sh_op12 = _simplify_full_rad(gi[(1,1)]*hh[(2,1)] + gi[(1,2)]*hh[(2,2)])
1300
+ sh_op21 = _simplify_full_rad(gi[(2,1)]*hh[(1,1)] + gi[(2,2)]*hh[(1,2)])
1301
+ sh_op22 = _simplify_full_rad(gi[(2,1)]*hh[(2,1)] + gi[(2,2)]*hh[(2,2)])
1302
+
1303
+ return {(1,1): sh_op11, (1,2): sh_op12, (2,1): sh_op21, (2,2): sh_op22}
1304
+
1305
+ def shape_operator(self):
1306
+ r"""
1307
+ Return the shape operator of the surface as a matrix. The shape
1308
+ operator is defined as the derivative of the Gauss map, and is
1309
+ computed here in terms of the first and second fundamental form by
1310
+ means of the Weingarten equations.
1311
+
1312
+ OUTPUT: matrix of the shape operator
1313
+
1314
+ EXAMPLES::
1315
+
1316
+ sage: R = var('R')
1317
+ sage: assume(R>0)
1318
+ sage: u, v = var('u,v', domain='real')
1319
+ sage: assume(cos(v)>0)
1320
+ sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere')
1321
+ sage: S = sphere.shape_operator(); S
1322
+ [-1/R 0]
1323
+ [ 0 -1/R]
1324
+
1325
+ The eigenvalues of the shape operator are the principal curvatures of
1326
+ the surface::
1327
+
1328
+ sage: u, v = var('u,v', domain='real')
1329
+ sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid')
1330
+ sage: S = paraboloid.shape_operator(); S
1331
+ [2*(4*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2) -8*u*v/(4*u^2 + 4*v^2 + 1)^(3/2)]
1332
+ [ -8*u*v/(4*u^2 + 4*v^2 + 1)^(3/2) 2*(4*u^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2)]
1333
+ sage: S.eigenvalues()
1334
+ [2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1), 2/sqrt(4*u^2 + 4*v^2 + 1)]
1335
+ """
1336
+
1337
+ shop = self.shape_operator_coefficients()
1338
+ shop_matrix = matrix([[shop[(1,1)],shop[(1,2)]],
1339
+ [shop[(2,1)],shop[(2,2)]]])
1340
+ return shop_matrix
1341
+
1342
+ def principal_directions(self):
1343
+ r"""
1344
+ Finds the principal curvatures and principal directions of the surface.
1345
+
1346
+ OUTPUT:
1347
+
1348
+ For each principal curvature, returns a list of the form
1349
+ `(\rho, V, n)`, where `\rho` is the principal curvature,
1350
+ `V` is the corresponding principal direction, and `n` is
1351
+ the multiplicity.
1352
+
1353
+ EXAMPLES::
1354
+
1355
+ sage: u, v = var('u, v', domain='real')
1356
+ sage: R, r = var('R,r', domain='real')
1357
+ sage: assume(R>r,r>0)
1358
+ sage: torus = ParametrizedSurface3D([(R+r*cos(v))*cos(u),(R+r*cos(v))*sin(u),r*sin(v)],[u,v],'torus')
1359
+ sage: torus.principal_directions()
1360
+ [(-cos(v)/(r*cos(v) + R), [(1, 0)], 1), (-1/r, [(0, 1)], 1)]
1361
+
1362
+ ::
1363
+
1364
+ sage: u, v = var('u, v', domain='real')
1365
+ sage: V = vector([u*cos(u+v), u*sin(u+v), u+v])
1366
+ sage: helicoid = ParametrizedSurface3D(V, (u, v))
1367
+ sage: helicoid.principal_directions()
1368
+ [(-1/(u^2 + 1), [(1, -(u^2 - sqrt(u^2 + 1) + 1)/(u^2 + 1))], 1),
1369
+ (1/(u^2 + 1), [(1, -(u^2 + sqrt(u^2 + 1) + 1)/(u^2 + 1))], 1)]
1370
+ """
1371
+ return self.shape_operator().eigenvectors_right()
1372
+
1373
+ @cached_method
1374
+ def connection_coefficients(self):
1375
+ r"""
1376
+ Compute the connection coefficients or Christoffel symbols
1377
+ `\Gamma^k_{ij}` of the surface. If the coefficients of the first
1378
+ fundamental form are given by `g_{ij}` (where `i, j = 1, 2`), then
1379
+ `\Gamma^k_{ij} = \frac{1}{2} g^{kl} \left( \frac{\partial g_{li}}{\partial x^j}
1380
+ - \frac{\partial g_{ij}}{\partial x^l}
1381
+ + \frac{\partial g_{lj}}{\partial x^i} \right)`.
1382
+ Here, `(g^{kl})` is the inverse of the matrix `(g_{ij})`, with
1383
+ `i, j = 1, 2`.
1384
+
1385
+ OUTPUT:
1386
+
1387
+ Dictionary of connection coefficients, where the keys are 3-tuples
1388
+ `(i,j,k)` and the values are the corresponding coefficients
1389
+ `\Gamma^k_{ij}`.
1390
+
1391
+ EXAMPLES::
1392
+
1393
+ sage: r = var('r')
1394
+ sage: assume(r > 0)
1395
+ sage: u, v = var('u,v', domain='real')
1396
+ sage: assume(cos(v)>0)
1397
+ sage: sphere = ParametrizedSurface3D([r*cos(u)*cos(v),r*sin(u)*cos(v),r*sin(v)],[u,v],'sphere')
1398
+ sage: sphere.connection_coefficients()
1399
+ {(1, 1, 1): 0,
1400
+ (1, 1, 2): cos(v)*sin(v),
1401
+ (1, 2, 1): -sin(v)/cos(v),
1402
+ (1, 2, 2): 0,
1403
+ (2, 1, 1): -sin(v)/cos(v),
1404
+ (2, 1, 2): 0,
1405
+ (2, 2, 1): 0,
1406
+ (2, 2, 2): 0}
1407
+ """
1408
+ x = self.variables
1409
+ gg = self.first_fundamental_form_coefficients()
1410
+ gi = self.first_fundamental_form_inverse_coefficients()
1411
+
1412
+ dg = {}
1413
+ for i,j,k in product((1, 2), repeat=3):
1414
+ dg[(i,j,k)] = _simplify_full_rad(gg[(j,k)].differentiate(x[i]))
1415
+
1416
+ structfun = {}
1417
+ for i,j,k in product((1, 2), repeat=3):
1418
+ structfun[(i,j,k)] = sum(gi[(k,s)]*(dg[(i,j,s)] + dg[(j,i,s)]
1419
+ - dg[(s,i,j)])/2
1420
+ for s in (1,2))
1421
+ structfun[(i,j,k)] = _simplify_full_rad(structfun[(i,j,k)])
1422
+ return structfun
1423
+
1424
+ @cached_method
1425
+ def _create_geodesic_ode_system(self):
1426
+ r"""
1427
+ Helper method to create a fast floating-point version of the
1428
+ geodesic equations, used by :meth:`geodesics_numerical`.
1429
+
1430
+ EXAMPLES::
1431
+
1432
+ sage: p, q = var('p,q', domain='real')
1433
+ sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')
1434
+ sage: ode = sphere._create_geodesic_ode_system()
1435
+ sage: ode.function(0.0, (1.0, 0.0, 1.0, 1.0))
1436
+ [1.00000000000000, 1.00000000000000, -0.4546487134128409, 3.114815449309804]
1437
+ """
1438
+ from sage.ext.fast_eval import fast_float
1439
+ from sage.calculus.ode import ode_solver
1440
+
1441
+ u1 = self.variables[1]
1442
+ u2 = self.variables[2]
1443
+
1444
+ C = self.connection_coefficients()
1445
+
1446
+ with SR.temp_var(domain='real') as v1:
1447
+ with SR.temp_var(domain='real') as v2:
1448
+ dv1 = - C[(1,1,1)]*v1**2 - 2*C[(1,2,1)]*v1*v2 - C[(2,2,1)]*v2**2
1449
+ dv2 = - C[(1,1,2)]*v1**2 - 2*C[(1,2,2)]*v1*v2 - C[(2,2,2)]*v2**2
1450
+ fun1 = fast_float(dv1, str(u1), str(u2), str(v1), str(v2))
1451
+ fun2 = fast_float(dv2, str(u1), str(u2), str(v1), str(v2))
1452
+
1453
+ geodesic_ode = ode_solver()
1454
+ geodesic_ode.function = (
1455
+ lambda t, u1_u2_v1_v2:
1456
+ [u1_u2_v1_v2[2], u1_u2_v1_v2[3], fun1(*u1_u2_v1_v2), fun2(*u1_u2_v1_v2)])
1457
+ return geodesic_ode
1458
+
1459
+ def geodesics_numerical(self, p0, v0, tinterval):
1460
+ r"""
1461
+ Numerical integration of the geodesic equations. Explicitly, the
1462
+ geodesic equations are given by
1463
+ `\frac{d^2 u^i}{dt^2} + \Gamma^i_{jk} \frac{d u^j}{dt} \frac{d u^k}{dt} = 0`.
1464
+
1465
+ Solving these equations gives the coordinates `(u^1, u^2)` of
1466
+ the geodesic on the surface. The coordinates in space can
1467
+ then be found by substituting `(u^1, u^2)` into the vector
1468
+ `\vec{r}(u^1, u^2)` representing the surface.
1469
+
1470
+ ALGORITHM:
1471
+
1472
+ The geodesic equations are integrated forward in time using
1473
+ the ode solvers from ``sage.calculus.ode``. See the member
1474
+ function ``_create_geodesic_ode_system`` for more details.
1475
+
1476
+ INPUT:
1477
+
1478
+ - ``p0`` -- 2-tuple with coordinates of the initial point
1479
+
1480
+ - ``v0`` -- 2-tuple with components of the initial tangent vector to the geodesic
1481
+
1482
+ - ``tinterval`` -- list ``[a, b, M]``, where ``(a,b)`` is the domain
1483
+ of the geodesic and ``M`` is the number of subdivision points used
1484
+ when returning the solution
1485
+
1486
+ OUTPUT:
1487
+
1488
+ List of lists ``[t, [u1(t), u2(t)], [v1(t), v2(t)], [x1(t), x2(t), x3(t)]]``, where
1489
+
1490
+ - ``t`` -- a subdivision point;
1491
+
1492
+ - ``[u1(t), u2(t)]`` are the intrinsic coordinates of the geodesic point;
1493
+
1494
+ - ``[v1(t), v2(t)]`` are the intrinsic coordinates of the tangent vector to the geodesic;
1495
+
1496
+ - ``[x1(t), x2(t), x3(t)]`` are the coordinates of the geodesic point in the three-dimensional space.
1497
+
1498
+ EXAMPLES::
1499
+
1500
+ sage: p, q = var('p,q', domain='real')
1501
+ sage: assume(cos(q)>0)
1502
+ sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')
1503
+ sage: geodesic = sphere.geodesics_numerical([0.0,0.0],[1.0,1.0],[0,2*pi,5])
1504
+ sage: times, points, tangent_vectors, ext_points = zip(*geodesic)
1505
+
1506
+ sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits
1507
+ sage: round4(times)
1508
+ [0.0000, 1.257, 2.513, 3.770, 5.027, 6.283]
1509
+ sage: [round4(p) for p in points]
1510
+ [[0.0000, 0.0000], [0.7644, 1.859], [-0.2876, 3.442], [-0.6137, 5.502], [0.5464, 6.937], [0.3714, 9.025]]
1511
+ sage: [round4(p) for p in ext_points]
1512
+ [[1.000, 0.0000, 0.0000], [-0.2049, 0.6921, 0.6921], [-0.9160, -0.2836, -0.2836], [0.5803, -0.5759, -0.5759], [0.6782, 0.5196, 0.5196], [-0.8582, 0.3629, 0.3629]]
1513
+ """
1514
+ solver = self._create_geodesic_ode_system()
1515
+
1516
+ t_interval, n = tinterval[0:2], tinterval[2]
1517
+ solver.y_0 = [p0[0], p0[1], v0[0], v0[1]]
1518
+ solver.ode_solve(t_span=t_interval, num_points=n)
1519
+
1520
+ parsed_solution = \
1521
+ [[vec[0], vec[1][0:2], vec[1][2:], self.point(vec[1])]
1522
+ for vec in solver.solution]
1523
+
1524
+ return parsed_solution
1525
+
1526
+ @cached_method
1527
+ def _create_pt_ode_system(self, curve, t):
1528
+ """
1529
+ Helper method to create a fast floating-point version of the parallel
1530
+ transport equations, used by ``parallel_translation_numerical``.
1531
+
1532
+ INPUT:
1533
+
1534
+ - ``curve`` -- curve in intrinsic coordinates along which to do parallel transport
1535
+ - ``t`` -- curve parameter
1536
+
1537
+ EXAMPLES::
1538
+
1539
+ sage: p, q = var('p,q', domain='real')
1540
+ sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere')
1541
+ sage: s = var('s')
1542
+ sage: ode = sphere._create_pt_ode_system((s, s), s)
1543
+ sage: ode.function(0.0, (1.0, 1.0))
1544
+ [-0.0, 0.0]
1545
+ """
1546
+
1547
+ from sage.ext.fast_eval import fast_float
1548
+ from sage.calculus.ode import ode_solver
1549
+
1550
+ u1 = self.variables[1]
1551
+ u2 = self.variables[2]
1552
+
1553
+ du1 = diff(curve[0], t)
1554
+ du2 = diff(curve[1], t)
1555
+
1556
+ C = self.connection_coefficients()
1557
+ for coef in C:
1558
+ C[coef] = C[coef].subs({u1: curve[0], u2: curve[1]})
1559
+
1560
+ with SR.temp_var(domain='real') as v1:
1561
+ with SR.temp_var(domain='real') as v2:
1562
+ dv1 = - C[(1,1,1)]*v1*du1 - C[(1,2,1)]*(du1*v2 + du2*v1) - \
1563
+ C[(2,2,1)]*du2*v2
1564
+ dv2 = - C[(1,1,2)]*v1*du1 - C[(1,2,2)]*(du1*v2 + du2*v1) - \
1565
+ C[(2,2,2)]*du2*v2
1566
+ fun1 = fast_float(dv1, str(t), str(v1), str(v2))
1567
+ fun2 = fast_float(dv2, str(t), str(v1), str(v2))
1568
+
1569
+ pt_ode = ode_solver()
1570
+ pt_ode.function = lambda t, v1_v2: [fun1(t, v1_v2[0], v1_v2[1]), fun2(t, v1_v2[0], v1_v2[1])]
1571
+ return pt_ode
1572
+
1573
+ def parallel_translation_numerical(self, curve, t, v0, tinterval):
1574
+ r"""
1575
+ Numerically solve the equations for parallel translation of a vector
1576
+ along a curve on the surface. Explicitly, the equations for parallel
1577
+ translation are given by
1578
+ `\frac{d u^i}{dt} + u^j \frac{d c^k}{dt} \Gamma^i_{jk} = 0`,
1579
+ where `\Gamma^i_{jk}` are the connection coefficients of the surface,
1580
+ the vector to be transported has components `u^j` and the curve along
1581
+ which to transport has components `c^k`.
1582
+
1583
+ ALGORITHM:
1584
+
1585
+ The parallel transport equations are integrated forward in time using
1586
+ the ode solvers from ``sage.calculus.ode``. See :meth:`_create_pt_ode_system`
1587
+ for more details.
1588
+
1589
+ INPUT:
1590
+
1591
+ - ``curve`` -- 2-tuple of functions which determine the curve with respect to
1592
+ the local coordinate system
1593
+
1594
+ - ``t`` -- symbolic variable denoting the curve parameter
1595
+
1596
+ - ``v0`` -- 2-tuple representing the initial vector
1597
+
1598
+ - ``tinterval`` -- list ``[a, b, N]``, where ``(a, b)`` is the domain of the curve
1599
+ and ``N`` is the number of subdivision points
1600
+
1601
+ OUTPUT:
1602
+
1603
+ The list consisting of lists ``[t, [v1(t), v2(t)]]``, where
1604
+
1605
+ - ``t`` -- a subdivision point;
1606
+
1607
+ - ``[v1(t), v2(t)]`` is the list of coordinates of the vector parallel translated
1608
+ along the curve.
1609
+
1610
+ EXAMPLES::
1611
+
1612
+ sage: p, q = var('p,q', domain='real')
1613
+ sage: v = [p,q]
1614
+ sage: assume(cos(q)>0)
1615
+ sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],v,'sphere')
1616
+ sage: s = var('s')
1617
+ sage: vector_field = sphere.parallel_translation_numerical([s,s],s,[1.0,1.0],[0.0, pi/4, 5])
1618
+ sage: times, components = zip(*vector_field)
1619
+
1620
+ sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits
1621
+ sage: round4(times)
1622
+ [0.0000, 0.1571, 0.3142, 0.4712, 0.6283, 0.7854]
1623
+ sage: [round4(v) for v in components]
1624
+ [[1.000, 1.000], [0.9876, 1.025], [0.9499, 1.102], [0.8853, 1.238], [0.7920, 1.448], [0.6687, 1.762]]
1625
+ """
1626
+ solver = self._create_pt_ode_system(tuple(curve), t)
1627
+
1628
+ t_interval, n = tinterval[0:2], tinterval[2]
1629
+ solver.y_0 = v0
1630
+ solver.ode_solve(t_span=t_interval, num_points=n)
1631
+
1632
+ return solver.solution