def _new_comp(self, basis): r""" Create some (uninitialized) components of ``self`` in a given basis. This method, which is already implemented in :meth:`FreeModuleTensor._new_comp`, is redefined here for efficiency. INPUT: - ``basis`` -- basis of the free module on which ``self`` is defined OUTPUT: - an instance of :class:`~sage.tensor.modules.comp.Components` EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: v = M([1,-2,3], name='v') sage: v._new_comp(e) 1-index components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: type(v._new_comp(e)) <class 'sage.tensor.modules.comp.Components'> """ fmodule = self._fmodule # the base free module return Components(fmodule._ring, basis, 1, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter)
def _new_coef(self, frame): r""" Create the connection coefficients w.r.t. the given frame. TESTS:: sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: g = M.metric('g') sage: g[0,0], g[1,1] = 1, 1 sage: nab = g.connection() sage: nab._new_coef(X.frame()) 3-indices components w.r.t. Coordinate frame (M, (d/dx,d/dy)), with symmetry on the index positions (1, 2) sage: e = M.vector_frame('e') sage: nab._new_coef(e) 3-indices components w.r.t. Vector frame (M, (e_0,e_1)) """ from sage.tensor.modules.comp import Components, CompWithSym from sage.manifolds.differentiable.scalarfield import DiffScalarField from sage.manifolds.differentiable.vectorframe import CoordFrame if isinstance(frame, CoordFrame): # the Christoffel symbols are symmetric: return CompWithSym(frame._domain.scalar_field_algebra(), frame, 3, start_index=self._domain._sindex, output_formatter=DiffScalarField.coord_function, sym=(1,2)) else: # a priori no symmetry in a generic frame: return Components(frame._domain.scalar_field_algebra(), frame, 3, start_index=self._domain._sindex, output_formatter=DiffScalarField.coord_function)
def __invert__(self): r""" Return the inverse automorphism. """ from sage.matrix.constructor import matrix from sage.tensor.modules.comp import Components from vectorframe import CoordFrame from utilities import simplify_chain if self._is_identity: return self if self._inverse is None: if self._name is None: inv_name = None else: inv_name = self._name + '^(-1)' if self._latex_name is None: inv_latex_name = None else: inv_latex_name = self._latex_name + r'^{-1}' fmodule = self._fmodule si = fmodule._sindex nsi = fmodule._rank + si self._inverse = fmodule.automorphism(name=inv_name, latex_name=inv_latex_name) for frame in self._components: if isinstance(frame, CoordFrame): chart = frame._chart else: chart = self._domain._def_chart #!# to be improved try: mat_self = matrix([[ self.comp(frame)[i, j, chart]._express for j in range(si, nsi) ] for i in range(si, nsi)]) except (KeyError, ValueError): continue mat_inv = mat_self.inverse() cinv = Components(fmodule._ring, frame, 2, start_index=si, output_formatter=fmodule._output_formatter) for i in range(si, nsi): for j in range(si, nsi): cinv[i, j] = { chart: simplify_chain(mat_inv[i - si, j - si]) } self._inverse._components[frame] = cinv return self._inverse
def _new_comp(self, basis): r""" Create some (uninitialized) components of ``self`` in a given basis. This method, which is already implemented in :meth:`FreeModuleTensor._new_comp`, is redefined here for efficiency. INPUT: - ``basis`` -- basis of the free module on which ``self`` is defined OUTPUT: - an instance of :class:`~sage.tensor.modules.comp.CompFullyAntiSym`, or of :class:`~sage.tensor.modules.comp.Components` if the degree of ``self`` is 1. EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: a = M.alternating_contravariant_tensor(2, name='a') sage: a._new_comp(e) Fully antisymmetric 2-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: a = M.alternating_contravariant_tensor(1) sage: a._new_comp(e) 1-index components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring """ fmodule = self._fmodule # the base free module if self._tensor_rank == 1: return Components(fmodule._ring, basis, 1, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter) return CompFullyAntiSym(fmodule._ring, basis, self._tensor_rank, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter)
def __invert__(self): r""" Return the inverse automorphism of ``self``. EXAMPLES:: sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: a = M.automorphism_field(name='a') sage: a[:] = [[0, 2], [-1, 0]] sage: b = a.inverse(); b Field of tangent-space automorphisms a^(-1) on the 2-dimensional differentiable manifold M sage: b[:] [ 0 -1] [1/2 0] sage: a[:] [ 0 2] [-1 0] The result is cached:: sage: a.inverse() is b True Instead of ``inverse()``, one can use the power minus one to get the inverse:: sage: b is a^(-1) True or the operator ``~``:: sage: b is ~a True """ from sage.rings.real_mpfr import RR from sage.matrix.constructor import matrix from sage.tensor.modules.comp import Components from sage.manifolds.differentiable.vectorframe import CoordFrame from sage.manifolds.utilities import (simplify_chain_real, simplify_chain_generic) if self._is_identity: return self if self._inverse is None: if self._name is None: inv_name = None else: inv_name = self._name + '^(-1)' if self._latex_name is None: inv_latex_name = None else: inv_latex_name = self._latex_name + r'^{-1}' fmodule = self._fmodule si = fmodule._sindex ; nsi = fmodule._rank + si self._inverse = fmodule.automorphism(name=inv_name, latex_name=inv_latex_name) if self._domain.base_field() == RR: simplify_chain = simplify_chain_real else: simplify_chain = simplify_chain_generic for frame in self._components: if isinstance(frame, CoordFrame): chart = frame._chart else: chart = self._domain._def_chart #!# to be improved try: mat_self = matrix( [[self.comp(frame)[i, j, chart]._express for j in range(si, nsi)] for i in range(si, nsi)]) except (KeyError, ValueError): continue mat_inv = mat_self.inverse() cinv = Components(fmodule._ring, frame, 2, start_index=si, output_formatter=fmodule._output_formatter) for i in range(si, nsi): for j in range(si, nsi): cinv[i, j] = {chart: simplify_chain(mat_inv[i-si,j-si])} self._inverse._components[frame] = cinv return self._inverse
def _pullback_chart(diff_map, tensor): r""" Helper function performing the pullback on chart domains only. INPUT: - ``diff_map`` -- a restriction of ``self``, whose both domain and codomain are chart domains - ``tensor`` -- a covariant tensor field, whose domain is the codomain of ``diff_map`` OUTPUT: - the pull back of ``tensor`` by ``diff_map`` """ dom1 = diff_map._domain dom2 = diff_map._codomain ncov = tensor._tensor_type[1] resu_name = None resu_latex_name = None if diff_map._name is not None and tensor._name is not None: resu_name = diff_map._name + '_*(' + tensor._name + ')' if (diff_map._latex_name is not None and tensor._latex_name is not None): resu_latex_name = '{' + diff_map._latex_name + '}_*' + \ tensor._latex_name fmodule1 = dom1.vector_field_module() ring1 = fmodule1._ring si1 = fmodule1._sindex of1 = fmodule1._output_formatter si2 = dom2._sindex resu = fmodule1.tensor((0,ncov), name=resu_name, latex_name=resu_latex_name, sym=tensor._sym, antisym=tensor._antisym) for frame2 in tensor._components: if isinstance(frame2, CoordFrame): chart2 = frame2._chart for chart1 in dom1._atlas: if (chart1._domain is dom1 and (chart1, chart2) in diff_map._coord_expression): # Computation at the component level: frame1 = chart1._frame tcomp = tensor._components[frame2] if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(ring1, frame1, ncov, start_index=si1, output_formatter=of1) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(ring1, frame1, ncov, start_index=si1, output_formatter=of1) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(ring1, frame1, ncov, start_index=si1, output_formatter=of1, sym=tcomp.sym, antisym=tcomp.antisym) else: ptcomp = Components(ring1, frame1, ncov, start_index=si1, output_formatter=of1) phi = diff_map._coord_expression[(chart1, chart2)] jacob = phi.jacobian() # X2 coordinates expressed in terms of # X1 ones via the diff. map: coord2_1 = phi(*(chart1._xx)) for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in dom2.manifold().index_generator(ncov): ff = tcomp[[ind_old]].coord_function(chart2) t = chart1.function(ff(*coord2_1)) for i in range(ncov): t *= jacob[ind_old[i]-si2, ind_new[i]-si1] res += t ptcomp[ind_new] = res resu._components[frame1] = ptcomp return resu
def pushforward(self, tensor): r""" Pushforward operator associated with ``self``. In what follows, let `\Phi` denote the differentiable map, `M` its domain and `N` its codomain. INPUT: - ``tensor`` -- :class:`~sage.manifolds.differentiable.tensorfield.TensorField`; a fully contrariant tensor field `T` on `M`, i.e. a tensor field of type `(p, 0)`, with `p` a positive integer OUTPUT: - a :class:`~sage.manifolds.differentiable.tensorfield.TensorField` representing a fully contravariant tensor field along `M` with values in `N`, which is the pushforward of `T` by `\Phi` EXAMPLES: Pushforward of a vector field on the 2-sphere `S^2` to the Euclidean 3-space `\RR^3`, via the standard embedding of `S^2`:: sage: S2 = Manifold(2, 'S^2', start_index=1) sage: U = S2.open_subset('U') # domain of spherical coordinates sage: spher.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3', start_index=1) sage: cart.<x,y,z> = R3.chart() sage: Phi = U.diff_map(R3, {(spher, cart): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='Phi', latex_name=r'\Phi') sage: v = U.vector_field(name='v') sage: v[:] = 0, 1 sage: v.display() v = d/dph sage: pv = Phi.pushforward(v); pv Vector field Phi^*(v) along the Open subset U of the 2-dimensional differentiable manifold S^2 with values on the 3-dimensional differentiable manifold R^3 sage: pv.display() Phi^*(v) = -sin(ph)*sin(th) d/dx + cos(ph)*sin(th) d/dy Pushforward of a vector field on the real line to the `\RR^3`, via a helix embedding:: sage: R.<t> = RealLine() sage: Psi = R.diff_map(R3, [cos(t), sin(t), t], name='Psi', ....: latex_name=r'\Psi') sage: u = R.vector_field(name='u') sage: u[0] = 1 sage: u.display() u = d/dt sage: pu = Psi.pushforward(u); pu Vector field Psi^*(u) along the Real number line R with values on the 3-dimensional differentiable manifold R^3 sage: pu.display() Psi^*(u) = -sin(t) d/dx + cos(t) d/dy + d/dz """ from sage.tensor.modules.comp import (Components, CompWithSym, CompFullySym, CompFullyAntiSym) from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal vmodule = tensor.base_module() dest_map = vmodule.destination_map() dom1 = tensor.domain() ambient_dom1 = dest_map.codomain() if not ambient_dom1.is_subset(self._domain): raise ValueError("the {} does not take its ".format(tensor) + "values on the domain of the {}".format(self)) (ncon, ncov) = tensor.tensor_type() if ncov != 0: raise ValueError("the pushforward cannot be taken on a tensor " + "with some covariant part") if ncon == 0: raise ValueError("the pushforward cannot be taken on a scalar " + "field") if dest_map != dom1.identity_map(): raise NotImplementedError("the case of a non-trivial destination" + " map is not implemented yet") if not isinstance(tensor, TensorFieldParal): raise NotImplementedError("the case of a non-parallelizable " + "domain is not implemented yet") # A pair of charts (chart1, chart2) where the computation # is feasable is searched, privileging the default chart of the # map's domain for chart1 chart1 = None; chart2 = None def_chart1 = dom1.default_chart() def_chart2 = self._codomain.default_chart() if (def_chart1._frame in tensor._components and (def_chart1, def_chart2) in self._coord_expression): chart1 = def_chart1 chart2 = def_chart2 else: for (chart1n, chart2n) in self._coord_expression: if (chart2n == def_chart2 and chart1n._frame in tensor._components): chart1 = chart1n chart2 = def_chart2 break if chart2 is None: # It is not possible to have def_chart2 as chart for # expressing the result; any other chart is then looked for: for (chart1n, chart2n) in self._coord_expression: if chart1n._frame in tensor._components: chart1 = chart1n chart2 = chart2n break if chart1 is None: # Computations of components are attempted chart1 = dom1.default_chart() tensor.comp(chart1.frame()) for chart2n in self._codomain.atlas(): try: self.coord_functions(chart1, chart2n) chart2 = chart2n break except ValueError: pass else: raise ValueError("no pair of charts could be found to " + "compute the pushforward of " + "the {} by the {}".format(tensor, self)) # Vector field module for the result: fmodule2 = dom1.vector_field_module(dest_map=self) # frame2 = fmodule2.basis(from_frame=chart2.frame()) si1 = dom1.start_index() si2 = fmodule2._sindex ring2 = fmodule2._ring of2 = fmodule2._output_formatter # Computation at the component level: tcomp = tensor._components[chart1.frame()] # Construction of the pushforward components (ptcomp): if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2, sym=tcomp._sym, antisym=tcomp._antisym) else: ptcomp = Components(ring2, frame2, ncon, start_index=si2, output_formatter=of2) # Computation of the pushforward components: jacob = self.differential_functions(chart1=chart1, chart2=chart2) si2 = chart2.domain().start_index() for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in dom1.index_generator(ncon): t = tcomp[[ind_old]].coord_function(chart1) for i in range(ncon): t *= jacob[ind_new[i]-si2, ind_old[i]-si1] res += t ptcomp[ind_new] = res # Name of the result: resu_name = None resu_latex_name = None if self._name is not None and tensor._name is not None: resu_name = self._name + '^*(' + tensor._name + ')' if self._latex_name is not None and tensor._latex_name is not None: resu_latex_name = self._latex_name + '^*' + tensor._latex_name # Creation of the result with the components obtained above: resu = fmodule2.tensor_from_comp((ncon, 0), ptcomp, name=resu_name, latex_name=resu_latex_name) return resu
def pushforward(self, tensor): r""" Pushforward operator associated with the embedding. INPUT: - ``tensor`` -- instance of :class:`~sage.geometry.manifolds.tensorfield.TensorField` representing a fully contravariant tensor field `T` on the submanifold, i.e. a tensor field of type (k,0), with k a positive integer. The case k=0 corresponds to a scalar field. OUTPUT: - instance of :class:`~sage.geometry.manifolds.tensorfield.TensorField` representing a field of fully contravariant tensors of the ambient manifold, field defined on the subset occupied by the submanifold. EXAMPLES: Pushforward of a vector field defined on `S^2`, submanifold of `\RR^3`:: sage: Manifold._clear_cache_() # for doctests only sage: M = Manifold(3, 'R^3', r'\RR^3', start_index=1) sage: c_cart.<x,y,z> = M.chart() # Cartesian coordinates on R^3 sage: S = M.submanifold(2, 'S^2', start_index=1) sage: U = S.open_subset('U') # U = S minus two poles sage: c_spher.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') # spherical coordinates on U sage: S.def_embedding( S.diff_mapping(M, [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)], name='i', latex_name=r'\iota') ) sage: v = U.vector_field(name='v') sage: v[2] = 1 ; v.display() # azimuthal vector field on S^2 v = d/dph sage: iv = S.pushforward(v) ; iv vector field 'i_*(v)' along the open subset 'U' of the 2-dimensional submanifold 'S^2' of the 3-dimensional manifold 'R^3' with values on the 3-dimensional manifold 'R^3' sage: iv.display() i_*(v) = -sin(ph)*sin(th) d/dx + cos(ph)*sin(th) d/dy The components of the pushforward vector are scalar fields on the submanifold:: sage: iv.comp()[[1]] scalar field on the open subset 'U' of the 2-dimensional submanifold 'S^2' of the 3-dimensional manifold 'R^3' Pushforward of a tangent vector to a helix, submanifold of `\RR^3`:: sage: H = M.submanifold(1, 'H') sage: c_t.<t> = H.chart() sage: H.def_embedding( H.diff_mapping(M, [cos(t), sin(t), t], name='iH') ) sage: u = H.vector_field(name='u') sage: u[0] = 1 ; u.display() # tangent vector to the helix u = d/dt sage: iu = H.pushforward(u) ; iu vector field 'iH_*(u)' along the 1-dimensional submanifold 'H' of the 3-dimensional manifold 'R^3' with values on the 3-dimensional manifold 'R^3' sage: iu.display() iH_*(u) = -sin(t) d/dx + cos(t) d/dy + d/dz """ from tensorfield import TensorFieldParal from sage.tensor.modules.comp import Components, CompWithSym, \ CompFullySym, CompFullyAntiSym dom1 = tensor._domain if not dom1.is_subset(self): raise TypeError("The tensor field is not defined on " + str(self)) (ncon, ncov) = tensor._tensor_type if ncov != 0: raise TypeError("The pushforward cannot be taken on a tensor " + "with some covariant part.") embed = self._embedding resu_name = None ; resu_latex_name = None if embed._name is not None and tensor._name is not None: resu_name = embed._name + '_*(' + tensor._name + ')' if embed._latex_name is not None and tensor._latex_name is not None: resu_latex_name = embed._latex_name + '_*' + tensor._latex_name if ncon == 0: raise NotImplementedError("The case of a scalar field is not " + "implemented yet.") if not isinstance(tensor, TensorFieldParal): raise NotImplementedError("The case of a non-parallelizable " + "domain is not implemented yet.") # A pair of charts (chart1, chart2) where the computation # is feasable is searched, privileging the default chart of the # start domain for chart1 chart1 = None; chart2 = None def_chart1 = dom1._def_chart def_chart2 = embed._codomain._def_chart if def_chart1._frame in tensor._components and \ (def_chart1, def_chart2) in embed._coord_expression: chart1 = def_chart1 chart2 = def_chart2 else: for (chart1n, chart2n) in embed._coord_expression: if chart2n == def_chart2 and \ chart1n._frame in tensor._components: chart1 = chart1n chart2 = def_chart2 break if chart2 is None: # It is not possible to have def_chart2 as chart for # expressing the result; any other chart is then looked for: for (chart1n, chart2n) in self._coord_expression: if chart1n._frame in tensor._components: chart1 = chart1n chart2 = chart2n break if chart1 is None: raise ValueError("No common chart could be find to compute " + "the pushforward of the tensor field.") fmodule2 = dom1.vector_field_module(dest_map=embed) frame2 = fmodule2.basis(from_frame=chart2._frame) si1 = dom1._manifold._sindex si2 = fmodule2._sindex ring2 = fmodule2._ring of2 = fmodule2._output_formatter # Computation at the component level: tcomp = tensor._components[chart1._frame] # Construction of the pushforward components (ptcomp): if isinstance(tcomp, CompFullySym): ptcomp = CompFullySym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompFullyAntiSym): ptcomp = CompFullyAntiSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2) elif isinstance(tcomp, CompWithSym): ptcomp = CompWithSym(ring2, frame2, ncon, start_index=si2, output_formatter=of2, sym=tcomp._sym, antisym=tcomp._antisym) else: ptcomp = Components(ring2, frame2, ncon, start_index=si2, output_formatter=of2) phi = embed._coord_expression[(chart1, chart2)] jacob = phi.jacobian() # X2 coordinates expressed in terms of X1 ones via the mapping: # coord2_1 = phi(*(chart1._xx)) si1 = self._sindex si2 = self._ambient_manifold._sindex for ind_new in ptcomp.non_redundant_index_generator(): res = 0 for ind_old in self.index_generator(ncon): t = tcomp[[ind_old]].function_chart(chart1) for i in range(ncon): t *= jacob[ind_new[i]-si2][ind_old[i]-si1] res += t ptcomp[ind_new] = res resu = fmodule2.tensor_from_comp((ncon, 0), ptcomp, name=resu_name, latex_name=resu_latex_name) return resu