def __invert__(self):
        r"""
        Return the inverse automorphism.

        OUTPUT:

        - instance of :class:`FreeModuleAutomorphism` representing the
          automorphism that is the inverse of ``self``.

        EXAMPLES:

        Inverse of an automorphism of a rank-3 free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: a = M.automorphism(name='a')
            sage: a[e,:] = [[1,0,0],[0,-1,2],[0,1,-3]]
            sage: a.inverse()
            Automorphism a^(-1) of the Rank-3 free module M over the Integer
             Ring
            sage: a.inverse().parent()
            General linear group of the Rank-3 free module M over the Integer
             Ring

        Check that ``a.inverse()`` is indeed the inverse automorphism::

            sage: a.inverse() * a
            Identity map of the Rank-3 free module M over the Integer Ring
            sage: a * a.inverse()
            Identity map of the Rank-3 free module M over the Integer Ring
            sage: a.inverse().inverse() == a
            True

        Another check is::

            sage: a.inverse().matrix(e)
            [ 1  0  0]
            [ 0 -3 -2]
            [ 0 -1 -1]
            sage: a.inverse().matrix(e) == (a.matrix(e))^(-1)
            True

        The inverse is cached (as long as ``a`` is not modified)::

            sage: a.inverse() is a.inverse()
            True

        If ``a`` is modified, the inverse is automatically recomputed::

            sage: a[0,0] = -1
            sage: a.matrix(e)
            [-1  0  0]
            [ 0 -1  2]
            [ 0  1 -3]
            sage: a.inverse().matrix(e)  # compare with above
            [-1  0  0]
            [ 0 -3 -2]
            [ 0 -1 -1]

        Shortcuts for :meth:`inverse` are the operator ``~`` and the exponent
        ``-1``::

            sage: ~a is a.inverse()
            True
            sage: (a)^(-1) is a.inverse()
            True

        The inverse of the identity map is of course itself::

            sage: id = M.identity_map()
            sage: id.inverse() is id
            True

        and we have::

            sage: a*(a)^(-1) == id
            True
            sage: (a)^(-1)*a == id
            True

        If the name could cause some confusion, a bracket is added around the
        element before taking the inverse::

            sage: c = M.automorphism(name='a^(-1)*b')
            sage: c[e,:] = [[1,0,0],[0,-1,1],[0,2,-1]]
            sage: c.inverse()
            Automorphism (a^(-1)*b)^(-1) of the Rank-3 free module M over the
             Integer Ring

        """
        from .comp import Components
        if self._is_identity:
            return self
        if self._inverse is None:
            from sage.tensor.modules.format_utilities import is_atomic
            if self._name is None:
                inv_name = None
            else:
                if is_atomic(self._name, ['*']):
                    inv_name = self._name + '^(-1)'
                else:
                    inv_name = '(' + self._name + ')^(-1)'
            if self._latex_name is None:
                inv_latex_name = None
            else:
                if is_atomic(self._latex_name, ['\\circ', '\\otimes']):
                    inv_latex_name = self._latex_name + r'^{-1}'
                else:
                    inv_latex_name = r'\left(' + self._latex_name + \
                                     r'\right)^{-1}'
            fmodule = self._fmodule
            si = fmodule._sindex
            nsi = fmodule._rank + si
            self._inverse = self.__class__(fmodule, inv_name, inv_latex_name)
            for basis in self._components:
                try:
                    mat = self.matrix(basis)
                except (KeyError, ValueError):
                    continue
                mat_inv = mat.inverse()
                cinv = Components(fmodule._ring, basis, 2, start_index=si,
                                  output_formatter=fmodule._output_formatter)
                for i in range(si, nsi):
                    for j in range(si, nsi):
                        cinv[i, j] = mat_inv[i-si,j-si]
                self._inverse._components[basis] = cinv
            self._inverse._inverse = self
        return self._inverse
Esempio n. 2
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
Esempio n. 3
0
    def wedge(self, other):
        r"""
        Exterior product with another differential form.

        INPUT:

        - ``other`` -- another differential form (on the same manifold)

        OUTPUT:

        - a :class:`DiffForm` of the exterior product ``self/\other``

        EXAMPLES:

        Exterior product of two 1-forms on the 2-sphere::


            sage: M = Manifold(2, 'S^2', start_index=1) # the 2-dimensional sphere S^2
            sage: U = M.open_subset('U') ; V = M.open_subset('V')
            sage: M.declare_union(U,V)   # S^2 is the union of U and V
            sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() # stereographic coord. (North and South)
            sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
            ....:                intersection_name='W', restrictions1= x^2+y^2!=0,
            ....:                restrictions2= u^2+v^2!=0)
            sage: uv_to_xy = xy_to_uv.inverse()
            sage: W = U.intersection(V) # The complement of the two poles
            sage: e_xy = c_xy.frame() ; e_uv = c_uv.frame()
            sage: a = M.diff_form(1, name='a')
            sage: a[e_xy,:] = y, x
            sage: a.add_comp_by_continuation(e_uv, W, c_uv)
            sage: b = M.diff_form(1, name='b')
            sage: b[e_xy,:] = x^2 + y^2, y
            sage: b.add_comp_by_continuation(e_uv, W, c_uv)
            sage: c = a.wedge(b); c
            2-form a/\b on the 2-dimensional differentiable manifold S^2
            sage: c.display(e_xy)
            a/\b = (-x^3 - (x - 1)*y^2) dx/\dy
            sage: c.display(e_uv)
            a/\b = -(v^2 - u)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du/\dv

        """
        from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm
        from sage.tensor.modules.format_utilities import is_atomic
        if self._domain.is_subset(other._domain):
            if not self._ambient_domain.is_subset(other._ambient_domain):
                raise ValueError(
                    "incompatible ambient domains for exterior product")
        elif other._domain.is_subset(self._domain):
            if not other._ambient_domain.is_subset(self._ambient_domain):
                raise ValueError(
                    "incompatible ambient domains for exterior product")
        dom_resu = self._domain.intersection(other._domain)
        ambient_dom_resu = self._ambient_domain.intersection(
            other._ambient_domain)
        self_r = self.restrict(dom_resu)
        other_r = other.restrict(dom_resu)
        if ambient_dom_resu.is_manifestly_parallelizable():
            # call of the FreeModuleAltForm version:
            return FreeModuleAltForm.wedge(self_r, other_r)
        # otherwise, the result is created here:
        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 + ')'
            resu_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 + ')'
            resu_latex_name = slname + r'\wedge ' + olname
        dest_map = self._vmodule._dest_map
        dest_map_resu = dest_map.restrict(dom_resu,
                                          subcodomain=ambient_dom_resu)
        vmodule = dom_resu.vector_field_module(dest_map=dest_map_resu)
        resu_degree = self._tensor_rank + other._tensor_rank
        resu = vmodule.alternating_form(resu_degree,
                                        name=resu_name,
                                        latex_name=resu_latex_name)
        for dom in self_r._restrictions:
            if dom in other_r._restrictions:
                resu._restrictions[dom] = self_r._restrictions[dom].wedge(
                    other_r._restrictions[dom])
        return resu
