Exemple #1
0
    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)
Exemple #2
0
    def exterior_derivative(self):
        r"""
        Compute the exterior derivative of ``self``.

        OUTPUT:

        - a :class:`DiffFormParal` representing the exterior
          derivative of the differential form

        EXAMPLES:

        Exterior derivative of a 1-form on a 4-dimensional manifold::

            sage: M = Manifold(4, 'M')
            sage: c_txyz.<t,x,y,z> = M.chart()
            sage: a = M.one_form('A')
            sage: a[:] = (t*x*y*z, z*y**2, x*z**2, x**2 + y**2)
            sage: da = a.exterior_derivative() ; da
            2-form dA on the 4-dimensional differentiable manifold M
            sage: da.display()
            dA = -t*y*z dt/\dx - t*x*z dt/\dy - t*x*y dt/\dz
             + (-2*y*z + z^2) dx/\dy + (-y^2 + 2*x) dx/\dz
             + (-2*x*z + 2*y) dy/\dz
            sage: latex(da)
            \mathrm{d}A

        The result is cached, i.e. is not recomputed unless ``a`` is changed::

            sage: a.exterior_derivative() is da
            True

        Instead of invoking the method :meth:`exterior_derivative`, one may
        use the global function
        :func:`~sage.manifolds.utilities.exterior_derivative`
        or its alias :func:`~sage.manifolds.utilities.xder`::

            sage: from sage.manifolds.utilities import xder
            sage: xder(a) is a.exterior_derivative()
            True

        The exterior derivative is nilpotent::

            sage: dda = da.exterior_derivative() ; dda
            3-form ddA on the 4-dimensional differentiable manifold M
            sage: dda.display()
            ddA = 0
            sage: dda == 0
            True

        Let us check Cartan's identity::

            sage: v = M.vector_field(name='v')
            sage: v[:] = -y, x, t, z
            sage: a.lie_der(v) == v.contract(xder(a)) + xder(a(v)) # long time
            True

        """
        from sage.calculus.functional import diff
        from sage.tensor.modules.format_utilities import (format_unop_txt,
                                                          format_unop_latex)
        from sage.tensor.modules.comp import CompFullyAntiSym
        from sage.manifolds.differentiable.vectorframe import CoordFrame
        fmodule = self._fmodule  # shortcut
        rname = format_unop_txt('d', self._name)
        rlname = format_unop_latex(r'\mathrm{d}', self._latex_name)
        resu = fmodule.alternating_form(self._tensor_rank + 1,
                                        name=rname,
                                        latex_name=rlname)
        # 1/ List of all coordinate frames in which the components of self
        # are known
        coord_frames = []
        for frame in self._components:
            if isinstance(frame, CoordFrame):
                coord_frames.append(frame)
        if not coord_frames:
            # A coordinate frame is searched, at the price of a change of
            # frame, privileging the frame of the domain's default chart
            dom = self._domain
            def_coordf = dom._def_chart._frame
            for frame in self._components:
                if (frame, def_coordf) in dom._frame_changes:
                    self.comp(def_coordf, from_basis=frame)
                    coord_frames = [def_coordf]
                    break
            if not coord_frames:
                for chart in dom._atlas:
                    if chart != dom._def_chart:  # the case def_chart is
                        # treated above
                        coordf = chart._frame
                        for frame in self._components:
                            if (frame, coordf) in dom._frame_changes:
                                self.comp(coordf, from_basis=frame)
                                coord_frames[coordf]
                                break
                        if coord_frames:
                            break
        # 2/ The computation:
        for frame in coord_frames:
            chart = frame._chart
            sc = self._components[frame]
            dc = CompFullyAntiSym(fmodule._ring,
                                  frame,
                                  self._tensor_rank + 1,
                                  start_index=fmodule._sindex,
                                  output_formatter=fmodule._output_formatter)
            for ind, val in sc._comp.items():
                for i in fmodule.irange():
                    ind_d = (i, ) + ind
                    if len(ind_d) == len(set(ind_d)):
                        # all indices are different
                        dc[[ind_d]] += \
                           val.coord_function(chart).diff(i).scalar_field()
            resu._components[frame] = dc
        return resu
