def invariants_of_degree(self, deg, chi=None, R=None):
        r"""
        Return the (relative) invariants of given degree for this group.

        For this group, compute the invariants of degree ``deg``
        with respect to the group character ``chi``. The method
        is to project each possible monomial of degree ``deg`` via
        the Reynolds operator. Note that if the polynomial ring ``R``
        is specified it's base ring may be extended if the resulting
        invariant is defined over a bigger field.

        INPUT:

        - ``degree`` -- a positive integer

        - ``chi`` -- (default: trivial character) a linear group character of this group

        - ``R`` -- (optional) a polynomial ring

        OUTPUT: list of polynomials

        EXAMPLES::

            sage: Gr = MatrixGroup(SymmetricGroup(2))
            sage: Gr.invariants_of_degree(3)
            [x0^3 + x1^3, x0^2*x1 + x0*x1^2]
            sage: R.<x,y> = QQ[]
            sage: Gr.invariants_of_degree(4, R=R)
            [x^3*y + x*y^3, x^2*y^2, x^4 + y^4]

        ::

            sage: R.<x,y,z> = QQ[]
            sage: Gr = MatrixGroup(DihedralGroup(3))
            sage: ct = Gr.character_table()
            sage: chi = Gr.character(ct[0])
            sage: [f(*(g.matrix()*vector(R.gens()))) == chi(g)*f \
                  for f in Gr.invariants_of_degree(3, R=R, chi=chi) for g in Gr]
            [True, True, True, True, True, True]

        ::

            sage: i = GF(7)(3)
            sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]])
            sage: G.invariants_of_degree(25)
            []

        ::

            sage: G = MatrixGroup(SymmetricGroup(5))
            sage: R = QQ['x,y']
            sage: G.invariants_of_degree(3, R=R)
            Traceback (most recent call last):
            ...
            TypeError: number of variables in polynomial ring must match size of matrices

        ::

            sage: K.<i> = CyclotomicField(4)
            sage: G =  MatrixGroup(CyclicPermutationGroup(3))
            sage: chi = G.character(G.character_table()[1])
            sage: R.<x,y,z> = K[]
            sage: G.invariants_of_degree(2, R=R, chi=chi)
            [x*y + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*x*z +
             (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y*z,
             x^2 + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y^2 +
             (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*z^2]

        ::

            sage: S3 = MatrixGroup(SymmetricGroup(3))
            sage: chi = S3.character(S3.character_table()[0])
            sage: S3.invariants_of_degree(5, chi=chi)
            [x0^4*x1 - x0*x1^4 - x0^4*x2 + x1^4*x2 + x0*x2^4 - x1*x2^4,
             x0^3*x1^2 - x0^2*x1^3 - x0^3*x2^2 + x1^3*x2^2 + x0^2*x2^3 - x1^2*x2^3]
        """
        D = self.degree()
        deg = int(deg)
        if deg <= 0:
            raise ValueError("degree must be a positive integer")
        if R is None:
            R = PolynomialRing(self.base_ring(), 'x', D)
        elif R.ngens() != D:
            raise TypeError("number of variables in polynomial ring must match size of matrices")

        ms = self.molien_series(prec=deg+1,chi=chi)
        if ms[deg].is_zero():
            return []
        inv = set()
        for e in IntegerVectors(deg, D):
            F = self.reynolds_operator(R.monomial(*e), chi=chi)
            if not F.is_zero():
                F = F/F.lc()
                inv.add(F)
                if len(inv) == ms[deg]:
                    break
        return list(inv)
    def Chow_form(self):
        r"""
        Returns the Chow form associated to this subscheme.

        For a `k`-dimensional subvariety of `\mathbb{P}^N` of degree `D`.
        The `(N-k-1)`-dimensional projective linear subspaces of `\mathbb{P}^N`
        meeting `X` form a hypersurface in the Grassmannian `G(N-k-1,N)`.
        The homogeneous form of degree `D` defining this hypersurface in Plucker
        coordinates is called the Chow form of `X`.

        The base ring needs to be a number field, finite field, or `\QQbar`.

        ALGORITHM:

        For a `k`-dimension subscheme `X` consider the `k+1` linear forms
        `l_i = u_{i0}x_0 + \cdots + u_{in}x_n`. Let `J` be the ideal in the
        polynomial ring `K[x_i,u_{ij}]` defined by the equations of `X` and the `l_i`.
        Let `J'` be the saturation of `J` with respect to the irrelevant ideal of
        the ambient projective space of `X`. The elimination ideal `I = J' \cap K[u_{ij}]`
        is a principal ideal, let `R` be its generator. The Chow form is obtained by
        writing `R` as a polynomial in Plucker coordinates (i.e. bracket polynomials).
        [DalbecSturmfels]_.

        OUTPUT: a homogeneous polynomial.

        REFERENCES:

        .. [DalbecSturmfels] J. Dalbec and B. Sturmfels. Invariant methods in discrete and computational geometry,
           chapter Introduction to Chow forms, pages 37-58. Springer Netherlands, 1994.

        EXAMPLES::

            sage: P.<x0,x1,x2,x3> = ProjectiveSpace(GF(17), 3)
            sage: X = P.subscheme([x3+x1,x2-x0,x2-x3])
            sage: X.Chow_form()
            t0 - t1 + t2 + t3

        ::

            sage: P.<x0,x1,x2,x3> = ProjectiveSpace(QQ,3)
            sage: X = P.subscheme([x3^2 -101*x1^2 - 3*x2*x0])
            sage: X.Chow_form()
            t0^2 - 101*t2^2 - 3*t1*t3

        ::

            sage: P.<x0,x1,x2,x3>=ProjectiveSpace(QQ,3)
            sage: X = P.subscheme([x0*x2-x1^2, x0*x3-x1*x2, x1*x3-x2^2])
            sage: Ch = X.Chow_form(); Ch
            t2^3 + 2*t2^2*t3 + t2*t3^2 - 3*t1*t2*t4 - t1*t3*t4 + t0*t4^2 + t1^2*t5
            sage: Y = P.subscheme_from_Chow_form(Ch, 1); Y
            Closed subscheme of Projective Space of dimension 3 over Rational Field
            defined by:
              x2^2*x3 - x1*x3^2,
              -x2^3 + x0*x3^2,
              -x2^2*x3 + x1*x3^2,
              x1*x2*x3 - x0*x3^2,
              3*x1*x2^2 - 3*x0*x2*x3,
              -2*x1^2*x3 + 2*x0*x2*x3,
              -3*x1^2*x2 + 3*x0*x1*x3,
              x1^3 - x0^2*x3,
              x2^3 - x1*x2*x3,
              -3*x1*x2^2 + 2*x1^2*x3 + x0*x2*x3,
              2*x0*x2^2 - 2*x0*x1*x3,
              3*x1^2*x2 - 2*x0*x2^2 - x0*x1*x3,
              -x0*x1*x2 + x0^2*x3,
              -x0*x1^2 + x0^2*x2,
              -x1^3 + x0*x1*x2,
              x0*x1^2 - x0^2*x2
            sage: I = Y.defining_ideal()
            sage: I.saturation(I.ring().ideal(list(I.ring().gens())))[0]
            Ideal (x2^2 - x1*x3, x1*x2 - x0*x3, x1^2 - x0*x2) of Multivariate
            Polynomial Ring in x0, x1, x2, x3 over Rational Field
        """
        I = self.defining_ideal()
        P = self.ambient_space()
        R = P.coordinate_ring()
        N = P.dimension() + 1
        d = self.dimension()
        # create the ring for the generic linear hyperplanes
        # u0x0 + u1x1 + ...
        SS = PolynomialRing(R.base_ring(), 'u', N * (d + 1), order='lex')
        vars = SS.variable_names() + R.variable_names()
        S = PolynomialRing(R.base_ring(), vars, order='lex')
        n = S.ngens()
        newcoords = [S.gen(n - N + t) for t in range(N)]
        # map the generators of the subscheme into the ring with the hyperplane variables
        phi = R.hom(newcoords, S)
        phi(self.defining_polynomials()[0])
        # create the dim(X)+1 linear hyperplanes
        l = []
        for i in range(d + 1):
            t = 0
            for j in range(N):
                t += S.gen(N * i + j) * newcoords[j]
            l.append(t)
        # intersect the hyperplanes with X
        J = phi(I) + S.ideal(l)
        # saturate the ideal with respect to the irrelevant ideal
        J2 = J.saturation(S.ideal([phi(u) for u in R.gens()]))[0]
        # eliminate the original variables to be left with the hyperplane coefficients 'u'
        E = J2.elimination_ideal(newcoords)
        # create the plucker coordinates
        D = binomial(N, N - d - 1)  #number of plucker coordinates
        tvars = [str('t') + str(i) for i in range(D)]  #plucker coordinates
        T = PolynomialRing(R.base_ring(),
                           tvars + list(S.variable_names()),
                           order='lex')
        L = []
        coeffs = [
            T.gen(i) for i in range(0 + len(tvars),
                                    N * (d + 1) + len(tvars))
        ]
        M = matrix(T, d + 1, N, coeffs)
        i = 0
        for c in M.minors(d + 1):
            L.append(T.gen(i) - c)
            i += 1
        # create the ideal that we can use for eliminating to get a polynomial
        # in the plucker coordinates (brackets)
        br = T.ideal(L)
        # create a mapping into a polynomial ring over the plucker coordinates
        # and the hyperplane coefficients
        psi = S.hom(coeffs + [0 for _ in range(N)], T)
        E2 = T.ideal([psi(u) for u in E.gens()] + br)
        # eliminate the hyperplane coefficients
        CH = E2.elimination_ideal(coeffs)
        # CH should be a principal ideal, but because of the relations among
        # the plucker coordinates, the elimination will probably have several generators

        # get the relations among the plucker coordinates
        rel = br.elimination_ideal(coeffs)
        # reduce CH with respect to the relations
        reduced = []
        for f in CH.gens():
            reduced.append(f.reduce(rel))
        # find the principal generator

        # polynomial ring in just the plucker coordinates
        T2 = PolynomialRing(R.base_ring(), tvars)
        alp = T.hom(tvars + (N * (d + 1) + N) * [0], T2)
        # get the degrees of the reduced generators of CH
        degs = [u.degree() for u in reduced]
        mind = max(degs)
        # need the smallest degree form that did not reduce to 0
        for d in degs:
            if d < mind and d > 0:
                mind = d
        ind = degs.index(mind)
        CF = reduced[ind]  #this should be the Chow form of X
        # check that it is correct (i.e., it is a principal generator for CH + the relations)
        rel2 = rel + [CF]
        assert all(f in rel2
                   for f in CH.gens()), "did not find a principal generator"
        return alp(CF)