Esempio n. 4
0
    def interior_product(self, form):
        r"""
        Interior product with an alternating form.

        If ``self`` is an alternating contravariant tensor `A` of degree `p`
        and `B` is an alternating form of degree `q\geq p` on the same free
        module, the interior product of `A` by `B` is the alternating form
        `\iota_A B` of degree `q-p` defined by

        .. MATH::

            (\iota_A B)_{i_1\ldots i_{q-p}} = A^{k_1\ldots k_p}
                            B_{k_1\ldots k_p i_1\ldots i_{q-p}}

        .. NOTE::

            ``A.interior_product(B)`` yields the same result as
            ``A.contract(0,..., p-1, B, 0,..., p-1)`` (cf.
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`),
            but ``interior_product`` is more efficient, the alternating
            character of `A` being not used to reduce the computation in
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`

        INPUT:

        - ``form`` -- alternating form `B` (instance of
          :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`);
          the degree of `B` must be at least equal to the degree of ``self``

        OUTPUT:

        - element of the base ring (case `p=q`) or
          :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`
          (case `p<q`) representing the interior product `\iota_A B`, where `A`
          is ``self``

        .. SEEALSO::

            :meth:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm.interior_product`
            for the interior product of an alternating form by an alternating
            contravariant tensor

        EXAMPLES:

        Let us consider a rank-4 free module::

            sage: M = FiniteRankFreeModule(ZZ, 4, name='M', start_index=1)
            sage: e = M.basis('e')

        and various interior products on it, starting with a module element
        (``p=1``) and a linear form (``q=1``)::

            sage: a = M([-2,1,2,3], basis=e, name='A')
            sage: b = M.linear_form(name='B')
            sage: b[:] = [2, 0, -3, 4]
            sage: c = a.interior_product(b); c
            2
            sage: c == a.contract(b)
            True

        Case  ``p=1`` and ``q=3``::

            sage: b = M.alternating_form(3, name='B')
            sage: b[1,2,3], b[1,2,4], b[1,3,4], b[2,3,4] = 3, -1, 2, 5
            sage: c = a.interior_product(b); c
            Alternating form i_A B of degree 2 on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = 3 e^1/\e^2 + 3 e^1/\e^3 - 3 e^1/\e^4 + 9 e^2/\e^3 - 8 e^2/\e^4 + e^3/\e^4
            sage: latex(c)
            \iota_{A} B
            sage: c == a.contract(b)
            True

        Case  ``p=2`` and ``q=3``::

            sage: a = M.alternating_contravariant_tensor(2, name='A')
            sage: a[1,2], a[1,3], a[1,4] = 2, -5, 3
            sage: a[2,3], a[2,4], a[3,4] = -1, 4, 2
            sage: c = a.interior_product(b); c
            Linear form i_A B on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = -6 e^1 + 56 e^2 - 40 e^3 - 34 e^4
            sage: c == a.contract(0, 1, b, 0, 1)  # contraction on all indices of a
            True

        Case  ``p=2`` and ``q=4``::

            sage: b = M.alternating_form(4, name='B')
            sage: b[1,2,3,4] = 5
            sage: c = a.interior_product(b); c
            Alternating form i_A B of degree 2 on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = 20 e^1/\e^2 - 40 e^1/\e^3 - 10 e^1/\e^4 + 30 e^2/\e^3 + 50 e^2/\e^4 + 20 e^3/\e^4
            sage: c == a.contract(0, 1, b, 0, 1)
            True

        Case  ``p=2`` and ``q=2``::

            sage: b = M.alternating_form(2)
            sage: b[1,2], b[1,3], b[1,4] = 6, 0, -2
            sage: b[2,3], b[2,4], b[3,4] = 2, 3, 4
            sage: c = a.interior_product(b); c
            48
            sage: c == a.contract(0, 1, b, 0, 1)
            True

        Case  ``p=3`` and ``q=3``::

            sage: a = M.alternating_contravariant_tensor(3, name='A')
            sage: a[1,2,3], a[1,2,4], a[1,3,4], a[2,3,4] = -3, 2, 8, -5
            sage: b = M.alternating_form(3, name='B')
            sage: b[1,2,3], b[1,2,4], b[1,3,4], b[2,3,4] = 3, -1, 2, 5
            sage: c = a.interior_product(b); c
            -120
            sage: c == a.contract(0, 1, 2, b, 0, 1, 2)
            True

        Case  ``p=3`` and ``q=4``::

            sage: b = M.alternating_form(4, name='B')
            sage: b[1,2,3,4] = 5
            sage: c = a.interior_product(b); c
            Linear form i_A B on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = 150 e^1 + 240 e^2 - 60 e^3 - 90 e^4
            sage: c == a.contract(0, 1, 2, b, 0, 1, 2)
            True

        Case  ``p=4`` and ``q=4``::

            sage: a = M.alternating_contravariant_tensor(4, name='A')
            sage: a[1,2,3,4] = -2
            sage: c = a.interior_product(b); c
            -240
            sage: c == a.contract(0, 1, 2, 3, b, 0, 1, 2, 3)
            True

        """
        from .format_utilities import is_atomic
        from .free_module_alt_form import FreeModuleAltForm
        if not isinstance(form, FreeModuleAltForm):
            raise TypeError("{} is not an alternating form".format(form))
        p_res = form._tensor_rank - self._tensor_rank  # degree of the result
        if self._tensor_rank == 1:
            # Case p = 1:
            res = self.contract(form)  # contract() deals efficiently with
            # the antisymmetry for p = 1
        else:
            # Case p > 1:
            if form._fmodule != self._fmodule:
                raise ValueError(
                    "{} is not defined on the same ".format(form) +
                    "module as the {}".format(self))
            if form._tensor_rank < self._tensor_rank:
                raise ValueError(
                    "the degree of the {} is lower ".format(form) +
                    "than that of the {}".format(self))
            # Interior product at the component level:
            basis = self.common_basis(form)
            if basis is None:
                raise ValueError("no common basis for the interior product")
            comp = self._components[basis].interior_product(
                form._components[basis])
            if p_res == 0:
                res = comp  # result is a scalar
            else:
                res = self._fmodule.tensor_from_comp((0, p_res), comp)
        # Name of the result
        res_name = None
        if self._name is not None and form._name is not None:
            sname = self._name
            oname = form._name
            if not is_atomic(sname):
                sname = '(' + sname + ')'
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            res_name = 'i_' + sname + ' ' + oname
        res_latex_name = None
        if self._latex_name is not None and form._latex_name is not None:
            slname = self._latex_name
            olname = form._latex_name
            if not is_atomic(olname):
                olname = r'\left(' + olname + r'\right)'
            res_latex_name = r'\iota_{' + slname + '} ' + olname
        if p_res == 0:
            if res_name:
                try:  # there is no guarantee that base ring elements have
                    # set_name
                    res.set_name(res_name, latex_name=res_latex_name)
                except (AttributeError, TypeError):
                    pass
        else:
            res.set_name(res_name, latex_name=res_latex_name)
        return res