Exemple #3
0
    def wedge(self, other):
        r"""
        Exterior product of ``self`` with the alternating form ``other``.

        INPUT:

        - ``other`` -- an alternating form

        OUTPUT:

        - instance of :class:`FreeModuleAltForm` representing the exterior
          product ``self/\other``

        EXAMPLES:

        Exterior product of two linear forms::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: a = M.linear_form('A')
            sage: a[:] = [1,-3,4]
            sage: b = M.linear_form('B')
            sage: b[:] = [2,-1,2]
            sage: c = a.wedge(b) ; c
            Alternating form A/\B of degree 2 on the Rank-3 free module M
             over the Integer Ring
            sage: c.display()
            A/\B = 5 e^0/\e^1 - 6 e^0/\e^2 - 2 e^1/\e^2
            sage: latex(c)
            A\wedge B
            sage: latex(c.display())
            A\wedge B = 5 e^0\wedge e^1 -6 e^0\wedge e^2 -2 e^1\wedge e^2

        Test of the computation::

            sage: a.wedge(b) == a*b - b*a
            True

        Exterior product of a linear form and an alternating form of degree 2::

            sage: d = M.linear_form('D')
            sage: d[:] = [-1,2,4]
            sage: s = d.wedge(c) ; s
            Alternating form D/\A/\B of degree 3 on the Rank-3 free module M
             over the Integer Ring
            sage: s.display()
            D/\A/\B = 34 e^0/\e^1/\e^2

        Test of the computation::

            sage: s[0,1,2] == d[0]*c[1,2] + d[1]*c[2,0] + d[2]*c[0,1]
            True

        Let us check that the exterior product is associative::

            sage: d.wedge(a.wedge(b)) == (d.wedge(a)).wedge(b)
            True

        and that it is graded anticommutative::

            sage: a.wedge(b) == - b.wedge(a)
            True
            sage: d.wedge(c) == c.wedge(d)
            True

        """
        from .format_utilities import is_atomic
        if not isinstance(other, FreeModuleAltForm):
            raise TypeError("the second argument for the exterior product " +
                            "must be an alternating form")
        if other._tensor_rank == 0:
            return other*self
        if self._tensor_rank == 0:
            return self*other
        fmodule = self._fmodule
        basis = self.common_basis(other)
        if basis is None:
            raise ValueError("no common basis for the exterior product")
        rank_r = self._tensor_rank + other._tensor_rank
        cmp_s = self._components[basis]
        cmp_o = other._components[basis]
        cmp_r = CompFullyAntiSym(fmodule._ring, basis, rank_r,
                                 start_index=fmodule._sindex,
                                 output_formatter=fmodule._output_formatter)
        for ind_s, val_s in six.iteritems(cmp_s._comp):
            for ind_o, val_o in six.iteritems(cmp_o._comp):
                ind_r = ind_s + ind_o
                if len(ind_r) == len(set(ind_r)): # all indices are different
                    cmp_r[[ind_r]] += val_s * val_o
        result = fmodule.alternating_form(rank_r)
        result._components[basis] = cmp_r
        if self._name is not None and other._name is not None:
            sname = self._name
            oname = other._name
            if not is_atomic(sname):
                sname = '(' + sname + ')'
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            result._name = sname + '/\\' + oname
        if self._latex_name is not None and other._latex_name is not None:
            slname = self._latex_name
            olname = other._latex_name
            if not is_atomic(slname):
                slname = '(' + slname + ')'
            if not is_atomic(olname):
                olname = '(' + olname + ')'
            result._latex_name = slname + r'\wedge ' + olname
        return result
Exemple #4
0
        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
Exemple #5
0
    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 wedge(self, other):
        r"""
        Exterior product of ``self`` with the alternating contravariant
        tensor ``other``.

        INPUT:

        - ``other`` -- an alternating contravariant tensor

        OUTPUT:

        - instance of :class:`AlternatingContrTensor` representing the
          exterior product ``self ∧ other``

        EXAMPLES:

        Exterior product of two module elements::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: a = M([1,-3,4], basis=e, name='A')
            sage: b = M([2,-1,2], basis=e, name='B')
            sage: c = a.wedge(b) ; c
            Alternating contravariant tensor A∧B of degree 2 on the Rank-3
             free module M over the Integer Ring
            sage: c.display()
            A∧B = 5 e_0∧e_1 - 6 e_0∧e_2 - 2 e_1∧e_2
            sage: latex(c)
            A\wedge B
            sage: latex(c.display())
            A\wedge B = 5 e_{0}\wedge e_{1} -6 e_{0}\wedge e_{2}
             -2 e_{1}\wedge e_{2}

        Test of the computation::

            sage: a.wedge(b) == a*b - b*a
            True

        Exterior product of a module element and an alternating contravariant
        tensor of degree 2::

            sage: d = M([-1,2,4], basis=e, name='D')
            sage: s = d.wedge(c) ; s
            Alternating contravariant tensor D∧A∧B of degree 3 on the Rank-3
             free module M over the Integer Ring
            sage: s.display()
            D∧A∧B = 34 e_0∧e_1∧e_2

        Test of the computation::

            sage: s[0,1,2] == d[0]*c[1,2] + d[1]*c[2,0] + d[2]*c[0,1]
            True

        Let us check that the exterior product is associative::

            sage: d.wedge(a.wedge(b)) == (d.wedge(a)).wedge(b)
            True

        and that it is graded anticommutative::

            sage: a.wedge(b) == - b.wedge(a)
            True
            sage: d.wedge(c) == c.wedge(d)
            True

        """
        from sage.typeset.unicode_characters import unicode_wedge
        from .format_utilities import is_atomic
        if not isinstance(other, AlternatingContrTensor):
            raise TypeError("the second argument for the exterior product " +
                            "must be an alternating contravariant tensor")
        if other._tensor_rank == 0:
            return other * self
        fmodule = self._fmodule
        basis = self.common_basis(other)
        if basis is None:
            raise ValueError("no common basis for the exterior product")
        rank_r = self._tensor_rank + other._tensor_rank
        cmp_s = self._components[basis]
        cmp_o = other._components[basis]
        cmp_r = CompFullyAntiSym(fmodule._ring,
                                 basis,
                                 rank_r,
                                 start_index=fmodule._sindex,
                                 output_formatter=fmodule._output_formatter)
        for ind_s, val_s in cmp_s._comp.items():
            for ind_o, val_o in cmp_o._comp.items():
                ind_r = ind_s + ind_o
                if len(ind_r) == len(set(ind_r)):  # all indices are different
                    cmp_r[[ind_r]] += val_s * val_o
        result = fmodule.alternating_contravariant_tensor(rank_r)
        result._components[basis] = cmp_r
        if self._name is not None and other._name is not None:
            sname = self._name
            oname = other._name
            if not is_atomic(sname):
                sname = '(' + sname + ')'
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            result._name = sname + unicode_wedge + oname
        if self._latex_name is not None and other._latex_name is not None:
            slname = self._latex_name
            olname = other._latex_name
            if not is_atomic(slname):
                slname = r'\left(' + slname + r'\right)'
            if not is_atomic(olname):
                olname = r'\left(' + olname + r'\right)'
            result._latex_name = slname + r'\wedge ' + olname
        return result