示例#3
0
    def groebner_basis(self,
                       tailreduce=False,
                       reduced=True,
                       algorithm=None,
                       report=None,
                       use_full_group=False):
        """
        Return a symmetric Groebner basis (type :class:`~sage.structure.sequence.Sequence`) of ``self``.

        INPUT:

        - ``tailreduce`` -- (bool, default ``False``) If True, use tail reduction
          in intermediate computations
        - ``reduced`` -- (bool, default ``True``) If ``True``, return the reduced normalised
          symmetric Groebner basis.
        - ``algorithm`` -- (string, default ``None``) Determine the algorithm (see below for
          available algorithms).
        - ``report`` -- (object, default ``None``) If not ``None``, print information on the
          progress of computation.
        - ``use_full_group`` -- (bool, default ``False``) If ``True`` then proceed as
          originally suggested by [AB2008]_. Our default method should be faster; see
          :meth:`.symmetrisation` for more details.

        The computation of symmetric Groebner bases also involves the
        computation of *classical* Groebner bases, i.e., of Groebner
        bases for ideals in polynomial rings with finitely many
        variables. For these computations, Sage provides the following
        ALGORITHMS:

        ''
            autoselect (default)

        'singular:groebner'
            Singular's ``groebner`` command

        'singular:std'
            Singular's ``std`` command

        'singular:stdhilb'
            Singular's ``stdhib`` command

        'singular:stdfglm'
            Singular's ``stdfglm`` command

        'singular:slimgb'
            Singular's ``slimgb`` command

        'libsingular:std'
            libSingular's ``std`` command

        'libsingular:slimgb'
            libSingular's ``slimgb`` command

        'toy:buchberger'
            Sage's toy/educational buchberger without strategy

        'toy:buchberger2'
            Sage's toy/educational buchberger with strategy

        'toy:d_basis'
            Sage's toy/educational d_basis algorithm

        'macaulay2:gb'
            Macaulay2's ``gb`` command (if available)

        'magma:GroebnerBasis'
            Magma's ``Groebnerbasis`` command (if available)


        If only a system is given - e.g. 'magma' - the default algorithm is
        chosen for that system.

        .. note::

           The Singular and libSingular versions of the respective
           algorithms are identical, but the former calls an external
           Singular process while the later calls a C function, i.e. the
           calling overhead is smaller.

        EXAMPLES::

            sage: X.<x,y> = InfinitePolynomialRing(QQ)
            sage: I1 = X*(x[1]+x[2],x[1]*x[2])
            sage: I1.groebner_basis()
            [x_1]
            sage: I2 = X*(y[1]^2*y[3]+y[1]*x[3])
            sage: I2.groebner_basis()
            [x_1*y_2 + y_2^2*y_1, x_2*y_1 + y_2*y_1^2]

        Note that a symmetric Groebner basis of a principal ideal is
        not necessarily formed by a single polynomial.

        When using the algorithm originally suggested by Aschenbrenner
        and Hillar, the result is the same, but the computation takes
        much longer::

            sage: I2.groebner_basis(use_full_group=True)
            [x_1*y_2 + y_2^2*y_1, x_2*y_1 + y_2*y_1^2]

        Last, we demonstrate how the report on the progress of
        computations looks like::

            sage: I1.groebner_basis(report=True, reduced=True)
            Symmetric interreduction
            [1/2]  >
            [2/2] :>
            [1/2]  >
            [2/2]  >
            Symmetrise 2 polynomials at level 2
            Apply permutations
            >
            >
            Symmetric interreduction
            [1/3]  >
            [2/3]  >
            [3/3] :>
            -> 0
            [1/2]  >
            [2/2]  >
            Symmetrisation done
            Classical Groebner basis
            -> 2 generators
            Symmetric interreduction
            [1/2]  >
            [2/2]  >
            Symmetrise 2 polynomials at level 3
            Apply permutations
            >
            >
            :>
            ::>
            :>
            ::>
            Symmetric interreduction
            [1/4]  >
            [2/4] :>
            -> 0
            [3/4] ::>
            -> 0
            [4/4] :>
            -> 0
            [1/1]  >
            Apply permutations
            :>
            :>
            :>
            Symmetric interreduction
            [1/1]  >
            Classical Groebner basis
            -> 1 generators
            Symmetric interreduction
            [1/1]  >
            Symmetrise 1 polynomials at level 4
            Apply permutations
            >
            :>
            :>
            >
            :>
            :>
            Symmetric interreduction
            [1/2]  >
            [2/2] :>
            -> 0
            [1/1]  >
            Symmetric interreduction
            [1/1]  >
            [x_1]

        The Aschenbrenner-Hillar algorithm is only guaranteed to work
        if the base ring is a field. So, we raise a TypeError if this
        is not the case::

            sage: R.<x,y> = InfinitePolynomialRing(ZZ)
            sage: I = R*[x[1]+x[2],y[1]]
            sage: I.groebner_basis()
            Traceback (most recent call last):
            ...
            TypeError: The base ring (= Integer Ring) must be a field

        TESTS:

        In an earlier version, the following examples failed::

            sage: X.<y,z> = InfinitePolynomialRing(GF(5),order='degrevlex')
            sage: I = ['-2*y_0^2 + 2*z_0^2 + 1', '-y_0^2 + 2*y_0*z_0 - 2*z_0^2 - 2*z_0 - 1', 'y_0*z_0 + 2*z_0^2 - 2*z_0 - 1', 'y_0^2 + 2*y_0*z_0 - 2*z_0^2 + 2*z_0 - 2', '-y_0^2 - 2*y_0*z_0 - z_0^2 + y_0 - 1']*X
            sage: I.groebner_basis()
            [1]

            sage: Y.<x,y> = InfinitePolynomialRing(GF(3), order='degrevlex', implementation='sparse')
            sage: I = ['-y_3']*Y
            sage: I.groebner_basis()
            [y_1]

        """
        # determine maximal generator index
        # and construct a common parent for the generators of self
        if algorithm is None:
            algorithm = ''
        PARENT = self.ring()
        if not (hasattr(PARENT.base_ring(), 'is_field')
                and PARENT.base_ring().is_field()):
            raise TypeError("The base ring (= %s) must be a field" %
                            PARENT.base_ring())
        OUT = self.symmetrisation(tailreduce=tailreduce,
                                  report=report,
                                  use_full_group=use_full_group)
        if not (report is None):
            print("Symmetrisation done")
        VarList = set([])
        for P in OUT.gens():
            if P._p != 0:
                if P.is_unit():
                    return Sequence([PARENT(1)], PARENT, check=False)
                VarList = VarList.union([str(X) for X in P.variables()])
        VarList = list(VarList)
        if not VarList:
            return Sequence([PARENT(0)], PARENT, check=False)
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        N = max([int(X.split('_')[1]) for X in VarList] + [1])

        #from sage.combinat.permutation import Permutations
        while (1):
            if hasattr(PARENT, '_P'):
                CommonR = PARENT._P
            else:
                VarList = set([])
                for P in OUT.gens():
                    if P._p != 0:
                        if P.is_unit():
                            return Sequence([PARENT(1)], PARENT, check=False)
                        VarList = VarList.union(
                            [str(X) for X in P.variables()])
                VarList = list(VarList)
                VarList.sort(key=PARENT.varname_key, reverse=True)
                CommonR = PolynomialRing(PARENT._base,
                                         VarList,
                                         order=PARENT._order)

            try:  # working around one libsingular bug and one libsingular oddity
                DenseIdeal = [
                    CommonR(P._p) if
                    ((CommonR is P._p.parent()) or CommonR.ngens() !=
                     P._p.parent().ngens()) else CommonR(repr(P._p))
                    for P in OUT.gens()
                ] * CommonR
            except Exception:
                if report is not None:
                    print("working around a libsingular bug")
                DenseIdeal = [repr(P._p) for P in OUT.gens()] * CommonR

            if report is not None:
                print("Classical Groebner basis")
                if algorithm != '':
                    print("(using %s)" % algorithm)
            newOUT = (DenseIdeal.groebner_basis(algorithm) * PARENT)
            if report is not None:
                print("->", len(newOUT.gens()), 'generators')
            # Symmetrise out to the next index:
            N += 1
            newOUT = newOUT.symmetrisation(N=N,
                                           tailreduce=tailreduce,
                                           report=report,
                                           use_full_group=use_full_group)
            if [X.lm() for X in OUT.gens()] == [X.lm() for X in newOUT.gens()]:
                if reduced:
                    if tailreduce:
                        return Sequence(newOUT.normalisation().gens(),
                                        PARENT,
                                        check=False)
                    return Sequence(newOUT.interreduction(
                        tailreduce=True, report=report).normalisation().gens(),
                                    PARENT,
                                    check=False)
                return Sequence(newOUT.gens(), PARENT, check=False)
            OUT = newOUT