Esempio n. 5
0
    def display(self, basis=None, format_spec=None):
        r"""
        Display the alternating form ``self`` in terms of its expansion w.r.t.
        a given module basis.

        The expansion is actually performed onto exterior products of elements
        of the cobasis (dual basis) associated with ``basis`` (see examples
        below). The output is either text-formatted (console mode) or
        LaTeX-formatted (notebook mode).

        INPUT:

        - ``basis`` -- (default: ``None``) basis of the free module with
          respect to which the alternating form is expanded; if none is
          provided, the module's default basis is assumed
        - ``format_spec`` -- (default: ``None``) format specification passed
          to ``self._fmodule._output_formatter`` to format the output

        EXAMPLES:

        Display of an alternating form of degree 1 (linear form) on a rank-3
        free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: e.dual_basis()
            Dual basis (e^0,e^1,e^2) on the Rank-3 free module M over the Integer Ring
            sage: a = M.linear_form('a', latex_name=r'\alpha')
            sage: a[:] = [1,-3,4]
            sage: a.display(e)
            a = e^0 - 3 e^1 + 4 e^2
            sage: a.display()  # a shortcut since e is M's default basis
            a = e^0 - 3 e^1 + 4 e^2
            sage: latex(a.display())  # display in the notebook
            \alpha = e^0 -3 e^1 + 4 e^2

        A shortcut is ``disp()``::

            sage: a.disp()
            a = e^0 - 3 e^1 + 4 e^2

        Display of an alternating form of degree 2 on a rank-3 free module::

            sage: b = M.alternating_form(2, 'b', latex_name=r'\beta')
            sage: b[0,1], b[0,2], b[1,2] = 3, 2, -1
            sage: b.display()
            b = 3 e^0/\e^1 + 2 e^0/\e^2 - e^1/\e^2
            sage: latex(b.display())  # display in the notebook
            \beta = 3 e^0\wedge e^1 + 2 e^0\wedge e^2 -e^1\wedge e^2

        Display of an alternating form of degree 3 on a rank-3 free module::

            sage: c = M.alternating_form(3, 'c')
            sage: c[0,1,2] = 4
            sage: c.display()
            c = 4 e^0/\e^1/\e^2
            sage: latex(c.display())
            c = 4 e^0\wedge e^1\wedge e^2

        Display of a vanishing alternating form::

            sage: c[0,1,2] = 0  # the only independent component set to zero
            sage: c.is_zero()
            True
            sage: c.display()
            c = 0
            sage: latex(c.display())
            c = 0
            sage: c[0,1,2] = 4  # value restored for what follows

        Display in a basis which is not the default one::

            sage: aut = M.automorphism(matrix=[[0,1,0], [0,0,-1], [1,0,0]],
            ....:                      basis=e)
            sage: f = e.new_basis(aut, 'f')
            sage: a.display(f)
            a = 4 f^0 + f^1 + 3 f^2
            sage: a.disp(f)     # shortcut notation
            a = 4 f^0 + f^1 + 3 f^2
            sage: b.display(f)
            b = -2 f^0/\f^1 - f^0/\f^2 - 3 f^1/\f^2
            sage: c.display(f)
            c = -4 f^0/\f^1/\f^2

        The output format can be set via the argument ``output_formatter``
        passed at the module construction::

            sage: N = FiniteRankFreeModule(QQ, 3, name='N', start_index=1,
            ....:                   output_formatter=Rational.numerical_approx)
            sage: e = N.basis('e')
            sage: b = N.alternating_form(2, 'b')
            sage: b[1,2], b[1,3], b[2,3] = 1/3, 5/2, 4
            sage: b.display()  # default format (53 bits of precision)
            b = 0.333333333333333 e^1/\e^2 + 2.50000000000000 e^1/\e^3
             + 4.00000000000000 e^2/\e^3

        The output format is then controlled by the argument ``format_spec`` of
        the method :meth:`display`::

            sage: b.display(format_spec=10)  # 10 bits of precision
            b = 0.33 e^1/\e^2 + 2.5 e^1/\e^3 + 4.0 e^2/\e^3

        Check that the bug reported in :trac:`22520` is fixed::

            sage: M = FiniteRankFreeModule(SR, 2, name='M')
            sage: e = M.basis('e')
            sage: a = M.alternating_form(2)
            sage: a[0,1] = SR.var('t', domain='real')
            sage: a.display()
            t e^0/\e^1

        """
        from sage.misc.latex import latex
        from sage.tensor.modules.format_utilities import is_atomic, \
                                                         FormattedExpansion
        if basis is None:
            basis = self._fmodule._def_basis
        cobasis = basis.dual_basis()
        comp = self.comp(basis)
        terms_txt = []
        terms_latex = []
        for ind in comp.non_redundant_index_generator():
            ind_arg = ind + (format_spec,)
            coef = comp[ind_arg]
            if not (coef == 0):   # NB: coef != 0 would return False for
                                  # cases in which Sage cannot conclude
                                  # see :trac:`22520`
                bases_txt = []
                bases_latex = []
                for k in range(self._tensor_rank):
                    bases_txt.append(cobasis[ind[k]]._name)
                    bases_latex.append(latex(cobasis[ind[k]]))
                basis_term_txt = "/\\".join(bases_txt)
                basis_term_latex = r"\wedge ".join(bases_latex)
                coef_txt = repr(coef)
                if coef_txt == "1":
                    terms_txt.append(basis_term_txt)
                    terms_latex.append(basis_term_latex)
                elif coef_txt == "-1":
                    terms_txt.append("-" + basis_term_txt)
                    terms_latex.append("-" + basis_term_latex)
                else:
                    coef_latex = latex(coef)
                    if is_atomic(coef_txt):
                        terms_txt.append(coef_txt + " " + basis_term_txt)
                    else:
                        terms_txt.append("(" + coef_txt + ") " +
                                         basis_term_txt)
                    if is_atomic(coef_latex):
                        terms_latex.append(coef_latex + basis_term_latex)
                    else:
                        terms_latex.append(r"\left(" + coef_latex + \
                                           r"\right)" + basis_term_latex)
        if not terms_txt:
            expansion_txt = "0"
        else:
            expansion_txt = terms_txt[0]
            for term in terms_txt[1:]:
                if term[0] == "-":
                    expansion_txt += " - " + term[1:]
                else:
                    expansion_txt += " + " + term
        if not terms_latex:
            expansion_latex = "0"
        else:
            expansion_latex = terms_latex[0]
            for term in terms_latex[1:]:
                if term[0] == "-":
                    expansion_latex += term
                else:
                    expansion_latex += "+" + term
        if self._name is None:
            resu_txt = expansion_txt
        else:
            resu_txt = self._name + " = " + expansion_txt
        if self._latex_name is None:
            resu_latex = expansion_latex
        else:
            resu_latex = latex(self) + " = " + expansion_latex
        return FormattedExpansion(resu_txt, resu_latex)
Esempio n. 6
0
    def wedge(self, other):
        r"""
        Exterior product with another differential form.

        INPUT:

        - ``other`` -- another differential form (on the same manifold)

        OUTPUT:

        - a :class:`DiffForm` of the exterior product ``self/\other``

        EXAMPLES:

        Exterior product of two 1-forms on the 2-sphere::


            sage: M = Manifold(2, 'S^2', start_index=1) # the 2-dimensional sphere S^2
            sage: U = M.open_subset('U') ; V = M.open_subset('V')
            sage: M.declare_union(U,V)   # S^2 is the union of U and V
            sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart() # stereographic coord. (North and South)
            sage: xy_to_uv = c_xy.transition_map(c_uv, (x/(x^2+y^2), y/(x^2+y^2)),
            ....:                intersection_name='W', restrictions1= x^2+y^2!=0,
            ....:                restrictions2= u^2+v^2!=0)
            sage: uv_to_xy = xy_to_uv.inverse()
            sage: W = U.intersection(V) # The complement of the two poles
            sage: e_xy = c_xy.frame() ; e_uv = c_uv.frame()
            sage: a = M.diff_form(1, name='a')
            sage: a[e_xy,:] = y, x
            sage: a.add_comp_by_continuation(e_uv, W, c_uv)
            sage: b = M.diff_form(1, name='b')
            sage: b[e_xy,:] = x^2 + y^2, y
            sage: b.add_comp_by_continuation(e_uv, W, c_uv)
            sage: c = a.wedge(b); c
            2-form a/\b on the 2-dimensional differentiable manifold S^2
            sage: c.display(e_xy)
            a/\b = (-x^3 - (x - 1)*y^2) dx/\dy
            sage: c.display(e_uv)
            a/\b = -(v^2 - u)/(u^8 + 4*u^6*v^2 + 6*u^4*v^4 + 4*u^2*v^6 + v^8) du/\dv

        """
        from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm
        from sage.tensor.modules.format_utilities import is_atomic
        if self._domain.is_subset(other._domain):
            if not self._ambient_domain.is_subset(other._ambient_domain):
                raise ValueError("incompatible ambient domains for exterior product")
        elif other._domain.is_subset(self._domain):
            if not other._ambient_domain.is_subset(self._ambient_domain):
                raise ValueError("incompatible ambient domains for exterior product")
        dom_resu = self._domain.intersection(other._domain)
        ambient_dom_resu = self._ambient_domain.intersection(other._ambient_domain)
        self_r = self.restrict(dom_resu)
        other_r = other.restrict(dom_resu)
        if ambient_dom_resu.is_manifestly_parallelizable():
            # call of the FreeModuleAltForm version:
            return FreeModuleAltForm.wedge(self_r, other_r)
        # otherwise, the result is created here:
        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 + ')'
            resu_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 + ')'
            resu_latex_name = slname + r'\wedge ' + olname
        dest_map = self._vmodule._dest_map
        dest_map_resu = dest_map.restrict(dom_resu,
                                          subcodomain=ambient_dom_resu)
        vmodule = dom_resu.vector_field_module(dest_map=dest_map_resu)
        resu_degree = self._tensor_rank + other._tensor_rank
        resu = vmodule.alternating_form(resu_degree, name=resu_name,
                                        latex_name=resu_latex_name)
        for dom in self_r._restrictions:
            if dom in other_r._restrictions:
                resu._restrictions[dom] = self_r._restrictions[dom].wedge(
                                          other_r._restrictions[dom])
        return resu
