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)
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
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
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
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
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