示例#4
0
    def groebner_basis(self, tailreduce=False, reduced=True, algorithm=None, report=None, use_full_group=False):
        """
        Return a symmetric Groebner basis (type :class:`~sage.structure.sequence.Sequence`) of ``self``.

        INPUT:

        - ``tailreduce`` -- (bool, default ``False``) If True, use tail reduction
          in intermediate computations
        - ``reduced`` -- (bool, default ``True``) If ``True``, return the reduced normalised
          symmetric Groebner basis.
        - ``algorithm`` -- (string, default ``None``) Determine the algorithm (see below for
          available algorithms).
        - ``report`` -- (object, default ``None``) If not ``None``, print information on the
          progress of computation.
        - ``use_full_group`` -- (bool, default ``False``) If ``True`` then proceed as
          originally suggested by [AB2008]_. Our default method should be faster; see
          :meth:`.symmetrisation` for more details.

        The computation of symmetric Groebner bases also involves the
        computation of *classical* Groebner bases, i.e., of Groebner
        bases for ideals in polynomial rings with finitely many
        variables. For these computations, Sage provides the following
        ALGORITHMS:

        ''
            autoselect (default)

        'singular:groebner'
            Singular's ``groebner`` command

        'singular:std'
            Singular's ``std`` command

        'singular:stdhilb'
            Singular's ``stdhib`` command

        'singular:stdfglm'
            Singular's ``stdfglm`` command

        'singular:slimgb'
            Singular's ``slimgb`` command

        'libsingular:std'
            libSingular's ``std`` command

        'libsingular:slimgb'
            libSingular's ``slimgb`` command

        'toy:buchberger'
            Sage's toy/educational buchberger without strategy

        'toy:buchberger2'
            Sage's toy/educational buchberger with strategy

        'toy:d_basis'
            Sage's toy/educational d_basis algorithm

        'macaulay2:gb'
            Macaulay2's ``gb`` command (if available)

        'magma:GroebnerBasis'
            Magma's ``Groebnerbasis`` command (if available)


        If only a system is given - e.g. 'magma' - the default algorithm is
        chosen for that system.

        .. note::

           The Singular and libSingular versions of the respective
           algorithms are identical, but the former calls an external
           Singular process while the later calls a C function, i.e. the
           calling overhead is smaller.

        EXAMPLES::

            sage: X.<x,y> = InfinitePolynomialRing(QQ)
            sage: I1 = X*(x[1]+x[2],x[1]*x[2])
            sage: I1.groebner_basis()
            [x_1]
            sage: I2 = X*(y[1]^2*y[3]+y[1]*x[3])
            sage: I2.groebner_basis()
            [x_1*y_2 + y_2^2*y_1, x_2*y_1 + y_2*y_1^2]

        Note that a symmetric Groebner basis of a principal ideal is
        not necessarily formed by a single polynomial.

        When using the algorithm originally suggested by Aschenbrenner
        and Hillar, the result is the same, but the computation takes
        much longer::

            sage: I2.groebner_basis(use_full_group=True)
            [x_1*y_2 + y_2^2*y_1, x_2*y_1 + y_2*y_1^2]

        Last, we demonstrate how the report on the progress of
        computations looks like::

            sage: I1.groebner_basis(report=True, reduced=True)
            Symmetric interreduction
            [1/2]  >
            [2/2] :>
            [1/2]  >
            [2/2]  >
            Symmetrise 2 polynomials at level 2
            Apply permutations
            >
            >
            Symmetric interreduction
            [1/3]  >
            [2/3]  >
            [3/3] :>
            -> 0
            [1/2]  >
            [2/2]  >
            Symmetrisation done
            Classical Groebner basis
            -> 2 generators
            Symmetric interreduction
            [1/2]  >
            [2/2]  >
            Symmetrise 2 polynomials at level 3
            Apply permutations
            >
            >
            :>
            ::>
            :>
            ::>
            Symmetric interreduction
            [1/4]  >
            [2/4] :>
            -> 0
            [3/4] ::>
            -> 0
            [4/4] :>
            -> 0
            [1/1]  >
            Apply permutations
            :>
            :>
            :>
            Symmetric interreduction
            [1/1]  >
            Classical Groebner basis
            -> 1 generators
            Symmetric interreduction
            [1/1]  >
            Symmetrise 1 polynomials at level 4
            Apply permutations
            >
            :>
            :>
            >
            :>
            :>
            Symmetric interreduction
            [1/2]  >
            [2/2] :>
            -> 0
            [1/1]  >
            Symmetric interreduction
            [1/1]  >
            [x_1]

        The Aschenbrenner-Hillar algorithm is only guaranteed to work
        if the base ring is a field. So, we raise a TypeError if this
        is not the case::

            sage: R.<x,y> = InfinitePolynomialRing(ZZ)
            sage: I = R*[x[1]+x[2],y[1]]
            sage: I.groebner_basis()
            Traceback (most recent call last):
            ...
            TypeError: The base ring (= Integer Ring) must be a field

        TESTS:

        In an earlier version, the following examples failed::

            sage: X.<y,z> = InfinitePolynomialRing(GF(5),order='degrevlex')
            sage: I = ['-2*y_0^2 + 2*z_0^2 + 1', '-y_0^2 + 2*y_0*z_0 - 2*z_0^2 - 2*z_0 - 1', 'y_0*z_0 + 2*z_0^2 - 2*z_0 - 1', 'y_0^2 + 2*y_0*z_0 - 2*z_0^2 + 2*z_0 - 2', '-y_0^2 - 2*y_0*z_0 - z_0^2 + y_0 - 1']*X
            sage: I.groebner_basis()
            [1]

            sage: Y.<x,y> = InfinitePolynomialRing(GF(3), order='degrevlex', implementation='sparse')
            sage: I = ['-y_3']*Y
            sage: I.groebner_basis()
            [y_1]

        """
        # determine maximal generator index
        # and construct a common parent for the generators of self
        if algorithm is None:
            algorithm=''
        PARENT = self.ring()
        if not (hasattr(PARENT.base_ring(),'is_field') and PARENT.base_ring().is_field()):
            raise TypeError("The base ring (= %s) must be a field"%PARENT.base_ring())
        OUT = self.symmetrisation(tailreduce=tailreduce,report=report,use_full_group=use_full_group)
        if not (report is None):
            print "Symmetrisation done"
        VarList = set([])
        for P in OUT.gens():
            if P._p!=0:
                if P.is_unit():
                    return Sequence([PARENT(1)], PARENT, check=False)
                VarList = VarList.union([str(X) for X in P.variables()])
        VarList = list(VarList)
        if not VarList:
            return Sequence([PARENT(0)], PARENT, check=False)
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        N = max([int(X.split('_')[1]) for X in VarList]+[1])

        #from sage.combinat.permutation import Permutations
        while (1):
            if hasattr(PARENT,'_P'):
                CommonR = PARENT._P
            else:
                VarList = set([])
                for P in OUT.gens():
                    if P._p!=0:
                        if P.is_unit():
                            return Sequence([PARENT(1)], PARENT, check=False)
                        VarList = VarList.union([str(X) for X in P.variables()])
                VarList = list(VarList)
                VarList.sort(cmp=PARENT.varname_cmp, reverse=True)
                CommonR = PolynomialRing(PARENT._base, VarList, order=PARENT._order)

            try: # working around one libsingular bug and one libsingular oddity
                DenseIdeal = [CommonR(P._p) if ((CommonR is P._p.parent()) or CommonR.ngens()!=P._p.parent().ngens()) else CommonR(repr(P._p))  for P in OUT.gens()]*CommonR
            except Exception:
                if report is not None:
                    print "working around a libsingular bug"
                DenseIdeal = [repr(P._p) for P in OUT.gens()]*CommonR

            if report is not None:
                print "Classical Groebner basis"
                if algorithm!='':
                    print "(using %s)"%algorithm
            newOUT = (DenseIdeal.groebner_basis(algorithm)*PARENT)
            if report is not None:
                print "->",len(newOUT.gens()),'generators'
            # Symmetrise out to the next index:
            N += 1
            newOUT = newOUT.symmetrisation(N=N,tailreduce=tailreduce,report=report,use_full_group=use_full_group)
            if [X.lm() for X in OUT.gens()] == [X.lm() for X in newOUT.gens()]:
                if reduced:
                    if tailreduce:
                        return Sequence(newOUT.normalisation().gens(), PARENT, check=False)
                    return Sequence(newOUT.interreduction(tailreduce=True, report=report).normalisation().gens(), PARENT, check=False)
                return Sequence(newOUT.gens(), PARENT, check=False)
            OUT = newOUT