Esempio n. 7
0
    def display(self, basis=None, format_spec=None):
        r"""
        Display the alternating contravariant tensor ``self`` in terms
        of its expansion w.r.t. a given module basis.

        The expansion is actually performed onto exterior products of
        elements of ``basis`` (see examples below). The output is either
        text-formatted (console mode) or LaTeX-formatted (notebook mode).

        INPUT:

        - ``basis`` -- (default: ``None``) basis of the free module with
          respect to which ``self`` is expanded; if none is provided,
          the module's default basis is assumed
        - ``format_spec`` -- (default: ``None``) format specification
          passed to ``self._fmodule._output_formatter`` to format the
          output

        EXAMPLES:

        Display of an alternating contravariant tensor of degree 2 on a rank-3
        free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: a = M.alternating_contravariant_tensor(2, 'a', latex_name=r'\alpha')
            sage: a[0,1], a[0,2], a[1,2] = 3, 2, -1
            sage: a.display()
            a = 3 e_0/\e_1 + 2 e_0/\e_2 - e_1/\e_2
            sage: latex(a.display())  # display in the notebook
            \alpha = 3 e_{0}\wedge e_{1} + 2 e_{0}\wedge e_{2} -e_{1}\wedge e_{2}

        Display of an alternating contravariant tensor of degree 3 on a rank-3
        free module::

            sage: b = M.alternating_contravariant_tensor(3, 'b')
            sage: b[0,1,2] = 4
            sage: b.display()
            b = 4 e_0/\e_1/\e_2
            sage: latex(b.display())
            b = 4 e_{0}\wedge e_{1}\wedge e_{2}

        Display of a vanishing alternating contravariant tensor::

            sage: b[0,1,2] = 0  # the only independent component set to zero
            sage: b.is_zero()
            True
            sage: b.display()
            b = 0
            sage: latex(b.display())
            b = 0
            sage: b[0,1,2] = 4  # value restored for what follows

        Display in a basis which is not the default one::

            sage: aut = M.automorphism(matrix=[[0,1,0], [0,0,-1], [1,0,0]],
            ....:                      basis=e)
            sage: f = e.new_basis(aut, 'f')
            sage: a.display(f)
            a = -2 f_0/\f_1 - f_0/\f_2 - 3 f_1/\f_2
            sage: a.disp(f)     # shortcut notation
            a = -2 f_0/\f_1 - f_0/\f_2 - 3 f_1/\f_2
            sage: b.display(f)
            b = -4 f_0/\f_1/\f_2

        The output format can be set via the argument ``output_formatter``
        passed at the module construction::

            sage: N = FiniteRankFreeModule(QQ, 3, name='N', start_index=1,
            ....:                   output_formatter=Rational.numerical_approx)
            sage: e = N.basis('e')
            sage: a = N.alternating_contravariant_tensor(2, 'a')
            sage: a[1,2], a[1,3], a[2,3] = 1/3, 5/2, 4
            sage: a.display()  # default format (53 bits of precision)
            a = 0.333333333333333 e_1/\e_2 + 2.50000000000000 e_1/\e_3
             + 4.00000000000000 e_2/\e_3

        The output format is then controlled by the argument ``format_spec`` of
        the method :meth:`display`::

            sage: a.display(format_spec=10)  # 10 bits of precision
            a = 0.33 e_1/\e_2 + 2.5 e_1/\e_3 + 4.0 e_2/\e_3

        """
        from sage.misc.latex import latex
        from sage.tensor.modules.format_utilities import (is_atomic,
                                                          FormattedExpansion)
        basis, format_spec = self._preparse_display(basis=basis,
                                                    format_spec=format_spec)
        comp = self.comp(basis)
        terms_txt = []
        terms_latex = []
        for ind in comp.non_redundant_index_generator():
            ind_arg = ind + (format_spec, )
            coef = comp[ind_arg]
            # Check whether the coefficient is zero, preferably via
            # the fast method is_trivial_zero():
            if hasattr(coef, 'is_trivial_zero'):
                zero_coef = coef.is_trivial_zero()
            else:
                zero_coef = coef == 0
            if not zero_coef:
                bases_txt = []
                bases_latex = []
                for k in range(self._tensor_rank):
                    bases_txt.append(basis[ind[k]]._name)
                    bases_latex.append(latex(basis[ind[k]]))
                basis_term_txt = "/\\".join(bases_txt)
                basis_term_latex = r"\wedge ".join(bases_latex)
                coef_txt = repr(coef)
                if coef_txt == "1":
                    terms_txt.append(basis_term_txt)
                    terms_latex.append(basis_term_latex)
                elif coef_txt == "-1":
                    terms_txt.append("-" + basis_term_txt)
                    terms_latex.append("-" + basis_term_latex)
                else:
                    coef_latex = latex(coef)
                    if is_atomic(coef_txt):
                        terms_txt.append(coef_txt + " " + basis_term_txt)
                    else:
                        terms_txt.append("(" + coef_txt + ") " +
                                         basis_term_txt)
                    if is_atomic(coef_latex):
                        terms_latex.append(coef_latex + basis_term_latex)
                    else:
                        terms_latex.append(r"\left(" + coef_latex + \
                                           r"\right)" + basis_term_latex)
        if not terms_txt:
            expansion_txt = "0"
        else:
            expansion_txt = terms_txt[0]
            for term in terms_txt[1:]:
                if term[0] == "-":
                    expansion_txt += " - " + term[1:]
                else:
                    expansion_txt += " + " + term
        if not terms_latex:
            expansion_latex = "0"
        else:
            expansion_latex = terms_latex[0]
            for term in terms_latex[1:]:
                if term[0] == "-":
                    expansion_latex += term
                else:
                    expansion_latex += "+" + term
        if self._name is None:
            resu_txt = expansion_txt
        else:
            resu_txt = self._name + " = " + expansion_txt
        if self._latex_name is None:
            resu_latex = expansion_latex
        else:
            resu_latex = latex(self) + " = " + expansion_latex
        return FormattedExpansion(resu_txt, resu_latex)
    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 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_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
    def interior_product(self, alt_tensor):
        r"""
        Interior product with an alternating contravariant tensor.

        If ``self`` is an alternating form `A` of degree `p` and `B` is an
        alternating contravariant tensor of degree `q\geq p` on the same free
        module, the interior product of `A` by `B` is the alternating
        contravariant tensor `\iota_A B` of degree `q-p` defined by

        .. MATH::

            (\iota_A B)^{i_1\ldots i_{q-p}} = A_{k_1\ldots k_p}
                            B^{k_1\ldots k_p i_1\ldots i_{q-p}}

        .. NOTE::

            ``A.interior_product(B)`` yields the same result as
            ``A.contract(0,..., p-1, B, 0,..., p-1)`` (cf.
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`),
            but ``interior_product`` is more efficient, the alternating
            character of `A` being not used to reduce the computation in
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`

        INPUT:

        - ``alt_tensor`` -- alternating contravariant tensor `B` (instance of
          :class:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor`);
          the degree of `B` must be at least equal to the degree of ``self``

        OUTPUT:

        - element of the base ring (case `p=q`) or
          :class:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor`
          (case `p<q`) representing the interior product `\iota_A B`, where `A`
          is ``self``

        .. SEEALSO::

            :meth:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor.interior_product`
            for the interior product of an alternating contravariant tensor by
            an alternating form

        EXAMPLES:

        Let us consider a rank-3 free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1)
            sage: e = M.basis('e')

        and various interior products on it, starting with a linear form
        (``p=1``) and a module element (``q=1``)::

            sage: a = M.linear_form(name='A')
            sage: a[:] = [-2, 4, 3]
            sage: b = M([3, 1, 5], basis=e, name='B')
            sage: c = a.interior_product(b); c
            13
            sage: c == a.contract(b)
            True

        Case  ``p=1`` and ``q=2``::

            sage: b = M.alternating_contravariant_tensor(2, name='B')
            sage: b[1,2], b[1,3], b[2,3] = 5, 2, 3
            sage: c = a.interior_product(b); c
            Element i_A B of the Rank-3 free module M over the Integer Ring
            sage: c.display()
            i_A B = -26 e_1 - 19 e_2 + 8 e_3
            sage: latex(c)
            \iota_{A} B
            sage: c == a.contract(b)
            True

        Case  ``p=1`` and ``q=3``::

            sage: b = M.alternating_contravariant_tensor(3, name='B')
            sage: b[1,2,3] = 5
            sage: c = a.interior_product(b); c
            Alternating contravariant tensor i_A B of degree 2 on the Rank-3 free module M over the Integer Ring
            sage: c.display()
            i_A B = 15 e_1/\e_2 - 20 e_1/\e_3 - 10 e_2/\e_3
            sage: c == a.contract(b)
            True

        Case  ``p=2`` and ``q=2``::

            sage: a = M.alternating_form(2, name='A')
            sage: a[1,2], a[1,3], a[2,3] = 2, -3, 1
            sage: b = M.alternating_contravariant_tensor(2, name='B')
            sage: b[1,2], b[1,3], b[2,3] = 5, 2, 3
            sage: c = a.interior_product(b); c
            14
            sage: c == a.contract(0, 1, b, 0, 1)   # contraction on all indices of a
            True

        Case  ``p=2`` and ``q=3``::

            sage: b = M.alternating_contravariant_tensor(3, name='B')
            sage: b[1,2,3] = 5
            sage: c = a.interior_product(b); c
            Element i_A B of the Rank-3 free module M over the Integer Ring
            sage: c.display()
            i_A B = 10 e_1 + 30 e_2 + 20 e_3
            sage: c == a.contract(0, 1, b, 0, 1)
            True

        Case  ``p=3`` and ``q=3``::

            sage: a = M.alternating_form(3, name='A')
            sage: a[1,2,3] = -2
            sage: c = a.interior_product(b); c
            -60
            sage: c  == a.contract(0, 1, 2, b, 0, 1, 2)
            True

        """
        from .format_utilities import is_atomic
        from .alternating_contr_tensor import AlternatingContrTensor
        if not isinstance(alt_tensor,  AlternatingContrTensor):
            raise TypeError("{} is not an alternating ".format(alt_tensor) +
                            "contravariant tensor")
        p_res = alt_tensor._tensor_rank - self._tensor_rank  # degree of result
        if self._tensor_rank == 1:
            # Case p = 1:
            res = self.contract(alt_tensor)  # contract() deals efficiently
                                             # with antisymmetry for p = 1
        else:
            # Case p > 1:
            if alt_tensor._fmodule != self._fmodule:
                raise ValueError("{} is not defined on ".format(alt_tensor) +
                                 "the same module as the {}".format(self))
            if alt_tensor._tensor_rank < self._tensor_rank:
                raise ValueError("the degree of the {} ".format(alt_tensor) +
                                 "is lower than that of the {}".format(self))
            # Interior product at the component level:
            basis = self.common_basis(alt_tensor)
            if basis is None:
                raise ValueError("no common basis for the interior product")
            comp = self._components[basis].interior_product(
                                                 alt_tensor._components[basis])
            if p_res == 0:
                res = comp  # result is a scalar
            else:
                res = self._fmodule.tensor_from_comp((p_res, 0), comp)
        # Name of the result
        res_name = None
        if self._name is not None and alt_tensor._name is not None:
            sname = self._name
            oname = alt_tensor._name
            if not is_atomic(sname):
                sname = '(' + sname + ')'
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            res_name = 'i_' + sname + ' ' + oname
        res_latex_name = None
        if self._latex_name is not None and alt_tensor._latex_name is not None:
            slname = self._latex_name
            olname = alt_tensor._latex_name
            if not is_atomic(olname):
                olname = r'\left(' + olname + r'\right)'
            res_latex_name = r'\iota_{' + slname + '} ' + olname
        if p_res == 0:
            if res_name:
                try:  # there is no guarantee that base ring elements have
                      # set_name
                    res.set_name(res_name, latex_name=res_latex_name)
                except (AttributeError, TypeError):
                    pass
        else:
            res.set_name(res_name, latex_name=res_latex_name)
        return res
    def interior_product(self, form):
        r"""
        Interior product with an alternating form.

        If ``self`` is an alternating contravariant tensor `A` of degree `p`
        and `B` is an alternating form of degree `q\geq p` on the same free
        module, the interior product of `A` by `B` is the alternating form
        `\iota_A B` of degree `q-p` defined by

        .. MATH::

            (\iota_A B)_{i_1\ldots i_{q-p}} = A^{k_1\ldots k_p}
                            B_{k_1\ldots k_p i_1\ldots i_{q-p}}

        .. NOTE::

            ``A.interior_product(B)`` yields the same result as
            ``A.contract(0,..., p-1, B, 0,..., p-1)`` (cf.
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`),
            but ``interior_product`` is more efficient, the alternating
            character of `A` being not used to reduce the computation in
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`

        INPUT:

        - ``form`` -- alternating form `B` (instance of
          :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`);
          the degree of `B` must be at least equal to the degree of ``self``

        OUTPUT:

        - element of the base ring (case `p=q`) or
          :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`
          (case `p<q`) representing the interior product `\iota_A B`, where `A`
          is ``self``

        .. SEEALSO::

            :meth:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm.interior_product`
            for the interior product of an alternating form by an alternating
            contravariant tensor

        EXAMPLES:

        Let us consider a rank-4 free module::

            sage: M = FiniteRankFreeModule(ZZ, 4, name='M', start_index=1)
            sage: e = M.basis('e')

        and various interior products on it, starting with a module element
        (``p=1``) and a linear form (``q=1``)::

            sage: a = M([-2,1,2,3], basis=e, name='A')
            sage: b = M.linear_form(name='B')
            sage: b[:] = [2, 0, -3, 4]
            sage: c = a.interior_product(b); c
            2
            sage: c == a.contract(b)
            True

        Case  ``p=1`` and ``q=3``::

            sage: b = M.alternating_form(3, name='B')
            sage: b[1,2,3], b[1,2,4], b[1,3,4], b[2,3,4] = 3, -1, 2, 5
            sage: c = a.interior_product(b); c
            Alternating form i_A B of degree 2 on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = 3 e^1/\e^2 + 3 e^1/\e^3 - 3 e^1/\e^4 + 9 e^2/\e^3 - 8 e^2/\e^4 + e^3/\e^4
            sage: latex(c)
            \iota_{A} B
            sage: c == a.contract(b)
            True

        Case  ``p=2`` and ``q=3``::

            sage: a = M.alternating_contravariant_tensor(2, name='A')
            sage: a[1,2], a[1,3], a[1,4] = 2, -5, 3
            sage: a[2,3], a[2,4], a[3,4] = -1, 4, 2
            sage: c = a.interior_product(b); c
            Linear form i_A B on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = -6 e^1 + 56 e^2 - 40 e^3 - 34 e^4
            sage: c == a.contract(0, 1, b, 0, 1)  # contraction on all indices of a
            True

        Case  ``p=2`` and ``q=4``::

            sage: b = M.alternating_form(4, name='B')
            sage: b[1,2,3,4] = 5
            sage: c = a.interior_product(b); c
            Alternating form i_A B of degree 2 on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = 20 e^1/\e^2 - 40 e^1/\e^3 - 10 e^1/\e^4 + 30 e^2/\e^3 + 50 e^2/\e^4 + 20 e^3/\e^4
            sage: c == a.contract(0, 1, b, 0, 1)
            True

        Case  ``p=2`` and ``q=2``::

            sage: b = M.alternating_form(2)
            sage: b[1,2], b[1,3], b[1,4] = 6, 0, -2
            sage: b[2,3], b[2,4], b[3,4] = 2, 3, 4
            sage: c = a.interior_product(b); c
            48
            sage: c == a.contract(0, 1, b, 0, 1)
            True

        Case  ``p=3`` and ``q=3``::

            sage: a = M.alternating_contravariant_tensor(3, name='A')
            sage: a[1,2,3], a[1,2,4], a[1,3,4], a[2,3,4] = -3, 2, 8, -5
            sage: b = M.alternating_form(3, name='B')
            sage: b[1,2,3], b[1,2,4], b[1,3,4], b[2,3,4] = 3, -1, 2, 5
            sage: c = a.interior_product(b); c
            -120
            sage: c == a.contract(0, 1, 2, b, 0, 1, 2)
            True

        Case  ``p=3`` and ``q=4``::

            sage: b = M.alternating_form(4, name='B')
            sage: b[1,2,3,4] = 5
            sage: c = a.interior_product(b); c
            Linear form i_A B on the Rank-4 free module M over the Integer Ring
            sage: c.display()
            i_A B = 150 e^1 + 240 e^2 - 60 e^3 - 90 e^4
            sage: c == a.contract(0, 1, 2, b, 0, 1, 2)
            True

        Case  ``p=4`` and ``q=4``::

            sage: a = M.alternating_contravariant_tensor(4, name='A')
            sage: a[1,2,3,4] = -2
            sage: c = a.interior_product(b); c
            -240
            sage: c == a.contract(0, 1, 2, 3, b, 0, 1, 2, 3)
            True

        """
        from .format_utilities import is_atomic
        from .free_module_alt_form import FreeModuleAltForm
        if not isinstance(form, FreeModuleAltForm):
            raise TypeError("{} is not an alternating form".format(form))
        p_res = form._tensor_rank - self._tensor_rank  # degree of the result
        if self._tensor_rank == 1:
            # Case p = 1:
            res = self.contract(form)  # contract() deals efficiently with
                                       # the antisymmetry for p = 1
        else:
            # Case p > 1:
            if form._fmodule != self._fmodule:
                raise ValueError("{} is not defined on the same ".format(form) +
                                 "module as the {}".format(self))
            if form._tensor_rank < self._tensor_rank:
                raise ValueError("the degree of the {} is lower ".format(form) +
                                 "than that of the {}".format(self))
            # Interior product at the component level:
            basis = self.common_basis(form)
            if basis is None:
                raise ValueError("no common basis for the interior product")
            comp = self._components[basis].interior_product(
                                                       form._components[basis])
            if p_res == 0:
                res = comp  # result is a scalar
            else:
                res = self._fmodule.tensor_from_comp((0, p_res), comp)
        # Name of the result
        res_name = None
        if self._name is not None and form._name is not None:
            sname = self._name
            oname = form._name
            if not is_atomic(sname):
                sname = '(' + sname + ')'
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            res_name = 'i_' + sname + ' ' + oname
        res_latex_name = None
        if self._latex_name is not None and form._latex_name is not None:
            slname = self._latex_name
            olname = form._latex_name
            if not is_atomic(olname):
                olname = r'\left(' + olname + r'\right)'
            res_latex_name = r'\iota_{' + slname + '} ' + olname
        if p_res == 0:
            if res_name:
                try:  # there is no guarantee that base ring elements have
                      # set_name
                    res.set_name(res_name, latex_name=res_latex_name)
                except (AttributeError, TypeError):
                    pass
        else:
            res.set_name(res_name, latex_name=res_latex_name)
        return res
    def display(self, basis=None, format_spec=None):
        r"""
        Display the alternating form ``self`` in terms of its expansion w.r.t.
        a given module basis.

        The expansion is actually performed onto exterior products of elements
        of the cobasis (dual basis) associated with ``basis`` (see examples
        below). The output is either text-formatted (console mode) or
        LaTeX-formatted (notebook mode).

        INPUT:

        - ``basis`` -- (default: ``None``) basis of the free module with
          respect to which the alternating form is expanded; if none is
          provided, the module's default basis is assumed
        - ``format_spec`` -- (default: ``None``) format specification passed
          to ``self._fmodule._output_formatter`` to format the output

        EXAMPLES:

        Display of an alternating form of degree 1 (linear form) on a rank-3
        free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: e.dual_basis()
            Dual basis (e^0,e^1,e^2) on the Rank-3 free module M over the Integer Ring
            sage: a = M.linear_form('a', latex_name=r'\alpha')
            sage: a[:] = [1,-3,4]
            sage: a.display(e)
            a = e^0 - 3 e^1 + 4 e^2
            sage: a.display()  # a shortcut since e is M's default basis
            a = e^0 - 3 e^1 + 4 e^2
            sage: latex(a.display())  # display in the notebook
            \alpha = e^{0} -3 e^{1} + 4 e^{2}

        A shortcut is ``disp()``::

            sage: a.disp()
            a = e^0 - 3 e^1 + 4 e^2

        Display of an alternating form of degree 2 on a rank-3 free module::

            sage: b = M.alternating_form(2, 'b', latex_name=r'\beta')
            sage: b[0,1], b[0,2], b[1,2] = 3, 2, -1
            sage: b.display()
            b = 3 e^0/\e^1 + 2 e^0/\e^2 - e^1/\e^2
            sage: latex(b.display())  # display in the notebook
            \beta = 3 e^{0}\wedge e^{1} + 2 e^{0}\wedge e^{2} -e^{1}\wedge e^{2}

        Display of an alternating form of degree 3 on a rank-3 free module::

            sage: c = M.alternating_form(3, 'c')
            sage: c[0,1,2] = 4
            sage: c.display()
            c = 4 e^0/\e^1/\e^2
            sage: latex(c.display())
            c = 4 e^{0}\wedge e^{1}\wedge e^{2}

        Display of a vanishing alternating form::

            sage: c[0,1,2] = 0  # the only independent component set to zero
            sage: c.is_zero()
            True
            sage: c.display()
            c = 0
            sage: latex(c.display())
            c = 0
            sage: c[0,1,2] = 4  # value restored for what follows

        Display in a basis which is not the default one::

            sage: aut = M.automorphism(matrix=[[0,1,0], [0,0,-1], [1,0,0]],
            ....:                      basis=e)
            sage: f = e.new_basis(aut, 'f')
            sage: a.display(f)
            a = 4 f^0 + f^1 + 3 f^2
            sage: a.disp(f)     # shortcut notation
            a = 4 f^0 + f^1 + 3 f^2
            sage: b.display(f)
            b = -2 f^0/\f^1 - f^0/\f^2 - 3 f^1/\f^2
            sage: c.display(f)
            c = -4 f^0/\f^1/\f^2

        The output format can be set via the argument ``output_formatter``
        passed at the module construction::

            sage: N = FiniteRankFreeModule(QQ, 3, name='N', start_index=1,
            ....:                   output_formatter=Rational.numerical_approx)
            sage: e = N.basis('e')
            sage: b = N.alternating_form(2, 'b')
            sage: b[1,2], b[1,3], b[2,3] = 1/3, 5/2, 4
            sage: b.display()  # default format (53 bits of precision)
            b = 0.333333333333333 e^1/\e^2 + 2.50000000000000 e^1/\e^3
             + 4.00000000000000 e^2/\e^3

        The output format is then controlled by the argument ``format_spec`` of
        the method :meth:`display`::

            sage: b.display(format_spec=10)  # 10 bits of precision
            b = 0.33 e^1/\e^2 + 2.5 e^1/\e^3 + 4.0 e^2/\e^3

        Check that the bug reported in :trac:`22520` is fixed::

            sage: M = FiniteRankFreeModule(SR, 2, name='M')
            sage: e = M.basis('e')
            sage: a = M.alternating_form(2)
            sage: a[0,1] = SR.var('t', domain='real')
            sage: a.display()
            t e^0/\e^1

        """
        from sage.misc.latex import latex
        from sage.tensor.modules.format_utilities import is_atomic, \
                                                         FormattedExpansion
        if basis is None:
            basis = self._fmodule._def_basis
        cobasis = basis.dual_basis()
        comp = self.comp(basis)
        terms_txt = []
        terms_latex = []
        for ind in comp.non_redundant_index_generator():
            ind_arg = ind + (format_spec,)
            coef = comp[ind_arg]
            # Check whether the coefficient is zero, preferably via
            # the fast method is_trivial_zero():
            if hasattr(coef, 'is_trivial_zero'):
                zero_coef = coef.is_trivial_zero()
            else:
                zero_coef = coef == 0
            if not zero_coef:
                bases_txt = []
                bases_latex = []
                for k in range(self._tensor_rank):
                    bases_txt.append(cobasis[ind[k]]._name)
                    bases_latex.append(latex(cobasis[ind[k]]))
                basis_term_txt = "/\\".join(bases_txt)
                basis_term_latex = r"\wedge ".join(bases_latex)
                coef_txt = repr(coef)
                if coef_txt == "1":
                    terms_txt.append(basis_term_txt)
                    terms_latex.append(basis_term_latex)
                elif coef_txt == "-1":
                    terms_txt.append("-" + basis_term_txt)
                    terms_latex.append("-" + basis_term_latex)
                else:
                    coef_latex = latex(coef)
                    if is_atomic(coef_txt):
                        terms_txt.append(coef_txt + " " + basis_term_txt)
                    else:
                        terms_txt.append("(" + coef_txt + ") " +
                                         basis_term_txt)
                    if is_atomic(coef_latex):
                        terms_latex.append(coef_latex + basis_term_latex)
                    else:
                        terms_latex.append(r"\left(" + coef_latex + \
                                           r"\right)" + basis_term_latex)
        if not terms_txt:
            expansion_txt = "0"
        else:
            expansion_txt = terms_txt[0]
            for term in terms_txt[1:]:
                if term[0] == "-":
                    expansion_txt += " - " + term[1:]
                else:
                    expansion_txt += " + " + term
        if not terms_latex:
            expansion_latex = "0"
        else:
            expansion_latex = terms_latex[0]
            for term in terms_latex[1:]:
                if term[0] == "-":
                    expansion_latex += term
                else:
                    expansion_latex += "+" + term
        if self._name is None:
            resu_txt = expansion_txt
        else:
            resu_txt = self._name + " = " + expansion_txt
        if self._latex_name is None:
            resu_latex = expansion_latex
        else:
            resu_latex = latex(self) + " = " + expansion_latex
        return FormattedExpansion(resu_txt, resu_latex)