Exemple #7
0
    def exterior_der(self):
        r"""
        Compute the exterior derivative of the differential form.

        OUTPUT:

        - the exterior derivative of ``self``.

        EXAMPLE:

        Exterior derivative of a 1-form on a 4-dimensional manifold::

            sage: M = Manifold(4, 'M')
            sage: c_txyz.<t,x,y,z> = M.chart()
            sage: a = M.one_form('A')
            sage: a[:] = (t*x*y*z, z*y**2, x*z**2, x**2 + y**2)
            sage: da = a.exterior_der() ; da
            2-form 'dA' on the 4-dimensional manifold 'M'
            sage: da.display()
            dA = -t*y*z dt/\dx - t*x*z dt/\dy - t*x*y dt/\dz + (-2*y*z + z^2) dx/\dy + (-y^2 + 2*x) dx/\dz + (-2*x*z + 2*y) dy/\dz
            sage: latex(da)
            \mathrm{d}A

        The exterior derivative is nilpotent::

            sage: dda = da.exterior_der() ; dda
            3-form 'ddA' on the 4-dimensional manifold 'M'
            sage: dda.display()
            ddA = 0
            sage: dda == 0
            True

        """
        from sage.calculus.functional import diff
        from sage.tensor.modules.format_utilities import format_unop_txt, \
                                                         format_unop_latex
        from sage.tensor.modules.comp import CompFullyAntiSym
        from vectorframe import CoordFrame
        if self._exterior_derivative is None:
            # A new computation is necessary:
            fmodule = self._fmodule  # shortcut
            rname = format_unop_txt('d', self._name)
            rlname = format_unop_latex(r'\mathrm{d}', self._latex_name)
            self._exterior_derivative = fmodule.alternating_form(
                self._tensor_rank + 1, name=rname, latex_name=rlname)
            # 1/ List of all coordinate frames in which the components of self
            # are known
            coord_frames = []
            for frame in self._components:
                if isinstance(frame, CoordFrame):
                    coord_frames.append(frame)
            if coord_frames == []:
                # A coordinate frame is searched, at the price of a change of
                # frame, priveleging the frame of the domain's default chart
                dom = self._domain
                def_coordf = dom._def_chart._frame
                for frame in self._components:
                    if (frame, def_coordf) in dom._frame_changes:
                        self.comp(def_coordf, from_basis=frame)
                        coord_frames = [def_coordf]
                        break
                if coord_frames == []:
                    for chart in dom._atlas:
                        if chart != dom._def_chart:  # the case def_chart is treated above
                            coordf = chart._frame
                            for frame in self._components:
                                if (frame, coordf) in dom._frame_changes:
                                    self.comp(coordf, from_basis=frame)
                                    coord_frames[coordf]
                                    break
                            if coord_frames != []:
                                break
            # 2/ The computation:
            for frame in coord_frames:
                chart = frame._chart
                sc = self._components[frame]
                dc = CompFullyAntiSym(
                    fmodule._ring,
                    frame,
                    self._tensor_rank + 1,
                    start_index=fmodule._sindex,
                    output_formatter=fmodule._output_formatter)
                for ind, val in sc._comp.iteritems():
                    for i in fmodule.irange():
                        ind_d = (i, ) + ind
                        if len(ind_d) == len(set(ind_d)):
                            # all indices are different
                            dc[[ind_d]] += \
                               val.function_chart(chart).diff(i).scalar_field()
                self._exterior_derivative._components[frame] = dc
        return self._exterior_derivative
Exemple #8
0
    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