示例#5
0
    def invariants_of_degree(self, deg, chi=None, R=None):
        r"""
        Return the (relative) invariants of given degree for this group.

        For this group, compute the invariants of degree ``deg``
        with respect to the group character ``chi``. The method
        is to project each possible monomial of degree ``deg`` via
        the Reynolds operator. Note that if the polynomial ring ``R``
        is specified it's base ring may be extended if the resulting
        invariant is defined over a bigger field.

        INPUT:

        - ``degree`` -- a positive integer

        - ``chi`` -- (default: trivial character) a linear group character of this group

        - ``R`` -- (optional) a polynomial ring

        OUTPUT: list of polynomials

        EXAMPLES::

            sage: Gr = MatrixGroup(SymmetricGroup(2))
            sage: sorted(Gr.invariants_of_degree(3))
            [x0^2*x1 + x0*x1^2, x0^3 + x1^3]
            sage: R.<x,y> = QQ[]
            sage: sorted(Gr.invariants_of_degree(4, R=R))
            [x^2*y^2, x^3*y + x*y^3, x^4 + y^4]

        ::

            sage: R.<x,y,z> = QQ[]
            sage: Gr = MatrixGroup(DihedralGroup(3))
            sage: ct = Gr.character_table()
            sage: chi = Gr.character(ct[0])
            sage: all(f(*(g.matrix()*vector(R.gens()))) == chi(g)*f
            ....: for f in Gr.invariants_of_degree(3, R=R, chi=chi) for g in Gr)
            True

        ::

            sage: i = GF(7)(3)
            sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]])
            sage: G.invariants_of_degree(25)
            []

        ::

            sage: G = MatrixGroup(SymmetricGroup(5))
            sage: R = QQ['x,y']
            sage: G.invariants_of_degree(3, R=R)
            Traceback (most recent call last):
            ...
            TypeError: number of variables in polynomial ring must match size of matrices

        ::

            sage: K.<i> = CyclotomicField(4)
            sage: G =  MatrixGroup(CyclicPermutationGroup(3))
            sage: chi = G.character(G.character_table()[1])
            sage: R.<x,y,z> = K[]
            sage: sorted(G.invariants_of_degree(2, R=R, chi=chi))
            [x*y + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*x*z + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y*z,
             x^2 + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y^2 + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*z^2]

        ::

            sage: S3 = MatrixGroup(SymmetricGroup(3))
            sage: chi = S3.character(S3.character_table()[0])
            sage: sorted(S3.invariants_of_degree(5, chi=chi))
            [x0^3*x1^2 - x0^2*x1^3 - x0^3*x2^2 + x1^3*x2^2 + x0^2*x2^3 - x1^2*x2^3,
            x0^4*x1 - x0*x1^4 - x0^4*x2 + x1^4*x2 + x0*x2^4 - x1*x2^4]
        """
        D = self.degree()
        deg = int(deg)
        if deg <= 0:
            raise ValueError("degree must be a positive integer")
        if R is None:
            R = PolynomialRing(self.base_ring(), 'x', D)
        elif R.ngens() != D:
            raise TypeError(
                "number of variables in polynomial ring must match size of matrices"
            )

        ms = self.molien_series(prec=deg + 1, chi=chi)
        if ms[deg].is_zero():
            return []
        inv = set()
        for e in IntegerVectors(deg, D):
            F = self.reynolds_operator(R.monomial(*e), chi=chi)
            if not F.is_zero():
                F = F / F.lc()
                inv.add(F)
                if len(inv) == ms[deg]:
                    break
        return list(inv)
    def Chow_form(self):
        r"""
        Returns the Chow form associated to this subscheme.

        For a `k`-dimensional subvariety of `\mathbb{P}^N` of degree `D`.
        The `(N-k-1)`-dimensional projective linear subspaces of `\mathbb{P}^N`
        meeting `X` form a hypersurface in the Grassmannian `G(N-k-1,N)`.
        The homogeneous form of degree `D` defining this hypersurface in Plucker
        coordinates is called the Chow form of `X`.

        The base ring needs to be a number field, finite field, or `\QQbar`.

        ALGORITHM:

        For a `k`-dimension subscheme `X` consider the `k+1` linear forms
        `l_i = u_{i0}x_0 + \cdots + u_{in}x_n`. Let `J` be the ideal in the
        polynomial ring `K[x_i,u_{ij}]` defined by the equations of `X` and the `l_i`.
        Let `J'` be the saturation of `J` with respect to the irrelevant ideal of
        the ambient projective space of `X`. The elimination ideal `I = J' \cap K[u_{ij}]`
        is a principal ideal, let `R` be its generator. The Chow form is obtained by
        writing `R` as a polynomial in Plucker coordinates (i.e. bracket polynomials).
        [DalbecSturmfels]_.

        OUTPUT: a homogeneous polynomial.

        REFERENCES:

        .. [DalbecSturmfels] J. Dalbec and B. Sturmfels. Invariant methods in discrete and computational geometry,
           chapter Introduction to Chow forms, pages 37-58. Springer Netherlands, 1994.

        EXAMPLES::

            sage: P.<x0,x1,x2,x3> = ProjectiveSpace(GF(17), 3)
            sage: X = P.subscheme([x3+x1,x2-x0,x2-x3])
            sage: X.Chow_form()
            t0 - t1 + t2 + t3

        ::

            sage: P.<x0,x1,x2,x3> = ProjectiveSpace(QQ,3)
            sage: X = P.subscheme([x3^2 -101*x1^2 - 3*x2*x0])
            sage: X.Chow_form()
            t0^2 - 101*t2^2 - 3*t1*t3

        ::

            sage: P.<x0,x1,x2,x3>=ProjectiveSpace(QQ,3)
            sage: X = P.subscheme([x0*x2-x1^2, x0*x3-x1*x2, x1*x3-x2^2])
            sage: Ch = X.Chow_form(); Ch
            t2^3 + 2*t2^2*t3 + t2*t3^2 - 3*t1*t2*t4 - t1*t3*t4 + t0*t4^2 + t1^2*t5
            sage: Y = P.subscheme_from_Chow_form(Ch, 1); Y
            Closed subscheme of Projective Space of dimension 3 over Rational Field
            defined by:
              x2^2*x3 - x1*x3^2,
              -x2^3 + x0*x3^2,
              -x2^2*x3 + x1*x3^2,
              x1*x2*x3 - x0*x3^2,
              3*x1*x2^2 - 3*x0*x2*x3,
              -2*x1^2*x3 + 2*x0*x2*x3,
              -3*x1^2*x2 + 3*x0*x1*x3,
              x1^3 - x0^2*x3,
              x2^3 - x1*x2*x3,
              -3*x1*x2^2 + 2*x1^2*x3 + x0*x2*x3,
              2*x0*x2^2 - 2*x0*x1*x3,
              3*x1^2*x2 - 2*x0*x2^2 - x0*x1*x3,
              -x0*x1*x2 + x0^2*x3,
              -x0*x1^2 + x0^2*x2,
              -x1^3 + x0*x1*x2,
              x0*x1^2 - x0^2*x2
            sage: I = Y.defining_ideal()
            sage: I.saturation(I.ring().ideal(list(I.ring().gens())))[0]
            Ideal (x2^2 - x1*x3, x1*x2 - x0*x3, x1^2 - x0*x2) of Multivariate
            Polynomial Ring in x0, x1, x2, x3 over Rational Field
        """
        I = self.defining_ideal()
        P = self.ambient_space()
        R = P.coordinate_ring()
        N = P.dimension()+1
        d = self.dimension()
        #create the ring for the generic linear hyperplanes
        # u0x0 + u1x1 + ...
        SS = PolynomialRing(R.base_ring(), 'u', N*(d+1), order='lex')
        vars = SS.variable_names() + R.variable_names()
        S = PolynomialRing(R.base_ring(), vars, order='lex')
        n = S.ngens()
        newcoords = [S.gen(n-N+t) for t in range(N)]
        #map the generators of the subscheme into the ring with the hyperplane variables
        phi = R.hom(newcoords,S)
        phi(self.defining_polynomials()[0])
        #create the dim(X)+1 linear hyperplanes
        l = []
        for i in range(d+1):
            t = 0
            for j in range(N):
                t += S.gen(N*i + j)*newcoords[j]
            l.append(t)
        #intersect the hyperplanes with X
        J = phi(I) + S.ideal(l)
        #saturate the ideal with respect to the irrelevant ideal
        J2 = J.saturation(S.ideal([phi(t) for t in R.gens()]))[0]
        #eliminate the original variables to be left with the hyperplane coefficients 'u'
        E = J2.elimination_ideal(newcoords)
        #create the plucker coordinates
        D = binomial(N,N-d-1) #number of plucker coordinates
        tvars = [str('t') + str(i) for i in range(D)] #plucker coordinates
        T = PolynomialRing(R.base_ring(), tvars+list(S.variable_names()), order='lex')
        L = []
        coeffs = [T.gen(i) for i in range(0+len(tvars), N*(d+1)+len(tvars))]
        M = matrix(T,d+1,N,coeffs)
        i = 0
        for c in M.minors(d+1):
            L.append(T.gen(i)-c)
            i += 1
        #create the ideal that we can use for eliminating to get a polynomial
        #in the plucker coordinates (brackets)
        br = T.ideal(L)
        #create a mapping into a polynomial ring over the plucker coordinates
        #and the hyperplane coefficients
        psi = S.hom(coeffs + [0 for i in range(N)],T)
        E2 = T.ideal([psi(u) for u in E.gens()] +br)
        #eliminate the hyperplane coefficients
        CH = E2.elimination_ideal(coeffs)
        #CH should be a principal ideal, but because of the relations among
        #the plucker coordinates, the elimination will probably have several generators

        #get the relations among the plucker coordinates
        rel = br.elimination_ideal(coeffs)
        #reduce CH with respect to the relations
        reduced = []
        for f in CH.gens():
            reduced.append(f.reduce(rel))
        #find the principal generator

        #polynomial ring in just the plucker coordinates
        T2 = PolynomialRing(R.base_ring(), tvars)
        alp = T.hom(tvars + (N*(d+1) +N)*[0], T2)
        #get the degrees of the reduced generators of CH
        degs = [u.degree() for u in reduced]
        mind = max(degs)
        #need the smallest degree form that did not reduce to 0
        for d in degs:
            if d < mind and d >0:
                mind = d
        ind = degs.index(mind)
        CF = reduced[ind] #this should be the Chow form of X
        #check that it is correct (i.e., it is a principal generator for CH + the relations)
        rel2 = rel + [CF]
        assert all([f in rel2 for f in CH.gens()]), "did not find a principal generator"
        return(alp(CF))
    def has_irred_rep(self, n, gen_set=None, restrict=None, force=False):
        """
        Returns `True` if there exists an `n`-dimensional irreducible representation of `self`,
        and `False` otherwise. Of course, this function runs `has_rep(n, restrict)` to verify
        there is a representation in the first place, and returns `False` if not.

        The argument `restrict` may be used equivalenty to its use in `has_rep()`.

        The argument `gen_set` may be set to `'PBW'` or `'pbw'`, if `self` has an algebra basis
        similar to that of a Poincaré-Birkhoff-Witt basis.

        Alternatively, an explicit generating set for the algorithm implemented by this function
        can be given, as a tuple or array of `FreeAlgebraElements`. This is only useful if the
        package cannot reduce the elements of `self`, but they can be reduced in theory.

        Use `force=True` if the function does not recognize the base field as computable, but the
        field is computable.
        """
        if (not force and self.base_field() not in NumberFields
                and self.base_field() not in FiniteFields):
            raise TypeError(
                'Base field must be computable. If %s is computable' %
                self.base_field() + ' then use force=True to bypass this.')

        if n not in ZZ or n < 1:
            raise ValueError('Dimension must be a positive integer.')

        if not self.has_rep(n, restrict): return False
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        from sage.groups.all import SymmetricGroup
        import math

        B = PolynomialRing(self.base_field(), (self.ngens() * n**2 + 1),
                           'x',
                           order='deglex')
        M = MatrixSpace(B, n, sparse=True)

        gen_matrix = list()
        if not isinstance(restrict, (tuple, list)):
            restrict = [None for i in range(self.ngens())]
        if len(restrict) != self.ngens():
            raise ValueError(
                'Length of restrict does not match number of generators.')
        for i in range(self.ngens()):
            ith_gen_matrix = []
            for j in range(n):
                for k in range(n):
                    if restrict[i] == 'upper' and j > k:
                        ith_gen_matrix.append(B.zero())
                    elif restrict[i] == 'lower' and j < k:
                        ith_gen_matrix.append(B.zero())
                    elif restrict[i] == 'diagonal' and j != k:
                        ith_gen_matrix.append(B.zero())
                    else:
                        ith_gen_matrix.append(B.gen(j + (j + 1) * k +
                                                    i * n**2))
            gen_matrix.append(M(ith_gen_matrix))

        relB = list()
        for i in range(self.nrels()):
            relB += self._to_matrix(self.rel(i), M, gen_matrix).list()

        Z = FreeAlgebra(ZZ, 2 * n - 2, 'Y')
        standard_poly = Z(0)
        for s in SymmetricGroup(2 * n - 2).list():
            standard_poly += s.sign() * reduce(
                lambda x, y: x * y, [Z('Y%s' % (i - 1)) for i in s.tuple()])

        if n <= 6 and is_NumberField(self.base_field()): p = 2 * n
        else:
            p = int(
                math.floor(n *
                           math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) +
                           n / float(2) - 3))

        if isinstance(gen_set, (tuple, list)):
            try:
                gen_set = [
                    self._to_matrix(elt, M, gen_matrix) for elt in gen_set
                ]
            except (NameError, TypeError) as error:
                print(error)

        if gen_set == None:
            word_gen_set = list(self._create_rep_gen_set(n, p))
            gen_set = [
                self._to_matrix(_to_element(self, [[word, self.one()]]), M,
                                gen_matrix) for word in word_gen_set
            ]

        elif gen_set == 'pbw' or gen_set == 'PBW':
            word_gen_set = list(self._create_pbw_rep_gen_set(n, p))
            gen_set = [
                self._to_matrix(_to_element(self, [[word, self.one()]]), M,
                                gen_matrix) for word in word_gen_set
            ]

        else:
            raise TypeError('Invalid generating set.')

        ordering = [i for i in range(2 * n - 2)]
        max_ordering = [
            len(gen_set) - (2 * n - 2) + i for i in range(2 * n - 2)
        ]
        ordering.insert(0, 0)
        max_ordering.insert(0, len(gen_set))
        rep_exists = False
        z = B.gen(B.ngens() - 1)

        while ordering[0] != max_ordering[0]:
            y = gen_set[ordering[0]].trace_of_product(
                standard_poly.subs({
                    Z('Y%s' % (j - 1)): gen_set[ordering[j]]
                    for j in range(1, 2 * n - 1)
                }))
            radB_test = relB + [B(1) - z * y]
            if B.one() not in B.ideal(radB_test):
                rep_exists = True
                break
            for i in range(2 * n - 2, -1, -1):
                if i != 0 and ordering[i] != max_ordering[i]:
                    ordering[i] += 1
                    break
                elif i == 0:
                    ordering[i] += 1
                    if ordering[i] != max_ordering[i]:
                        for j in range(1, 2 * n - 1):
                            ordering[j] = j - 1

        return rep_exists