Esempio n. 12
0
    def _sub_(self, other):
        r"""
        Subtraction of two mixed forms.

        INPUT:

        - ``other`` -- a mixed form, in the same algebra as ``self``

        OUTPUT:

        - the mixed form resulting from the subtraction of ``self``
          and ``other``

        TESTS::

        sage: M = Manifold(2, 'M')
        sage: U = M.open_subset('U') ; V = M.open_subset('V')
        sage: M.declare_union(U,V)   # M is the union of U and V
        sage: c_xy.<x,y> = U.chart() ; c_uv.<u,v> = V.chart()
        sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y),
        ....:                    intersection_name='W', restrictions1= x>0,
        ....:                    restrictions2= u+v>0)
        sage: uv_to_xy = xy_to_uv.inverse()
        sage: e_xy = c_xy.frame(); e_uv = c_uv.frame()
        sage: f = M.scalar_field(x, name='f')
        sage: a = M.diff_form(1, name='a')
        sage: a[e_xy,0] = x
        sage: A = M.mixed_form(name='A', comp=[f, a, 0])
        sage: A.add_comp_by_continuation(e_uv, U.intersection(V), c_uv)
        sage: g = M.scalar_field(u, name='g', chart=c_uv)
        sage: b = M.diff_form(1, name='b')
        sage: b[e_uv,1] = v
        sage: B = M.mixed_form(name='B', comp=[g, b, 0])
        sage: B.add_comp_by_continuation(e_xy, V.intersection(U), c_xy)
        sage: C = A._sub_(B); C
        Mixed differential form A-B on the 2-dimensional differentiable
         manifold M
        sage: A.display(e_uv)
        A = [1/2*u + 1/2*v] + [(1/4*u + 1/4*v) du + (1/4*u + 1/4*v) dv] + [0]
        sage: B.display(e_xy)
        B = [x + y] + [(x - y) dx + (-x + y) dy] + [0]
        sage: C.display(e_xy)
        A-B = [-y] + [y dx + (x - y) dy] + [0]
        sage: C.display(e_uv)
        A-B = [-1/2*u + 1/2*v] + [(1/4*u + 1/4*v) du + (1/4*u - 3/4*v) dv] + [0]
        sage: C == A - B  # indirect doctest
        True
        sage: Z = A.parent().zero(); Z
        Mixed differential form zero on the 2-dimensional differentiable
         manifold M
        sage: A._sub_(Z) == A
        True
        sage: Z._sub_(A) == -A
        True

        """
        resu_comp = [self[j] - other[j] for j in range(self._max_deg + 1)]
        resu = type(self)(self.parent(), comp=resu_comp)
        # Compose name:
        from sage.tensor.modules.format_utilities import is_atomic

        if self._name is not None and other._name is not None:
            sname = self._name
            oname = other._name
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            resu._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(olname):
                olname = '(' + olname + ')'
            resu._latex_name = slname + '-' + olname
        return resu