示例#8
0
    def one_step_elimination(self,
                             coordinate_index,
                             bsa_class='linear_system'):
        r"""
        Compute the projection by eliminating ``coordinates``  as a new instance of
        ``BasicSemialgebraicSet_polyhedral_linear_system``.
        """
        # create a new poly_ring with one less generator (coordinate).
        if coordinate_index >= self._poly_ring.ngens():
            raise ValueError("doesn't exist the elimination variable")
        coordinate = self._poly_ring.gens()[coordinate_index]
        variables_names = [
            str(self._poly_ring.gens()[i])
            for i in range(self._poly_ring.ngens()) if i != coordinate_index
        ]
        new_poly_ring = PolynomialRing(self._base_ring, variables_names,
                                       len(variables_names))
        # create the ring hommorphism
        polynomial_map = [
            new_poly_ring.gens()[i] for i in range(new_poly_ring.ngens())
        ]
        polynomial_map.insert(coordinate_index, new_poly_ring(0))

        new_eq = []
        new_lt = []
        new_le = []
        new_history_set = {}
        # try to find a substitution of coordinate in equalities.
        sub = None
        for e in self._eq:
            if e.monomial_coefficient(coordinate) != self.base_ring()(0):
                sub = coordinate - e / e.monomial_coefficient(coordinate)
                sub_history = self.history_set[e]
                break
        if sub is None:
            new_eq = self._eq
            for e in self._eq:
                new_history_set[e] = self.history_set[e]

            lt_lower = []
            lt_upper = []
            le_lower = []
            le_upper = []

            for lt in self._lt:
                if self._base_ring(lt.monomial_coefficient(
                        coordinate)) > self.base_ring()(0):
                    lt_upper.append(lt)
                elif self._base_ring(lt.monomial_coefficient(
                        coordinate)) < self.base_ring()(0):
                    lt_lower.append(lt)
                else:
                    new_lt.append(lt)
                    new_history_set[lt] = self.history_set[lt]
            for le in self._le:
                if self._base_ring(le.monomial_coefficient(
                        coordinate)) > self.base_ring()(0):
                    le_upper.append(le)
                elif self._base_ring(le.monomial_coefficient(
                        coordinate)) < self.base_ring()(0):
                    le_lower.append(le)
                else:
                    new_le.append(le)
                    new_history_set[le] = self.history_set[le]

            # compute less than or equal to inequality
            for l in le_lower:
                for u in le_upper:
                    polynomial = l * u.monomial_coefficient(coordinate) - (
                        u * l.monomial_coefficient(coordinate))
                    new_le.append(polynomial)
                    new_history_set[polynomial] = self.history_set[l].union(
                        self.history_set[u])

            # compute strictly less than inequality
            for l in le_lower:
                for u in lt_upper:
                    polynomial = l * u.monomial_coefficient(coordinate) - (
                        u * l.monomial_coefficient(coordinate))
                    new_lt.append(polynomial)
                    new_history_set[polynomial] = self.history_set[l].union(
                        self.history_set[u])
            for l in lt_lower:
                for u in le_upper:
                    polynomial = l * u.monomial_coefficient(coordinate) - (
                        u * l.monomial_coefficient(coordinate))
                    new_lt.append(polynomial)
                    new_history_set[polynomial] = self.history_set[l].union(
                        self.history_set[u])
            for l in lt_lower:
                for u in lt_upper:
                    polynomial = l * u.monomial_coefficient(coordinate) - (
                        u * l.monomial_coefficient(coordinate))
                    new_lt.append(polynomial)
                    new_history_set[polynomial] = self.history_set[l].union(
                        self.history_set[u])
        else:
            for e in self._eq:
                polynomial = e + e.monomial_coefficient(coordinate) * (
                    sub - coordinate)
                new_eq.append(polynomial)
                if e.monomial_coefficient(coordinate) != self.base_ring()(0):
                    new_history_set[polynomial] = self.history_set[e].union(
                        sub_history)
                else:
                    new_history_set[polynomial] = self.history_set[e]
            for lt in self._lt:
                polynomial = lt + lt.monomial_coefficient(coordinate) * (
                    sub - coordinate)
                new_lt.append(polynomial)
                if lt.monomial_coefficient(coordinate) != self.base_ring()(0):
                    new_history_set[polynomial] = self.history_set[lt].union(
                        sub_history)
                else:
                    new_history_set[polynomial] = self.history_set[lt]
            for le in self._le:
                polynomial = le + le.monomial_coefficient(coordinate) * (
                    sub - coordinate)
                new_le.append(polynomial)
                if le.monomial_coefficient(coordinate) != self.base_ring()(0):
                    new_history_set[polynomial] = self.history_set[le].union(
                        sub_history)
                else:
                    new_history_set[polynomial] = self.history_set[le]

        bsa = BasicSemialgebraicSet_polyhedral_linear_system(
            base_ring=self._base_ring,
            ambient_dim=self.ambient_dim(),
            poly_ring=self._poly_ring,
            eq=new_eq,
            lt=new_lt,
            le=new_le,
            history_set=new_history_set)
        new_bsa = bsa.section(polynomial_map,
                              bsa_class=bsa_class,
                              poly_ring=new_poly_ring,
                              history_set=new_history_set)
        down_stair_history_set = {}
        for key in new_bsa.history_set.keys():
            down_stair_history_set[key(
                polynomial_map)] = new_bsa.history_set[key]
        new_bsa.history_set = down_stair_history_set
        # remove constant polynomial
        new_bsa.remove_redundant_constant_polynomial()
        # remove polynomial with non_minimal_history_set
        new_bsa.remove_redundancy_non_minimal_history_set()
        return new_bsa
示例#9
0
class DifferentialPolynomialRing:
    element_class = DifferentialPolynomial

    def __init__(self, base_ring, fibre_names, base_names,
                 max_differential_orders):
        self._fibre_names = tuple(fibre_names)
        self._base_names = tuple(base_names)
        self._max_differential_orders = tuple(max_differential_orders)
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        jet_names = []
        idx_to_name = {}
        for fibre_idx in range(fibre_dim):
            u = self._fibre_names[fibre_idx]
            idx_to_name[(fibre_idx, ) + tuple([0] * base_dim)] = u
            for d in range(1, max_differential_orders[fibre_idx] + 1):
                for multi_index in IntegerVectors(d, base_dim):
                    v = '{}_{}'.format(
                        u, ''.join(self._base_names[i] * multi_index[i]
                                   for i in range(base_dim)))
                    jet_names.append(v)
                    idx_to_name[(fibre_idx, ) + tuple(multi_index)] = v
        self._polynomial_ring = PolynomialRing(
            base_ring, base_names + fibre_names + tuple(jet_names))
        self._idx_to_var = {
            idx: self._polynomial_ring(idx_to_name[idx])
            for idx in idx_to_name
        }
        self._var_to_idx = {
            jet: idx
            for (idx, jet) in self._idx_to_var.items()
        }
        # for conversion:
        base_vars = [var(b) for b in self._base_names]
        symbolic_functions = [
            function(f)(*base_vars) for f in self._fibre_names
        ]
        self._subs_jet_vars = SubstituteJetVariables(symbolic_functions)
        self._subs_tot_ders = SubstituteTotalDerivatives(symbolic_functions)

    def __repr__(self):
        return 'Differential Polynomial Ring in {} over {}'.format(
            ', '.join(map(repr, self._polynomial_ring.gens())),
            self._polynomial_ring.base_ring())

    def _latex_(self):
        return self._polynomial_ring._latex_()

    def base_ring(self):
        return self._polynomial_ring.base_ring()

    def _first_ngens(self, n):
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(i))
            for i in range(n))

    def gens(self):
        return self._first_ngens(self._polynomial_ring.ngens())

    def gen(self, i):
        return self.element_class(self, self._polynomial_ring.gen(i))

    def base_variables(self):
        return self._first_ngens(len(self._base_names))

    def base_dim(self):
        return len(self._base_names)

    def fibre_variable(self, i):
        return self.element_class(
            self, self._polynomial_ring.gen(len(self._base_names) + i))

    def fibre_variables(self):
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(base_dim + i))
            for i in range(fibre_dim))

    def fibre_dim(self):
        return len(self._fibre_names)

    def jet_variables(self):
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        whole_dim = self._polynomial_ring.ngens()
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(i))
            for i in range(base_dim + fibre_dim, whole_dim))

    def max_differential_orders(self):
        return self._max_differential_orders

    def _single_var_weights(self, u):
        return self._var_to_idx[u][1:]

    def _diff_single_var(self, u, x):
        x_idx = self._polynomial_ring.gens().index(x)
        u_idx = self._var_to_idx[u]
        du_idx = list(u_idx)
        du_idx[1 + x_idx] += 1
        du_idx = tuple(du_idx)
        if du_idx in self._idx_to_var:
            return self._idx_to_var[du_idx]
        else:
            raise ValueError(
                "can't differentiate {} any further with respect to {}".format(
                    u, x))

    def _integrate_single_var(self, u, x):
        x_idx = self._polynomial_ring.gens().index(x)
        u_idx = self._var_to_idx[u]
        if u_idx[1 + x_idx] == 0:
            raise ValueError(
                "can't integrate {} any further with respect to {}".format(
                    u, x))
        iu_idx = list(u_idx)
        iu_idx[1 + x_idx] -= 1
        iu_idx = tuple(iu_idx)
        return self._idx_to_var[iu_idx]

    def __contains__(self, arg):
        if isinstance(arg, self.element_class) and arg.parent() is self:
            return True
        if arg in self._polynomial_ring.base_ring():
            return True
        return False

    def __call__(self, arg):
        if isinstance(arg, self.element_class) and arg.parent() is self:
            return arg
        if is_Expression(arg):
            arg = self._subs_jet_vars(arg)
        return self.element_class(self, self._polynomial_ring(arg))

    def zero(self):
        return self.element_class(self, self._polynomial_ring.zero())

    def one(self):
        return self.element_class(self, self._polynomial_ring.one())

    def homogeneous_monomials(self,
                              fibre_degrees,
                              weights,
                              max_differential_orders=None):
        fibre_vars = self.fibre_variables()
        if not len(fibre_degrees) == len(fibre_vars):
            raise ValueError(
                'length of fibre_degrees vector must match number of fibre variables'
            )
        base_vars = self.base_variables()
        if not len(weights) == len(base_vars):
            raise ValueError(
                'length of weights vector must match number of base variables')
        monomials = []
        fibre_degree = sum(fibre_degrees)
        fibre_indexes = {}
        fibre_idx = 0
        for i in range(len(fibre_degrees)):
            for j in range(fibre_degrees[i]):
                fibre_indexes[fibre_idx] = i
                fibre_idx += 1
        proto = sum([[fibre_vars[i]] * fibre_degrees[i]
                     for i in range(len(fibre_degrees))], [])
        for V in product(*[IntegerVectors(w, fibre_degree) for w in weights]):
            total_differential_order = [0 for i in range(fibre_degree)]
            term = [p for p in proto]
            skip = False
            for j in range(fibre_degree):
                fibre_idx = fibre_indexes[j]
                for i in range(len(base_vars)):
                    if V[i][j] > 0:
                        total_differential_order[j] += V[i][j]
                        if max_differential_orders is not None and total_differential_order[
                                j] > max_differential_orders[fibre_idx]:
                            skip = True
                            break
                        term[j] = term[j].total_derivative(*([base_vars[i]] *
                                                             V[i][j]))
                if skip:
                    break
            if not skip:
                monomials.append(prod(term))
        return monomials