Esempio n. 13
0
        def _display_form(rst, basis, chart):
            r"""
            Display coordinate expression of a single form ``rst`` (without
            equality sign).

            This helper method is invoked by :meth:`display`.

            TESTS::

                sage: M = Manifold(2, 'M')
                sage: c_xy.<x,y> = M.chart()
                sage: omega = M.diff_form(1, name='omega')
                sage: omega[c_xy.frame(),0] = x^2
                sage: F = M.mixed_form(comp=[0,omega,0])
                sage: F.disp(c_xy.frame())
                [0] + [x^2 dx] + [0]

            """
            from sage.tensor.modules.format_utilities import is_atomic

            cobasis = basis.dual_basis()
            comp = rst.comp(basis)
            terms_txt = []
            terms_latex = []
            for ind in comp.non_redundant_index_generator():
                ind_arg = ind + (chart, )
                coef = comp[ind_arg]
                # Check whether the coefficient is zero, preferably via
                # the fast method is_trivial_zero():
                if hasattr(coef, 'is_trivial_zero'):
                    zero_coef = coef.is_trivial_zero()
                else:
                    zero_coef = coef == 0
                if not zero_coef:
                    bases_txt = []
                    bases_latex = []
                    for k in range(rst._tensor_rank):
                        bases_txt.append(cobasis[ind[k]]._name)
                        bases_latex.append(latex(cobasis[ind[k]]))
                    basis_term_txt = "/\\".join(bases_txt)
                    basis_term_latex = r"\wedge ".join(bases_latex)
                    coef_txt = repr(coef)
                    if coef_txt == "1":
                        terms_txt.append(basis_term_txt)
                        terms_latex.append(basis_term_latex)
                    elif coef_txt == "-1":
                        terms_txt.append("-" + basis_term_txt)
                        terms_latex.append("-" + basis_term_latex)
                    else:
                        coef_latex = latex(coef)
                        if is_atomic(coef_txt):
                            terms_txt.append(coef_txt + " " + basis_term_txt)
                        else:
                            terms_txt.append("(" + coef_txt + ") " +
                                             basis_term_txt)
                        if is_atomic(coef_latex):
                            terms_latex.append(coef_latex + basis_term_latex)
                        else:
                            terms_latex.append(r"\left(" + coef_latex +
                                               r"\right)" + basis_term_latex)
            if not terms_txt:
                resu_txt = "0"
            else:
                resu_txt = terms_txt[0]
                for term in terms_txt[1:]:
                    if term[0] == "-":
                        resu_txt += " - " + term[1:]
                    else:
                        resu_txt += " + " + term
            if not terms_latex:
                resu_latex = r"0"
            else:
                resu_latex = terms_latex[0]
                for term in terms_latex[1:]:
                    if term[0] == "-":
                        resu_latex += term
                    else:
                        resu_latex += "+" + term
            return FormattedExpansion(resu_txt, resu_latex)
Esempio n. 14
0
    def interior_product(self, alt_tensor):
        r"""
        Interior product with an alternating contravariant tensor.

        If ``self`` is an alternating form `A` of degree `p` and `B` is an
        alternating contravariant tensor of degree `q\geq p` on the same free
        module, the interior product of `A` by `B` is the alternating
        contravariant tensor `\iota_A B` of degree `q-p` defined by

        .. MATH::

            (\iota_A B)^{i_1\ldots i_{q-p}} = A_{k_1\ldots k_p}
                            B^{k_1\ldots k_p i_1\ldots i_{q-p}}

        .. NOTE::

            ``A.interior_product(B)`` yields the same result as
            ``A.contract(0,..., p-1, B, 0,..., p-1)`` (cf.
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`),
            but ``interior_product`` is more efficient, the alternating
            character of `A` being not used to reduce the computation in
            :meth:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor.contract`

        INPUT:

        - ``alt_tensor`` -- alternating contravariant tensor `B` (instance of
          :class:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor`);
          the degree of `B` must be at least equal to the degree of ``self``

        OUTPUT:

        - element of the base ring (case `p=q`) or
          :class:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor`
          (case `p<q`) representing the interior product `\iota_A B`, where `A`
          is ``self``

        .. SEEALSO::

            :meth:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor.interior_product`
            for the interior product of an alternating contravariant tensor by
            an alternating form

        EXAMPLES:

        Let us consider a rank-3 free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1)
            sage: e = M.basis('e')

        and various interior products on it, starting with a linear form
        (``p=1``) and a module element (``q=1``)::

            sage: a = M.linear_form(name='A')
            sage: a[:] = [-2, 4, 3]
            sage: b = M([3, 1, 5], basis=e, name='B')
            sage: c = a.interior_product(b); c
            13
            sage: c == a.contract(b)
            True

        Case  ``p=1`` and ``q=2``::

            sage: b = M.alternating_contravariant_tensor(2, name='B')
            sage: b[1,2], b[1,3], b[2,3] = 5, 2, 3
            sage: c = a.interior_product(b); c
            Element i_A B of the Rank-3 free module M over the Integer Ring
            sage: c.display()
            i_A B = -26 e_1 - 19 e_2 + 8 e_3
            sage: latex(c)
            \iota_{A} B
            sage: c == a.contract(b)
            True

        Case  ``p=1`` and ``q=3``::

            sage: b = M.alternating_contravariant_tensor(3, name='B')
            sage: b[1,2,3] = 5
            sage: c = a.interior_product(b); c
            Alternating contravariant tensor i_A B of degree 2 on the Rank-3 free module M over the Integer Ring
            sage: c.display()
            i_A B = 15 e_1/\e_2 - 20 e_1/\e_3 - 10 e_2/\e_3
            sage: c == a.contract(b)
            True

        Case  ``p=2`` and ``q=2``::

            sage: a = M.alternating_form(2, name='A')
            sage: a[1,2], a[1,3], a[2,3] = 2, -3, 1
            sage: b = M.alternating_contravariant_tensor(2, name='B')
            sage: b[1,2], b[1,3], b[2,3] = 5, 2, 3
            sage: c = a.interior_product(b); c
            14
            sage: c == a.contract(0, 1, b, 0, 1)   # contraction on all indices of a
            True

        Case  ``p=2`` and ``q=3``::

            sage: b = M.alternating_contravariant_tensor(3, name='B')
            sage: b[1,2,3] = 5
            sage: c = a.interior_product(b); c
            Element i_A B of the Rank-3 free module M over the Integer Ring
            sage: c.display()
            i_A B = 10 e_1 + 30 e_2 + 20 e_3
            sage: c == a.contract(0, 1, b, 0, 1)
            True

        Case  ``p=3`` and ``q=3``::

            sage: a = M.alternating_form(3, name='A')
            sage: a[1,2,3] = -2
            sage: c = a.interior_product(b); c
            -60
            sage: c  == a.contract(0, 1, 2, b, 0, 1, 2)
            True

        """
        from .format_utilities import is_atomic
        from .alternating_contr_tensor import AlternatingContrTensor
        if not isinstance(alt_tensor,  AlternatingContrTensor):
            raise TypeError("{} is not an alternating ".format(alt_tensor) +
                            "contravariant tensor")
        p_res = alt_tensor._tensor_rank - self._tensor_rank  # degree of result
        if self._tensor_rank == 1:
            # Case p = 1:
            res = self.contract(alt_tensor)  # contract() deals efficiently
                                             # with antisymmetry for p = 1
        else:
            # Case p > 1:
            if alt_tensor._fmodule != self._fmodule:
                raise ValueError("{} is not defined on ".format(alt_tensor) +
                                 "the same module as the {}".format(self))
            if alt_tensor._tensor_rank < self._tensor_rank:
                raise ValueError("the degree of the {} ".format(alt_tensor) +
                                 "is lower than that of the {}".format(self))
            # Interior product at the component level:
            basis = self.common_basis(alt_tensor)
            if basis is None:
                raise ValueError("no common basis for the interior product")
            comp = self._components[basis].interior_product(
                                                 alt_tensor._components[basis])
            if p_res == 0:
                res = comp  # result is a scalar
            else:
                res = self._fmodule.tensor_from_comp((p_res, 0), comp)
        # Name of the result
        res_name = None
        if self._name is not None and alt_tensor._name is not None:
            sname = self._name
            oname = alt_tensor._name
            if not is_atomic(sname):
                sname = '(' + sname + ')'
            if not is_atomic(oname):
                oname = '(' + oname + ')'
            res_name = 'i_' + sname + ' ' + oname
        res_latex_name = None
        if self._latex_name is not None and alt_tensor._latex_name is not None:
            slname = self._latex_name
            olname = alt_tensor._latex_name
            if not is_atomic(olname):
                olname = r'\left(' + olname + r'\right)'
            res_latex_name = r'\iota_{' + slname + '} ' + olname
        if p_res == 0:
            if res_name:
                try:  # there is no guarantee that base ring elements have
                      # set_name
                    res.set_name(res_name, latex_name=res_latex_name)
                except (AttributeError, TypeError):
                    pass
        else:
            res.set_name(res_name, latex_name=res_latex_name)
        return res
Esempio n. 15
0
    def wedge(self, other):
        r"""
        Exterior product with another differential form.

        INPUT:

        - ``other``: another differential form

        OUTPUT:

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

        """
        from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm
        from sage.tensor.modules.format_utilities import is_atomic
        if self._domain.is_subset(other._domain):
            if not self._ambient_domain.is_subset(other._ambient_domain):
                raise TypeError("Incompatible ambient domains for exterior " +
                                "product.")
        elif other._domain.is_subset(self._domain):
            if not other._ambient_domain.is_subset(self._ambient_domain):
                raise TypeError("Incompatible ambient domains for exterior " +
                                "product.")
        dom_resu = self._domain.intersection(other._domain)
        ambient_dom_resu = self._ambient_domain.intersection(
            other._ambient_domain)
        self_r = self.restrict(dom_resu)
        other_r = other.restrict(dom_resu)
        if ambient_dom_resu.is_manifestly_parallelizable():
            # call of the FreeModuleAltForm version:
            return FreeModuleAltForm.wedge(self_r, other_r)
        # otherwise, the result is created here:
        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 + ')'
            resu_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 + ')'
            resu_latex_name = slname + r'\wedge ' + olname
        dest_map = self._vmodule._dest_map
        dest_map_resu = dest_map.restrict(dom_resu,
                                          subcodomain=ambient_dom_resu)
        vmodule = dom_resu.vector_field_module(dest_map=dest_map_resu)
        resu_degree = self._tensor_rank + other._tensor_rank
        resu = vmodule.alternating_form(resu_degree,
                                        name=resu_name,
                                        latex_name=resu_latex_name)
        for dom in self_r._restrictions:
            if dom in other_r._restrictions:
                resu._restrictions[dom] = self_r._restrictions[dom].wedge(
                    other_r._restrictions[dom])
        return resu
Esempio n. 16
0
    def _display_expansion(self, basis=None, format_spec=None):
        r"""
        Return the pure expansion of ``self`` w.r.t. a given module basis.

        The expansion is actually performed onto exterior products of elements
        of the cobasis (dual basis) associated with ``basis`` (see examples
        below). The output is either text-formatted (console mode) or
        LaTeX-formatted (notebook mode).

        INPUT:

        - ``basis`` -- (default: ``None``) basis of the free module with
          respect to which the alternating form is expanded; if none is
          provided, the module's default basis is assumed
        - ``format_spec`` -- (default: ``None``) format specification passed
          to ``self._fmodule._output_formatter`` to format the output

        TESTS:

        Expansion display of an alternating form of degree 1 (linear form) on a
        rank-3 free module::

            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
            sage: e = M.basis('e')
            sage: e.dual_basis()
            Dual basis (e^0,e^1,e^2) on the Rank-3 free module M over the Integer Ring
            sage: a = M.linear_form('a', latex_name=r'\alpha')
            sage: a[:] = [1,-3,4]
            sage: a._display_expansion(e)
            e^0 - 3 e^1 + 4 e^2
            sage: a._display_expansion() # a shortcut since e is M's default basis
            e^0 - 3 e^1 + 4 e^2
            sage: latex(a._display_expansion())  # display in the notebook
            e^{0} -3 e^{1} + 4 e^{2}

        """
        from sage.misc.latex import latex
        from sage.tensor.modules.format_utilities import (is_atomic,
                                                          FormattedExpansion)
        basis, format_spec = self._preparse_display(basis=basis,
                                                    format_spec=format_spec)
        cobasis = basis.dual_basis()
        comp = self.comp(basis)
        terms_txt = []
        terms_latex = []
        for ind in comp.non_redundant_index_generator():
            ind_arg = ind + (format_spec, )
            coef = comp[ind_arg]
            # Check whether the coefficient is zero, preferably via
            # the fast method is_trivial_zero():
            if hasattr(coef, 'is_trivial_zero'):
                zero_coef = coef.is_trivial_zero()
            else:
                zero_coef = coef == 0
            if not zero_coef:
                bases_txt = []
                bases_latex = []
                for k in range(self._tensor_rank):
                    bases_txt.append(cobasis[ind[k]]._name)
                    bases_latex.append(latex(cobasis[ind[k]]))
                basis_term_txt = "/\\".join(bases_txt)
                basis_term_latex = r"\wedge ".join(bases_latex)
                coef_txt = repr(coef)
                if coef_txt == "1":
                    terms_txt.append(basis_term_txt)
                    terms_latex.append(basis_term_latex)
                elif coef_txt == "-1":
                    terms_txt.append("-" + basis_term_txt)
                    terms_latex.append("-" + basis_term_latex)
                else:
                    coef_latex = latex(coef)
                    if is_atomic(coef_txt):
                        terms_txt.append(coef_txt + " " + basis_term_txt)
                    else:
                        terms_txt.append("(" + coef_txt + ") " +
                                         basis_term_txt)
                    if is_atomic(coef_latex):
                        terms_latex.append(coef_latex + basis_term_latex)
                    else:
                        terms_latex.append(r"\left(" + coef_latex + \
                                           r"\right)" + basis_term_latex)
        if not terms_txt:
            expansion_txt = "0"
        else:
            expansion_txt = terms_txt[0]
            for term in terms_txt[1:]:
                if term[0] == "-":
                    expansion_txt += " - " + term[1:]
                else:
                    expansion_txt += " + " + term
        if not terms_latex:
            expansion_latex = "0"
        else:
            expansion_latex = terms_latex[0]
            for term in terms_latex[1:]:
                if term[0] == "-":
                    expansion_latex += term
                else:
                    expansion_latex += "+" + term
        return FormattedExpansion(expansion_txt, expansion_latex)