def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True): r""" Return the Kazhdan-Lusztig polynomial `P_{u,v}`. INPUT: - ``u``, ``v`` -- elements of the underlying Coxeter group - ``constant_term_one`` -- (default: True) True uses the constant equals one convention, False uses the Leclerc-Thibon convention .. SEEALSO:: - :class:`~sage.combinat.kazhdan_lusztig.KazhdanLusztigPolynomial` - :meth:`parabolic_kazhdan_lusztig_polynomial` EXAMPLES:: sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 sage: W.kazhdan_lusztig_polynomial([], [1,2, 1]) # optional - coxeter3 1 sage: W.kazhdan_lusztig_polynomial([1],[3,2]) # optional - coxeter3 0 sage: W = CoxeterGroup(['A',3],implementation='coxeter3') # optional - coxeter3 sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2]) # optional - coxeter3 q + 1 .. NOTE:: Coxeter3, as well as Sage's native implementation in :class:`~sage.combinat.kazhdan_lusztig.KazhdanLusztigPolynomial` use the convention under which Kazhdan-Lusztig polynomials give the change of basis from the `(C_w)_{w\in W}` basis to the `(T_w)_{w\in W}` of the Hecke algebra of `W` with parameters `q` and `q^{-1}`: .. MATH:: C_w = \sum_u P_{u,w} T_w In particular, `P_{u,u}=1`:: sage: all(W.kazhdan_lusztig_polynomial(u,u) == 1 for u in W) # optional - coxeter3 True This convention differs from Theorem 2.7 in [LT1998]_ by: .. MATH:: {}^{LT} P_{y,w}(q) = q^{\ell(w)-\ell(y)} P_{y,w}(q^{-2}) To access the Leclerc-Thibon convention use:: sage: W = CoxeterGroup(['A',3],implementation='coxeter3') # optional - coxeter3 sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2],constant_term_one=False) # optional - coxeter3 q^3 + q TESTS: We check that Coxeter3 and Sage's implementation give the same results:: sage: C = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 sage: W = WeylGroup("B3",prefix="s") sage: [s1,s2,s3] = W.simple_reflections() sage: R.<q> = LaurentPolynomialRing(QQ) sage: KL = KazhdanLusztigPolynomial(W,q) sage: all(KL.P(1,w) == C.kazhdan_lusztig_polynomial([],w.reduced_word()) for w in W) # optional - coxeter3 # long (15s) True """ u, v = self(u), self(v) p = u.value.kazhdan_lusztig_polynomial(v.value) if constant_term_one: return p ZZq = PolynomialRing(ZZ, 'q', sparse=True) # This is the same as q**len_diff * p(q**(-2)) len_diff = v.length()-u.length() d = {-2*deg+len_diff: coeff for deg,coeff in enumerate(p) if coeff != 0} return ZZq(d)
def __init__(self, F, m, B, a=None): r""" Initialization routine (constructor). INPUT: - ``F`` -- number field, the base field - ``m`` -- integer, the relative degree - ``B`` -- integer, the discriminant bound - ``a`` -- list (default: []), the coefficient list to begin with, corresponding to ``a[len(a)]*x^n + ... + a[0]x^(n-len(a))``. OUTPUT: the data initialized to begin enumeration of totally real fields with base field F, degree n, discriminant bounded by B, and starting with coefficients a. EXAMPLES:: sage: F.<t> = NumberField(x^2-2) sage: T = sage.rings.number_field.totallyreal_rel.tr_data_rel(F, 2, 2000) """ if a is None: # don't make the stupid noob mistake of putting a=[] a = [] # in the function signature above. # Initialize constants. self.m = m d = F.degree() self.d = d self.n = m * d self.B = B self.gamma = hermite_constant(self.n - self.d) self.F = F self.Z_F = F.maximal_order() self.Foo = F.real_embeddings() self.dF = abs(F.disc()) self.Fx = PolynomialRing(F, 'xF') self.beta = [[]] * m self.gnk = [[]] * m self.trace_elts = [] Z_Fbasis = self.Z_F.basis() # Initialize variables. if a == []: # No starting input, all polynomials will be found; initialize to zero. self.a = [0] * m + [1] self.amaxvals = [[]] * m anm1s = [[i] for i in range(0, m // 2 + 1)] for i in range(1, self.d): for j in range(len(anm1s)): anm1s[j] = [anm1s[j] + [i] for i in range(m)] anm1s = sum(anm1s, []) anm1s = [ sum([Z_Fbasis[i] * a[i] for i in range(self.d)]) for a in anm1s ] # Minimize trace in class. import numpy for i in range(len(anm1s)): Q = [[v(m * x) for v in self.Foo] + [0] for x in Z_Fbasis] + [[v(anm1s[i]) for v in self.Foo] + [10**6]] pari_string = '[' + ';'.join( [','.join(["%s" % ii for ii in row]) for row in zip(*Q)]) + ']' adj = pari(pari_string).qflll()[self.d] anm1s[i] += sum([ m * Z_Fbasis[ii] * int(adj[ii]) // int(adj[self.d]) for ii in range(self.d) ]) self.amaxvals[m - 1] = anm1s self.a[m - 1] = self.amaxvals[m - 1].pop() self.k = m - 2 bl = math.ceil(1.7719 * self.n) br = max([1./m*(am1**2).trace() + \ self.gamma*(1./(m**d)*self.B/self.dF)**(1./(self.n-d)) for am1 in anm1s]) br = math.floor(br) T2s = self.F._positive_integral_elements_with_trace([bl, br]) self.trace_elts.append([bl, br, T2s]) elif len(a) <= m + 1: # First few coefficients have been specified. # The value of k is the largest index of the coefficients of a which is # currently unknown; e.g., if k == -1, then we can iterate # over polynomials, and if k == n-1, then we have finished iterating. if a[len(a) - 1] != 1: raise ValueError( "a[len(a)-1](=%s) must be 1 so polynomial is monic" % a[len(a) - 1]) raise NotImplementedError("These have not been checked.") k = m - len(a) self.k = k a = [0] * (k + 1) + a self.amaxvals = [[]] * m for i in range(0, n + 1): self.a[i] = a[i] # Bounds come from an application of Lagrange multipliers in degrees 2,3. self.b_lower = [ -1. / m * (v(self.a[m - 1]) + (m - 1.) * math.sqrt( v(self.a[m - 1])**2 - 2. * (1 + 1. / (m - 1)) * v(self.a[m - 2]))) for v in self.Foo ] self.b_upper = [ -1. / m * (v(self.a[m - 1]) - (m - 1.) * math.sqrt( v(self.a[m - 1])**2 - 2. * (1 + 1. / (m - 1)) * v(self.a[m - 2]))) for v in self.Foo ] if k < m - 2: bminmax = [ lagrange_degree_3(n, v(self.a[m - 1]), v(self.a[m - 2]), v(self.a[m - 3])) for v in self.Foo ] self.b_lower = bminmax[0] self.b_upper = bminmax[1] # Annoying, but must reverse coefficients for numpy. gnk = [binomial(j, k + 2) * a[j] for j in range(k + 2, n + 1)] self.beta[k + 1] = [[self.b_lower] + numpy.roots( [v(gnk[i]) for i in range(len(gnk))].reverse()).tolist().sort() + [self.b_upper] for v in self.Foo] # Now to really initialize gnk. self.gnk[k + 1] = [ [0] + [binomial(j, k + 1) * v(a[j]) for j in range(k + 2, m + 1)] for v in self.Foo ] else: # Bad input! raise ValueError("a has length %s > m+1" % len(a))
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: enumerate_totallyreal_fields_all(2, 10)[0][1].parent() Interface to the PARI C library sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0, 0, 0, 0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim( d, int(math.floor((1. * B)**(1. * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "=" * 80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] j = i + 1 for E in enumerate_totallyreal_fields_prim( n / d, int( math.floor((1. * B)**(1. / d) / (1. * Sds[i][0])**(n * 1. / d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append( [EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort() weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print "=" * 80 print "Polynomials tested: {}".format(counts[0]) print( "Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print "Irreducible polynomials: {}".format(counts[2]) print "Polynomials with nfdisc <= B: {}".format(counts[3]) for i in range(len(S)): print S[i] if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [ map(ZZ, counts), [[ZZ(s[0]), map(QQ, s[1].reverse().Vec())] for s in S] ] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px(map(QQ, s[1].list()))] for s in S]
def dual(self): r""" Return the projective dual of the given subscheme of projective space. INPUT: - ``X`` -- A subscheme of projective space. At present, ``X`` is required to be an irreducible and reduced hypersurface defined over `\QQ` or a finite field. OUTPUT: - The dual of ``X`` as a subscheme of the dual projective space. EXAMPLES: The dual of a smooth conic in the plane is also a smooth conic:: sage: R.<x, y, z> = QQ[] sage: P.<x, y, z> = ProjectiveSpace(2, QQ) sage: I = R.ideal(x^2 + y^2 + z^2) sage: X = P.subscheme(I) sage: X.dual() Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: y0^2 + y1^2 + y2^2 The dual of the twisted cubic curve in projective 3-space is a singular quartic surface. In the following example, we compute the dual of this surface, which by double duality is equal to the twisted cubic itself. The output is the twisted cubic as an intersection of three quadrics:: sage: R.<x, y, z, w> = QQ[] sage: P.<x, y, z, w> = ProjectiveSpace(3, QQ) sage: I = R.ideal(y^2*z^2 - 4*x*z^3 - 4*y^3*w + 18*x*y*z*w - 27*x^2*w^2) sage: X = P.subscheme(I) sage: X.dual() Closed subscheme of Projective Space of dimension 3 over Rational Field defined by: y2^2 - y1*y3, y1*y2 - y0*y3, y1^2 - y0*y2 The singular locus of the quartic surface in the last example is itself supported on a twisted cubic:: sage: X.Jacobian().radical() Ideal (z^2 - 3*y*w, y*z - 9*x*w, y^2 - 3*x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field An example over a finite field:: sage: R = PolynomialRing(GF(61), 'a,b,c') sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring()) sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c)) sage: X.dual() Closed subscheme of Projective Space of dimension 2 over Finite Field of size 61 defined by: y0^2 - 30*y1^2 - 20*y2^2 TESTS:: sage: R = PolynomialRing(Qp(3), 'a,b,c') sage: P.<a, b, c> = ProjectiveSpace(2, R.base_ring()) sage: X = P.subscheme(R.ideal(a*a+2*b*b+3*c*c)) sage: X.dual() Traceback (most recent call last): ... NotImplementedError: base ring must be QQ or a finite field """ from sage.libs.singular.function_factory import ff K = self.base_ring() if not (is_RationalField(K) or is_FiniteField(K)): raise NotImplementedError("base ring must be QQ or a finite field") I = self.defining_ideal() m = I.ngens() n = I.ring().ngens() - 1 if (m != 1 or (n < 1) or I.is_zero() or I.is_trivial() or not I.is_prime()): raise NotImplementedError("At the present, the method is only" " implemented for irreducible and" " reduced hypersurfaces and the given" " list of generators for the ideal must" " have exactly one element.") R = PolynomialRing(K, 'x', n + 1) from sage.schemes.projective.projective_space import ProjectiveSpace Pd = ProjectiveSpace(n, K, 'y') Rd = Pd.coordinate_ring() x = R.variable_names() y = Rd.variable_names() S = PolynomialRing(K, x + y + ('t', )) if S.has_coerce_map_from(I.ring()): T = PolynomialRing(K, 'w', n + 1) I_S = (I.change_ring(T)).change_ring(S) else: I_S = I.change_ring(S) f_S = I_S.gens()[0] z = S.gens() J = I_S for i in range(n + 1): J = J + S.ideal(z[-1] * f_S.derivative(z[i]) - z[i + n + 1]) sat = ff.elim__lib.sat max_ideal = S.ideal(z[n + 1:2 * n + 2]) J_sat_gens = sat(J, max_ideal)[0] J_sat = S.ideal(J_sat_gens) L = J_sat.elimination_ideal(z[0:n + 1] + (z[-1], )) return Pd.subscheme(L.change_ring(Rd))
def ehrhart_polynomial(self, verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, smith_form=None, dualization=None, triangulation=None, triangulation_max_height=None, **kwds): r""" Return the Ehrhart polynomial of this polyhedron. Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a rational polynomial of degree `d` for integer `t`. `L` is called the *Ehrhart polynomial* of `P`. For more information see the :wikipedia:`Ehrhart_polynomial`. INPUT: - ``verbose`` - (boolean, default to ``False``) if ``True``, print the whole output of the LattE command. The following options are passed to the LattE command, for details you should consult `the LattE documentation <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__: - ``dual`` - (boolean) triangulate and signed-decompose in the dual space - ``irrational_primal`` - (boolean) triangulate in the dual space, signed-decompose in the primal space using irrationalization. - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose in the primal space using irrationalization. - ``maxdet`` -- (integer) decompose down to an index (determinant) of ``maxdet`` instead of index 1 (unimodular cones). - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones. - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2' - ``smith_form`` -- (string) either 'ilio' or 'lidia' - ``dualization`` -- (string) either 'cdd' or '4ti2' - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom' - ``triangulation_max_height`` - (integer) use a uniform distribution of height from 1 to this number .. NOTE:: Any additional argument is forwarded to LattE's executable ``count``. All occurrences of '_' will be replaced with a '-'. ALGORITHM: This method calls the program ``count`` from LattE integrale, a program for lattice point enumeration (see https://www.math.ucdavis.edu/~latte/). .. SEEALSO:: :mod:`~sage.interfaces.latte` the interface to LattE integrale EXAMPLES:: sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)]) sage: p = P.ehrhart_polynomial() # optional - latte_int sage: p # optional - latte_int 7/2*t^3 + 2*t^2 - 1/2*t + 1 sage: p(1) # optional - latte_int 6 sage: len(P.integral_points()) 6 sage: p(2) # optional - latte_int 36 sage: len((2*P).integral_points()) 36 The unit hypercubes:: sage: from itertools import product sage: def hypercube(d): ....: return Polyhedron(vertices=list(product([0,1],repeat=d))) sage: hypercube(3).ehrhart_polynomial() # optional - latte_int t^3 + 3*t^2 + 3*t + 1 sage: hypercube(4).ehrhart_polynomial() # optional - latte_int t^4 + 4*t^3 + 6*t^2 + 4*t + 1 sage: hypercube(5).ehrhart_polynomial() # optional - latte_int t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1 sage: hypercube(6).ehrhart_polynomial() # optional - latte_int t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1 An empty polyhedron:: sage: P = Polyhedron(ambient_dim=3, vertices=[]) sage: P.ehrhart_polynomial() # optional - latte_int 0 sage: parent(_) # optional - latte_int Univariate Polynomial Ring in t over Rational Field TESTS: Test options:: sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ) sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--maxdet=5' /dev/stdin ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 sage: p = P.ehrhart_polynomial(dual=True, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --dual /dev/stdin ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-primal /dev/stdin ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-all-primal /dev/stdin ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 Test bad options:: sage: P.ehrhart_polynomial(bim_bam_boum=19) # optional - latte_int Traceback (most recent call last): ... RuntimeError: LattE integrale program failed (exit code 1): ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--bim-bam-boum=19' /dev/stdin Unknown command/option --bim-bam-boum=19 """ if self.is_empty(): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ R = PolynomialRing(QQ, 't') return R.zero() # note: the options below are explicitely written in the function # declaration in order to keep tab completion (see #18211). kwds.update({ 'dual': dual, 'irrational_primal': irrational_primal, 'irrational_all_primal': irrational_all_primal, 'maxdet': maxdet, 'no_decomposition': no_decomposition, 'compute_vertex_cones': compute_vertex_cones, 'smith_form': smith_form, 'dualization': dualization, 'triangulation': triangulation, 'triangulation_max_height': triangulation_max_height }) from sage.interfaces.latte import count ine = self.cdd_Hrepresentation() return count(ine, cdd=True, ehrhart_polynomial=True, verbose=verbose, **kwds)
def demazure_character(self, w, f=None): r""" Returns the Demazure character associated to ``w``. INPUT: - ``w`` -- an element of the ambient weight lattice realization of the crystal, or a reduced word, or an element in the associated Weyl group OPTIONAL: - ``f`` -- a function from the crystal to a module This is currently only supported for crystals whose underlying weight space is the ambient space. The Demazure character is obtained by applying the Demazure operator `D_w` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ParentMethods.demazure_operator`) to the highest weight element of the classical crystal. The simple Demazure operators `D_i` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ElementMethods.demazure_operator_simple`) do not braid on the level of crystals, but on the level of characters they do. That is why it makes sense to input ``w`` either as a weight, a reduced word, or as an element of the underlying Weyl group. EXAMPLES:: sage: T = crystals.Tableaux(['A',2], shape = [2,1]) sage: e = T.weight_lattice_realization().basis() sage: weight = e[0] + 2*e[2] sage: weight.reduced_word() [2, 1] sage: T.demazure_character(weight) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x1*x3^2 sage: T = crystals.Tableaux(['A',3],shape=[2,1]) sage: T.demazure_character([1,2,3]) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: W = WeylGroup(['A',3]) sage: w = W.from_reduced_word([1,2,3]) sage: T.demazure_character(w) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: T = crystals.Tableaux(['B',2], shape = [2]) sage: e = T.weight_lattice_realization().basis() sage: weight = -2*e[1] sage: T.demazure_character(weight) x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1 sage: T = crystals.Tableaux("B2",shape=[1/2,1/2]) sage: b2=WeylCharacterRing("B2",base_ring=QQ).ambient() sage: T.demazure_character([1,2],f=lambda x:b2(x.weight())) b2(-1/2,1/2) + b2(1/2,-1/2) + b2(1/2,1/2) REFERENCES: .. [D1974] M. Demazure, Desingularisation des varietes de Schubert, Ann. E. N. S., Vol. 6, (1974), p. 163-172 .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms, Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313. :arXiv:`0707.4267` """ from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing if hasattr(w, 'reduced_word'): word = w.reduced_word() else: word = w n = self.weight_lattice_realization().n u = self.algebra(ZZ).sum_of_monomials(self.module_generators) u = self.demazure_operator(u, word) if f is None: x = ['x%s' % i for i in range(1, n + 1)] P = PolynomialRing(ZZ, x) # TODO: use P.linear_combination when PolynomialRing will be a ModulesWithBasis return sum((coeff * prod((x[i]**(c.weight()[i]) for i in range(n)), P.one()) for c, coeff in u), P.zero()) else: return sum((coeff * f(c)) for c, coeff in u)
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 markov_chain_transition_matrix(self, action='promotion', labeling='identity'): r""" Returns the transition matrix of the Markov chain for the action of generalized promotion or tau on ``self`` INPUT: - ``action`` -- 'promotion' or 'tau' (default: 'promotion') - ``labeling`` -- 'identity' or 'source' (default: 'identity') This method yields the transition matrix of the Markov chain defined by the action of the generalized promotion operator `\partial_i` (resp. `\tau_i`) on the set of linear extensions of a finite poset. Here the transition from the linear extension `\pi` to `\pi'`, where `\pi' = \pi \partial_i` (resp. `\pi'= \pi \tau_i`) is counted with weight `x_i` (resp. `x_{\pi_i}` if ``labeling`` is set to ``source``). EXAMPLES:: sage: P = Poset(([1,2,3,4], [[1,3],[1,4],[2,3]]), linear_extension = True) sage: L = P.linear_extensions() sage: L.markov_chain_transition_matrix() [-x0 - x1 - x2 x2 x0 + x1 0 0] [ x1 + x2 -x0 - x1 - x2 0 x0 0] [ 0 x1 -x0 - x1 0 x0] [ 0 x0 0 -x0 - x1 - x2 x1 + x2] [ x0 0 0 x1 + x2 -x0 - x1 - x2] sage: L.markov_chain_transition_matrix(labeling = 'source') [-x0 - x1 - x2 x3 x0 + x3 0 0] [ x1 + x2 -x0 - x1 - x3 0 x1 0] [ 0 x1 -x0 - x3 0 x1] [ 0 x0 0 -x0 - x1 - x2 x0 + x3] [ x0 0 0 x0 + x2 -x0 - x1 - x3] sage: L.markov_chain_transition_matrix(action = 'tau') [ -x0 - x2 x2 0 x0 0] [ x2 -x0 - x1 - x2 x1 0 x0] [ 0 x1 -x1 0 0] [ x0 0 0 -x0 - x2 x2] [ 0 x0 0 x2 -x0 - x2] sage: L.markov_chain_transition_matrix(action = 'tau', labeling = 'source') [ -x0 - x2 x3 0 x1 0] [ x2 -x0 - x1 - x3 x3 0 x1] [ 0 x1 -x3 0 0] [ x0 0 0 -x1 - x2 x3] [ 0 x0 0 x2 -x1 - x3] .. SEEALSO:: :meth:`markov_chain_digraph`, :meth:`promotion`, :meth:`tau` """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.matrix.constructor import matrix L = self.list() n = self.poset().cardinality() R = PolynomialRing(QQ, 'x', n) x = [R.gen(i) for i in range(n)] l = self.cardinality() M = dict([(i, j), 0] for i in range(l) for j in range(l)) if labeling == 'source': for i in range(l): perm = [self.poset().unwrap(k) for k in L[i]] for j in range(n - 1): p = getattr(L[i], action)(j + 1) M[(L.index(p), i)] += x[perm[j] - 1] else: for i in range(l): for j in range(n - 1): p = getattr(L[i], action)(j + 1) M[(L.index(p), i)] += x[j] for i in range(l): M[(i, i)] += -sum(M[(j, i)] for j in range(l)) return matrix(l, l, lambda x, y: M[(x, y)])
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 - 4)*x*z + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*y*z, x^2 + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*y^2 + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*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 ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, smith_form=None, dualization=None, triangulation=None, triangulation_max_height=None, **kwds): r""" Compute the Ehrhart quasipolynomial of this polyhedron with rational vertices. If the polyhedron is a lattice polytope, returns the Ehrhart polynomial, a univariate polynomial in ``variable`` over a rational field. If the polyhedron has rational, nonintegral vertices, returns a tuple of polynomials in ``variable`` over a rational field. The Ehrhart counting function of a polytope `P` with rational vertices is given by a *quasipolynomial*. That is, there exists a positive integer `l` and `l` polynomials `ehr_{P,i} \text{ for } i \in \{1,\dots,l \}` such that if `t` is equivalent to `i` mod `l` then `tP \cap \mathbb Z^d = ehr_{P,i}(t)`. INPUT: - ``variable`` -- string (default: 't'); The variable in which the Ehrhart polynomial should be expressed. - ``engine`` -- string; The backend to use. Allowed values are: * ``None`` (default); When no input is given the Ehrhart polynomial is computed using Normaliz (optional) * ``'latte'``; use LattE Integrale program (requires optional package 'latte_int') * ``'normaliz'``; use the Normaliz program (requires optional package 'pynormaliz'). The backend of ``self`` must be set to 'normaliz'. - When the ``engine`` is 'latte', the additional input values are: * ``verbose`` - boolean (default: ``False``); If ``True``, print the whole output of the LattE command. The following options are passed to the LattE command, for details consult `the LattE documentation <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__: * ``dual`` - boolean; triangulate and signed-decompose in the dual space * ``irrational_primal`` - boolean; triangulate in the dual space, signed-decompose in the primal space using irrationalization. * ``irrational_all_primal`` - boolean; triangulate and signed-decompose in the primal space using irrationalization. * ``maxdet`` -- integer; decompose down to an index (determinant) of ``maxdet`` instead of index 1 (unimodular cones). * ``no_decomposition`` -- boolean; do not signed-decompose simplicial cones. * ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2' * ``smith_form`` -- string; either 'ilio' or 'lidia' * ``dualization`` -- string; either 'cdd' or '4ti2' * ``triangulation`` - string; 'cddlib', '4ti2' or 'topcom' * ``triangulation_max_height`` - integer; use a uniform distribution of height from 1 to this number OUTPUT: A univariate polynomial over a rational field or a tuple of such polynomials. .. SEEALSO:: :mod:`~sage.interfaces.latte` the interface to LattE Integrale `PyNormaliz <https://pypi.org/project/PyNormaliz>`_ .. WARNING:: If the polytope has rational, non integral vertices, it must have ``backend='normaliz'``. EXAMPLES: As a first example, consider the line segment [0,1/2]. If we dilate this line segment by an even integral factor `k`, then the dilated line segment will contain `k/2 +1` lattice points. If `k` is odd then there will be `k/2+1/2` lattice points in the dilated line segment. Note that it is necessary to set the backend of the polytope to 'normaliz':: sage: line_seg = Polyhedron(vertices=[[0],[1/2]],backend='normaliz') # optional - pynormaliz sage: line_seg # optional - pynormaliz A 1-dimensional polyhedron in QQ^1 defined as the convex hull of 2 vertices sage: line_seg.ehrhart_quasipolynomial() # optional - pynormaliz (1/2*t + 1, 1/2*t + 1/2) For a more exciting example, let us look at the subpolytope of the 3 dimensional permutahedron fixed by the reflection across the hyperplane `x_1 = x_4`:: sage: verts = [[3/2, 3, 4, 3/2], ....: [3/2, 4, 3, 3/2], ....: [5/2, 1, 4, 5/2], ....: [5/2, 4, 1, 5/2], ....: [7/2, 1, 2, 7/2], ....: [7/2, 2, 1, 7/2]] sage: subpoly = Polyhedron(vertices=verts, backend='normaliz') # optional - pynormaliz sage: eq = subpoly.ehrhart_quasipolynomial() # optional - pynormaliz sage: eq # optional - pynormaliz (4*t^2 + 3*t + 1, 4*t^2 + 2*t) sage: eq = subpoly.ehrhart_quasipolynomial() # optional - pynormaliz sage: eq # optional - pynormaliz (4*t^2 + 3*t + 1, 4*t^2 + 2*t) sage: even_ep = eq[0] # optional - pynormaliz sage: odd_ep = eq[1] # optional - pynormaliz sage: even_ep(2) # optional - pynormaliz 23 sage: ts = 2*subpoly # optional - pynormaliz sage: ts.integral_points_count() # optional - pynormaliz latte_int 23 sage: odd_ep(1) # optional - pynormaliz 6 sage: subpoly.integral_points_count() # optional - pynormaliz latte_int 6 A polytope with rational nonintegral vertices must have ``backend='normaliz'``:: sage: line_seg = Polyhedron(vertices=[[0],[1/2]]) sage: line_seg.ehrhart_quasipolynomial() Traceback (most recent call last): ... TypeError: The backend of the polyhedron should be 'normaliz' The polyhedron should be compact:: sage: C = Polyhedron(backend='normaliz',rays=[[1/2,2],[2,1]]) # optional - pynormaliz sage: C.ehrhart_quasipolynomial() # optional - pynormaliz Traceback (most recent call last): ... ValueError: Ehrhart quasipolynomial only defined for compact polyhedra If the polytope happens to be a lattice polytope, the Ehrhart polynomial is returned:: sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)], backend='normaliz') # optional - pynormaliz sage: simplex = simplex.change_ring(QQ) # optional - pynormaliz sage: poly = simplex.ehrhart_quasipolynomial(engine='normaliz') # optional - pynormaliz sage: poly # optional - pynormaliz 7/2*t^3 + 2*t^2 - 1/2*t + 1 sage: simplex.ehrhart_polynomial() # optional - pynormaliz latte_int 7/2*t^3 + 2*t^2 - 1/2*t + 1 TESTS: The cache of the Ehrhart quasipolynomial is being pickled:: sage: P = polytopes.cuboctahedron(backend='normaliz')/2 # optional - pynormaliz sage: P.ehrhart_quasipolynomial() # optional - pynormaliz (5/6*t^3 + 2*t^2 + 5/3*t + 1, 5/6*t^3 + 1/2*t^2 + 1/6*t - 1/2) sage: Q = loads(dumps(P)) # optional - pynormaliz sage: Q.ehrhart_quasipolynomial.is_in_cache() # optional - pynormaliz True sage: P = polytopes.cuboctahedron().change_ring(QQ) # optional - latte_int sage: P.ehrhart_quasipolynomial(engine='latte') # optional - latte_int 20/3*t^3 + 8*t^2 + 10/3*t + 1 sage: Q = loads(dumps(P)) # optional - latte_int sage: Q.ehrhart_quasipolynomial.is_in_cache(engine='latte') # optional - latte_int True """ if self.is_empty(): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ R = PolynomialRing(QQ, 't') return R.zero() if not self.is_compact(): raise ValueError("Ehrhart quasipolynomial only defined for compact polyhedra") if engine is None: # setting the default to 'normaliz' engine = 'normaliz' if engine == 'normaliz': return self._ehrhart_quasipolynomial_normaliz(variable) if engine == 'latte': if any(not v.is_integral() for v in self.vertex_generator()): raise TypeError("the polytope has nonintegral vertices, the engine and backend of self should be 'normaliz'") poly = self._ehrhart_polynomial_latte(verbose, dual, irrational_primal, irrational_all_primal, maxdet, no_decomposition, compute_vertex_cones, smith_form, dualization, triangulation, triangulation_max_height, **kwds) return poly.change_variable_name(variable) # TO DO: replace this change of variable by creating the appropriate # polynomial ring in the latte interface. else: raise TypeError("the engine should be 'latte' or 'normaliz'")
def BCHCode(n, delta, F, b=0): r""" A 'Bose-Chaudhuri-Hockenghem code' (or BCH code for short) is the largest possible cyclic code of length n over field F=GF(q), whose generator polynomial has zeros (which contain the set) `Z = \{a^{b},a^{b+1}, ..., a^{b+delta-2}\}`, where a is a primitive `n^{th}` root of unity in the splitting field `GF(q^m)`, b is an integer `0\leq b\leq n-delta+1` and m is the multiplicative order of q modulo n. (The integers `b,...,b+delta-2` typically lie in the range `1,...,n-1`.) The integer `delta \geq 1` is called the "designed distance". The length n of the code and the size q of the base field must be relatively prime. The generator polynomial is equal to the least common multiple of the minimal polynomials of the elements of the set `Z` above. Special cases are b=1 (resulting codes are called 'narrow-sense' BCH codes), and `n=q^m-1` (known as 'primitive' BCH codes). It may happen that several values of delta give rise to the same BCH code. The largest one is called the Bose distance of the code. The true minimum distance, d, of the code is greater than or equal to the Bose distance, so `d\geq delta`. EXAMPLES:: sage: FF.<a> = GF(3^2,"a") sage: x = PolynomialRing(FF,"x").gen() sage: L = [b.minpoly() for b in [a,a^2,a^3]]; g = LCM(L) sage: f = x^(8)-1 sage: g.divides(f) True sage: C = codes.CyclicCode(8,g); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3),1); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3)); C Linear code of length 8, dimension 5 over Finite Field of size 3 sage: C.minimum_distance() 3 sage: C = codes.BCHCode(26, 5, GF(5), b=1); C Linear code of length 26, dimension 10 over Finite Field of size 5 """ q = F.order() R = IntegerModRing(n) m = R(q).multiplicative_order() FF = GF(q**m, "z") z = FF.gen() e = z.multiplicative_order() / n a = z**e # order n P = PolynomialRing(F, "x") x = P.gen() L1 = [] for coset in R.cyclotomic_cosets(q, range(b, b + delta - 1)): L1.extend(P((a**j).minpoly()) for j in coset) g = P(LCM(L1)) #print cosets, "\n", g, "\n", (x**n-1).factor(), "\n", L1, "\n", g.divides(x**n-1) if not (g.divides(x**n - 1)): raise ValueError("BCH codes does not exist with the given input.") return CyclicCodeFromGeneratingPolynomial(n, g)
def ehrhart_polynomial(self, engine=None, variable='t', verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, smith_form=None, dualization=None, triangulation=None, triangulation_max_height=None, **kwds): r""" Return the Ehrhart polynomial of this polyhedron. The polyhedron must be a lattice polytope. Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP\cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a rational polynomial of degree `d` for integer `t`. `L` is called the *Ehrhart polynomial* of `P`. For more information see the :wikipedia:`Ehrhart_polynomial`. The Ehrhart polynomial may be computed using either LattE Integrale or Normaliz by setting ``engine`` to 'latte' or 'normaliz' respectively. INPUT: - ``engine`` -- string; The backend to use. Allowed values are: * ``None`` (default); When no input is given the Ehrhart polynomial is computed using LattE Integrale (optional) * ``'latte'``; use LattE integrale program (optional) * ``'normaliz'``; use Normaliz program (optional package pynormaliz). The backend of ``self`` must be set to 'normaliz'. - ``variable`` -- string (default: 't'); The variable in which the Ehrhart polynomial should be expressed. - When the ``engine`` is 'latte', the additional input values are: * ``verbose`` - boolean (default: ``False``); If ``True``, print the whole output of the LattE command. The following options are passed to the LattE command, for details consult `the LattE documentation <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__: * ``dual`` - boolean; triangulate and signed-decompose in the dual space * ``irrational_primal`` - boolean; triangulate in the dual space, signed-decompose in the primal space using irrationalization. * ``irrational_all_primal`` - boolean; triangulate and signed-decompose in the primal space using irrationalization. * ``maxdet`` -- integer; decompose down to an index (determinant) of ``maxdet`` instead of index 1 (unimodular cones). * ``no_decomposition`` -- boolean; do not signed-decompose simplicial cones. * ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2' * ``smith_form`` -- string; either 'ilio' or 'lidia' * ``dualization`` -- string; either 'cdd' or '4ti2' * ``triangulation`` - string; 'cddlib', '4ti2' or 'topcom' * ``triangulation_max_height`` - integer; use a uniform distribution of height from 1 to this number OUTPUT: A univariate polynomial in ``variable`` over a rational field. .. SEEALSO:: :mod:`~sage.interfaces.latte` the interface to LattE Integrale `PyNormaliz <https://pypi.org/project/PyNormaliz>`_ EXAMPLES: To start, we find the Ehrhart polynomial of a three-dimensional ``simplex``, first using ``engine='latte'``. Leaving the engine unspecified sets the ``engine`` to 'latte' by default:: sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)]) sage: simplex = simplex.change_ring(QQ) sage: poly = simplex.ehrhart_polynomial(engine='latte') # optional - latte_int sage: poly # optional - latte_int 7/2*t^3 + 2*t^2 - 1/2*t + 1 sage: poly(1) # optional - latte_int 6 sage: len(simplex.integral_points()) # optional - latte_int 6 sage: poly(2) # optional - latte_int 36 sage: len((2*simplex).integral_points()) # optional - latte_int 36 Now we find the same Ehrhart polynomial, this time using ``engine='normaliz'``. To use the Normaliz engine, the ``simplex`` must be defined with ``backend='normaliz'``:: sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)], backend='normaliz') # optional - pynormaliz sage: simplex = simplex.change_ring(QQ) # optional - pynormaliz sage: poly = simplex.ehrhart_polynomial(engine = 'normaliz') # optional - pynormaliz sage: poly # optional - pynormaliz 7/2*t^3 + 2*t^2 - 1/2*t + 1 If the ``engine='normaliz'``, the backend should be ``'normaliz'``, otherwise it returns an error:: sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)]) sage: simplex = simplex.change_ring(QQ) sage: simplex.ehrhart_polynomial(engine='normaliz') # optional - pynormaliz Traceback (most recent call last): ... TypeError: The backend of the polyhedron should be 'normaliz' The polyhedron should be compact:: sage: C = Polyhedron(backend='normaliz',rays=[[1,2],[2,1]]) # optional - pynormaliz sage: C = C.change_ring(QQ) # optional - pynormaliz sage: C.ehrhart_polynomial() # optional - pynormaliz Traceback (most recent call last): ... ValueError: Ehrhart polynomial only defined for compact polyhedra The polyhedron should have integral vertices:: sage: L = Polyhedron(vertices = [[0],[1/2]]) sage: L.ehrhart_polynomial() Traceback (most recent call last): ... TypeError: the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz' TESTS: The cache of the Ehrhart polynomial is being pickled:: sage: P = polytopes.cube().change_ring(QQ) # optional - latte_int sage: P.ehrhart_polynomial() # optional - latte_int 8*t^3 + 12*t^2 + 6*t + 1 sage: Q = loads(dumps(P)) # optional - latte_int sage: Q.ehrhart_polynomial.is_in_cache() # optional - latte_int True """ # check if ``self`` is compact and has vertices in ZZ if self.is_empty(): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ R = PolynomialRing(QQ, variable) return R.zero() if not self.is_compact(): raise ValueError("Ehrhart polynomial only defined for compact polyhedra") if any(not v.is_integral() for v in self.vertex_generator()): raise TypeError("the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz'") # Passes to specific latte or normaliz subfunction depending on engine if engine is None: # set default engine to latte engine = 'latte' if engine == 'latte': poly = self._ehrhart_polynomial_latte(verbose, dual, irrational_primal, irrational_all_primal, maxdet, no_decomposition, compute_vertex_cones, smith_form, dualization, triangulation, triangulation_max_height, **kwds) return poly.change_variable_name(variable) # TO DO: replace this change of variable by creating the appropriate # polynomial ring in the latte interface. elif engine == 'normaliz': return self._ehrhart_polynomial_normaliz(variable) else: raise ValueError("engine must be 'latte' or 'normaliz'")
def julia_plot(f=None, **kwds): r""" Plots the Julia set of a given polynomial ``f``. Users can specify whether they would like to display the Mandelbrot side by side with the Julia set with the ``mandelbrot`` argument. If ``f`` is not specified, this method defaults to `f(z) = z^2-1`. The Julia set of a polynomial ``f`` is the set of complex numbers `z` for which the function `f(z)` is bounded under iteration. The Julia set can be visualized by plotting each point in the set in the complex plane. Julia sets are examples of fractals when plotted in the complex plane. ALGORITHM: Let `R_c = \bigl(1 + \sqrt{1 + 4|c|}\bigr)/2` if the polynomial is of the form `f(z) = z^2 + c`; otherwise, let `R_c = 2`. For every `p \in \mathbb{C}`, if `|f^{k}(p)| > R_c` for some `k \geq 0`, then `f^{n}(p) \to \infty`. Let `N` be the maximum number of iterations. Compute the first `N` points on the orbit of `p` under `f`. If for any `k < N`, `|f^{k}(p)| > R_c`, we stop the iteration and assign a color to the point `p` based on how quickly `p` escaped to infinity under iteration of `f`. If `|f^{i}(p)| \leq R_c` for all `i \leq N`, we assume `p` is in the Julia set and assign the point `p` the color black. INPUT: - ``f`` -- input polynomial (optional - default: ``z^2 - 1``). - ``period`` -- list (optional - default: ``None``), returns the Julia set for a random `c` value with the given (formal) cycle structure. - ``mandelbrot`` -- boolean (optional - default: ``True``), when set to ``True``, an image of the Mandelbrot set is appended to the right of the Julia set. - ``point_color`` -- RGB color (optional - default: ``'tomato'``), color of the point `c` in the Mandelbrot set (any valid input for Color). - ``x_center`` -- double (optional - default: ``-1.0``), Real part of center point. - ``y_center`` -- double (optional - default: ``0.0``), Imaginary part of center point. - ``image_width`` -- double (optional - default: ``4.0``), width of image in the complex plane. - ``max_iteration`` -- long (optional - default: ``500``), maximum number of iterations the map `f(z)`. - ``pixel_count`` -- long (optional - default: ``500``), side length of image in number of pixels. - ``base_color`` -- hex color (optional - default: ``'steelblue'``), color used to determine the coloring of set (any valid input for Color). - ``level_sep`` -- long (optional - default: 1), number of iterations between each color level. - ``number_of_colors`` -- long (optional - default: 30), number of colors used to plot image. - ``interact`` -- boolean (optional - default: ``False``), controls whether plot will have interactive functionality. OUTPUT: 24-bit RGB image of the Julia set in the complex plane. .. TODO:: Implement the side-by-side Mandelbrot-Julia plots for general one-parameter families of polynomials. EXAMPLES: The default ``f`` is `z^2 - 1`:: sage: julia_plot() 1001x500px 24-bit RGB image To display only the Julia set, set ``mandelbrot`` to ``False``:: sage: julia_plot(mandelbrot=False) 500x500px 24-bit RGB image :: sage: R.<z> = CC[] sage: f = z^3 - z + 1 sage: julia_plot(f) 500x500px 24-bit RGB image To display an interactive plot of the Julia set in the Notebook, set ``interact`` to ``True``. (This is only implemented for polynomials of the form ``f = z^2 + c``):: sage: julia_plot(interact=True) interactive(children=(FloatSlider(value=-1.0, description=u'Real c'... :: sage: R.<z> = CC[] sage: f = z^2 + 1/2 sage: julia_plot(f,interact=True) interactive(children=(FloatSlider(value=0.5, description=u'Real c'... To return the Julia set of a random `c` value with (formal) cycle structure `(2,3)`, set ``period = [2,3]``:: sage: julia_plot(period=[2,3]) 1001x500px 24-bit RGB image To return all of the Julia sets of `c` values with (formal) cycle structure `(2,3)`:: sage: period = [2,3] # not tested ....: R.<c> = QQ[] ....: P.<x,y> = ProjectiveSpace(R,1) ....: f = DynamicalSystem([x^2+c*y^2, y^2]) ....: L = f.dynatomic_polynomial(period).subs({x:0,y:1}).roots(ring=CC) ....: c_values = [k[0] for k in L] ....: for c in c_values: ....: julia_plot(c) Polynomial maps can be defined over a polynomial ring or a fraction field, so long as ``f`` is polynomial:: sage: R.<z> = CC[] sage: f = z^2 - 1 sage: julia_plot(f) 1001x500px 24-bit RGB image :: sage: R.<z> = CC[] sage: K = R.fraction_field(); z = K.gen() sage: f = z^2-1 sage: julia_plot(f) 1001x500px 24-bit RGB image Interact functionality is not implemented if the polynomial is not of the form `f = z^2 + c`:: sage: R.<z> = CC[] sage: f = z^3 + 1 sage: julia_plot(f, interact=True) Traceback (most recent call last): ... NotImplementedError: The interactive plot is only implemented for ... """ # extract keyword arguments period = kwds.pop("period", None) mandelbrot = kwds.pop("mandelbrot", True) point_color = kwds.pop("point_color", 'tomato') x_center = kwds.pop("x_center", 0.0) y_center = kwds.pop("y_center", 0.0) image_width = kwds.pop("image_width", 4.0) max_iteration = kwds.pop("max_iteration", 500) pixel_count = kwds.pop("pixel_count", 500) base_color = kwds.pop("base_color", 'steelblue') level_sep = kwds.pop("level_sep", 1) number_of_colors = kwds.pop("number_of_colors", 30) interacts = kwds.pop("interact", False) f_is_default_after_all = None if period: # pick a random c with the specified period R = PolynomialRing(CC, 'c') c = R.gen() x, y = ProjectiveSpace(R, 1, 'x,y').gens() F = DynamicalSystem([x**2 + c * y**2, y**2]) L = F.dynatomic_polynomial(period).subs({x: 0, y: 1}).roots(ring=CC) c = L[randint(0, len(L) - 1)][0] base_color = Color(base_color) point_color = Color(point_color) EPS = 0.00001 if f is not None and period is None: # f user-specified and no period given # try to coerce f to live in a polynomial ring S = PolynomialRing(CC, names='z') z = S.gen() try: f_poly = S(f) except TypeError: R = f.parent() if not (R.is_integral_domain() and (CC.is_subring(R) or CDF.is_subring(R))): raise ValueError('Given `f` must be a complex polynomial.') else: raise NotImplementedError( 'Julia sets not implemented for rational functions.') if (f_poly - z * z) in CC: # f is specified and of the form z^2 + c. f_is_default_after_all = True c = f_poly - z * z else: # f is specified and not of the form z^2 + c if interacts: raise NotImplementedError( "The interactive plot is only implemented for " "polynomials of the form f = z^2 + c.") else: return general_julia(f_poly, x_center, y_center, image_width, max_iteration, pixel_count, level_sep, number_of_colors, base_color) # otherwise we can use fast_julia_plot for z^2 + c if f_is_default_after_all or f is None or period is not None: # specify default c = -1 value if f and period were not specified if not f_is_default_after_all and period is None: c = -1 c = CC(c) c_real = c.real() c_imag = c.imag() if interacts: # set widgets from ipywidgets.widgets import FloatSlider, IntSlider, \ ColorPicker, interact widgets = dict( c_real=FloatSlider(min=-2.0, max=2.0, step=EPS, value=c_real, description="Real c"), c_imag=FloatSlider(min=-2.0, max=2.0, step=EPS, value=c_imag, description="Imag c"), x_center=FloatSlider(min=-1.0, max=1.0, step=EPS, value=x_center, description="Real center"), y_center=FloatSlider(min=-1.0, max=1.0, step=EPS, value=y_center, description="Imag center"), image_width=FloatSlider(min=EPS, max=4.0, step=EPS, value=image_width, description="Width"), max_iteration=IntSlider(min=0, max=1000, value=max_iteration, description="Iterations"), pixel_count=IntSlider(min=10, max=1000, value=pixel_count, description="Pixels"), level_sep=IntSlider(min=1, max=20, value=level_sep, description="Color sep"), color_num=IntSlider(min=1, max=100, value=number_of_colors, description="# Colors"), base_color=ColorPicker(value=base_color.html_color(), description="Base color"), ) if mandelbrot: widgets["point_color"] = ColorPicker( value=point_color.html_color(), description="Point color") return interact(**widgets).widget(julia_helper) else: return interact(**widgets).widget(fast_julia_plot) elif mandelbrot: # non-interactive with mandelbrot return julia_helper(c_real, c_imag, x_center, y_center, image_width, max_iteration, pixel_count, level_sep, number_of_colors, base_color, point_color) else: # non-interactive without mandelbrot return fast_julia_plot(c_real, c_imag, x_center, y_center, image_width, max_iteration, pixel_count, level_sep, number_of_colors, base_color)
def parabolic_kazhdan_lusztig_polynomial(self, u, v, J, constant_term_one=True): """ Return the parabolic Kazhdan-Lusztig polynomial `P_{u,v}^{-,J}`. INPUT: - ``u``, ``v`` -- minimal length coset representatives of `W/W_J` for this Coxeter group `W` - ``J`` -- a subset of the index set of ``self`` specifying the parabolic subgroup This method implements the parabolic Kazhdan-Lusztig polynomials `P^{-,J}_{u,v}` of [Deo1987b]_, which are defined as `P^{-,J}_{u,v} = \sum_{z\in W_J} (-1)^{\ell(z)} P_{yz,w}(q)` with the conventions in Sage. As for :meth:`kazhdan_lusztig_polynomial` the convention differs from Theorem 2.7 in [LT1998]_ by: .. MATH:: {}^{LT} P_{y,w}^{-,J}(q) = q^{\ell(w)-\ell(y)} P_{y,w}^{-,J}(q^{-2}) EXAMPLES:: sage: W = CoxeterGroup(['A',3], implementation='coxeter3') # optional - coxeter3 sage: W.parabolic_kazhdan_lusztig_polynomial([],[3,2],[1,3]) # optional - coxeter3 0 sage: W.parabolic_kazhdan_lusztig_polynomial([2],[2,1,3,2],[1,3]) # optional - coxeter3 q sage: C = CoxeterGroup(['A',3,1], implementation='coxeter3') # optional - coxeter3 sage: C.parabolic_kazhdan_lusztig_polynomial([],[1],[0]) # optional - coxeter3 1 sage: C.parabolic_kazhdan_lusztig_polynomial([],[1,2,1],[0]) # optional - coxeter3 1 sage: C.parabolic_kazhdan_lusztig_polynomial([],[0,1,0,1,2,1],[0]) # optional - coxeter3 q sage: w=[1, 2, 1, 3, 0, 2, 1, 0, 3, 0, 2] sage: v=[1, 2, 1, 3, 0, 1, 2, 1, 0, 3, 0, 2, 1, 0, 3, 0, 2] sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3]) # optional - coxeter3 q^2 + q sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3],constant_term_one=False) # optional - coxeter3 q^4 + q^2 TESTS:: sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 sage: type(W.parabolic_kazhdan_lusztig_polynomial([2],[],[1])) # optional - coxeter3 <type 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'> """ u = self(u) v = self(v) if any(d in J for d in u.descents()) or any(d in J for d in v.descents()): raise ValueError("u and v have to be minimal coset representatives") J_set = set(J) WOI = self.weak_order_ideal(lambda x: J_set.issuperset(x.descents())) if constant_term_one: P = PolynomialRing(ZZ, 'q') return P.sum((-1)**(z.length()) * self.kazhdan_lusztig_polynomial(u*z,v) for z in WOI if (u*z).bruhat_le(v)) P = PolynomialRing(ZZ, 'q', sparse=True) return P.sum((-1)**(z.length()) * self.kazhdan_lusztig_polynomial(u*z,v, constant_term_one=False).shift(z.length()) for z in WOI if (u*z).bruhat_le(v))
def LaurentPolynomialRing(base_ring, *args, **kwds): r""" Return the globally unique univariate or multivariate Laurent polynomial ring with given properties and variable name or names. There are four ways to call the Laurent polynomial ring constructor: 1. ``LaurentPolynomialRing(base_ring, name, sparse=False)`` 2. ``LaurentPolynomialRing(base_ring, names, order='degrevlex')`` 3. ``LaurentPolynomialRing(base_ring, name, n, order='degrevlex')`` 4. ``LaurentPolynomialRing(base_ring, n, name, order='degrevlex')`` The optional arguments sparse and order *must* be explicitly named, and the other arguments must be given positionally. INPUT: - ``base_ring`` -- a commutative ring - ``name`` -- a string - ``names`` -- a list or tuple of names, or a comma separated string - ``n`` -- a positive integer - ``sparse`` -- bool (default: False), whether or not elements are sparse - ``order`` -- string or :class:`~sage.rings.polynomial.term_order.TermOrder`, e.g., - ``'degrevlex'`` (default) -- degree reverse lexicographic - ``'lex'`` -- lexicographic - ``'deglex'`` -- degree lexicographic - ``TermOrder('deglex',3) + TermOrder('deglex',3)`` -- block ordering OUTPUT: ``LaurentPolynomialRing(base_ring, name, sparse=False)`` returns a univariate Laurent polynomial ring; all other input formats return a multivariate Laurent polynomial ring. UNIQUENESS and IMMUTABILITY: In Sage there is exactly one single-variate Laurent polynomial ring over each base ring in each choice of variable and sparseness. There is also exactly one multivariate Laurent polynomial ring over each base ring for each choice of names of variables and term order. :: sage: R.<x,y> = LaurentPolynomialRing(QQ,2); R Multivariate Laurent Polynomial Ring in x, y over Rational Field sage: f = x^2 - 2*y^-2 You can't just globally change the names of those variables. This is because objects all over Sage could have pointers to that polynomial ring. :: sage: R._assign_names(['z','w']) Traceback (most recent call last): ... ValueError: variable names cannot be changed after object creation. EXAMPLES: 1. ``LaurentPolynomialRing(base_ring, name, sparse=False)`` :: sage: LaurentPolynomialRing(QQ, 'w') Univariate Laurent Polynomial Ring in w over Rational Field Use the diamond brackets notation to make the variable ready for use after you define the ring:: sage: R.<w> = LaurentPolynomialRing(QQ) sage: (1 + w)^3 1 + 3*w + 3*w^2 + w^3 You must specify a name:: sage: LaurentPolynomialRing(QQ) Traceback (most recent call last): ... TypeError: you must specify the names of the variables sage: R.<abc> = LaurentPolynomialRing(QQ, sparse=True); R Univariate Laurent Polynomial Ring in abc over Rational Field sage: R.<w> = LaurentPolynomialRing(PolynomialRing(GF(7),'k')); R Univariate Laurent Polynomial Ring in w over Univariate Polynomial Ring in k over Finite Field of size 7 Rings with different variables are different:: sage: LaurentPolynomialRing(QQ, 'x') == LaurentPolynomialRing(QQ, 'y') False 2. ``LaurentPolynomialRing(base_ring, names, order='degrevlex')`` :: sage: R = LaurentPolynomialRing(QQ, 'a,b,c'); R Multivariate Laurent Polynomial Ring in a, b, c over Rational Field sage: S = LaurentPolynomialRing(QQ, ['a','b','c']); S Multivariate Laurent Polynomial Ring in a, b, c over Rational Field sage: T = LaurentPolynomialRing(QQ, ('a','b','c')); T Multivariate Laurent Polynomial Ring in a, b, c over Rational Field All three rings are identical. :: sage: (R is S) and (S is T) True There is a unique Laurent polynomial ring with each term order:: sage: R = LaurentPolynomialRing(QQ, 'x,y,z', order='degrevlex'); R Multivariate Laurent Polynomial Ring in x, y, z over Rational Field sage: S = LaurentPolynomialRing(QQ, 'x,y,z', order='invlex'); S Multivariate Laurent Polynomial Ring in x, y, z over Rational Field sage: S is LaurentPolynomialRing(QQ, 'x,y,z', order='invlex') True sage: R == S False 3. ``LaurentPolynomialRing(base_ring, name, n, order='degrevlex')`` If you specify a single name as a string and a number of variables, then variables labeled with numbers are created. :: sage: LaurentPolynomialRing(QQ, 'x', 10) Multivariate Laurent Polynomial Ring in x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 over Rational Field sage: LaurentPolynomialRing(GF(7), 'y', 5) Multivariate Laurent Polynomial Ring in y0, y1, y2, y3, y4 over Finite Field of size 7 sage: LaurentPolynomialRing(QQ, 'y', 3, sparse=True) Multivariate Laurent Polynomial Ring in y0, y1, y2 over Rational Field By calling the :meth:`~sage.structure.category_object.CategoryObject.inject_variables` method, all those variable names are available for interactive use:: sage: R = LaurentPolynomialRing(GF(7),15,'w'); R Multivariate Laurent Polynomial Ring in w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14 over Finite Field of size 7 sage: R.inject_variables() Defining w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14 sage: (w0 + 2*w8 + w13)^2 w0^2 + 4*w0*w8 + 4*w8^2 + 2*w0*w13 + 4*w8*w13 + w13^2 """ from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing R = PolynomialRing(base_ring, *args, **kwds) if R in _cache: return _cache[R] # put () here to re-enable weakrefs if is_PolynomialRing(R): # univariate case P = LaurentPolynomialRing_univariate(R) else: assert is_MPolynomialRing(R) P = LaurentPolynomialRing_mpair(R) _cache[R] = P return P
def invariant_generators(self): r""" Return invariant ring generators. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where `G` in `GL(n,F)` is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of `G`-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of `G`-invariant polynomials. ALGORITHM: Wraps Singular's ``invariant_algebra_reynolds`` and ``invariant_ring`` in ``finvar.lib``. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = CyclotomicField(8) sage: z = F.gen() sage: a = z+1/z sage: b = z^2 sage: MS = MatrixSpace(F,2,2) sage: g1 = MS([[1/a, 1/a], [1/a, -1/a]]) sage: g2 = MS([[-b, 0], [0, b]]) sage: G=MatrixGroup([g1,g2]) sage: G.invariant_generators() [x1^4 + 2*x1^2*x2^2 + x2^4, x1^5*x2 - x1*x2^5, x1^8 + 28/9*x1^6*x2^2 + 70/9*x1^4*x2^4 + 28/9*x1^2*x2^6 + x2^8] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - [Stu1993]_ - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", :arxiv:`math/0703035`. """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() # len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() # test if the field is admissible if F.gen() == 1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F, 'polynomial'): # we got an algebraic extension if len(F.gens()) > 1: raise NotImplementedError( "can only deal with finite fields and (simple algebraic extensions of) the rationals" ) FieldStr = '(%d,%s)' % (F.characteristic(), str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)' % (F.characteristic(), ','.join( str(p) for p in F.gens())) # Setting Singular's variable names # We need to make sure that field generator and variables get different names. if str(F.gen())[0] == 'x': VarStr = 'y' else: VarStr = 'x' VarNames = '(' + ','.join( (VarStr + str(i) for i in range(1, n + 1))) + ')' # The function call and affectation below have side-effects. Do not remove! # (even if pyflakes say so) R = singular.ring(FieldStr, VarNames, 'dp') if hasattr(F, 'polynomial') and F.gen() != 1: # we have to define minpoly singular.eval('minpoly = ' + str(F.polynomial()).replace('x', str(F.gen()))) A = [singular.matrix(n, n, str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F, n, [VarStr + str(i) for i in range(1, n + 1)]) if q == 0 or (q > 0 and self.cardinality() % q): from sage.all import Matrix try: elements = [g.matrix() for g in self.list()] except (TypeError, ValueError): elements if elements is not None: ReyName = 't' + singular._next_var_name() singular.eval('matrix %s[%d][%d]' % (ReyName, self.cardinality(), n)) for i in range(1, self.cardinality() + 1): M = Matrix(F, elements[i - 1]) D = [{} for foobar in range(self.degree())] for x, y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)' % (ReyName, i, row + 1, ReyName, i, row + 1, repr(t[1]), t[0] + 1)) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)' % (IRName, ReyName)) else: ReyName = 't' + singular._next_var_name() singular.eval('list %s=group_reynolds((%s))' % (ReyName, Lgens)) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])' % (IRName, ReyName)) OUT = [ singular.eval(IRName + '[1,%d]' % (j)) for j in range(1, 1 + int(singular('ncols(' + IRName + ')'))) ] return [PR(gen) for gen in OUT] if self.cardinality() % q == 0: PName = 't' + singular._next_var_name() SName = 't' + singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)' % (PName, SName, Lgens)) OUT = [ singular.eval(PName + '[1,%d]' % (j)) for j in range(1, 1 + singular('ncols(' + PName + ')')) ] OUT += [ singular.eval(SName + '[1,%d]' % (j)) for j in range(2, 1 + singular('ncols(' + SName + ')')) ] return [PR(gen) for gen in OUT]
def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algorithm=None): r""" Return a list of pairs ``(root,multiplicity)`` of roots of the polynomial ``p``. If the argument ``multiplicities`` is set to ``False`` then return the list of roots. .. SEEALSO:: :meth:`_factor_univariate_polynomial` EXAMPLES:: sage: R.<x> = PolynomialRing(GF(5),'x') sage: K = GF(5).algebraic_closure('t') sage: sorted((x^6 - 1).roots(K,multiplicities=False)) [1, 4, 2*t2 + 1, 2*t2 + 2, 3*t2 + 3, 3*t2 + 4] sage: ((K.gen(2)*x - K.gen(3))**2).roots(K) [(3*t6^5 + 2*t6^4 + 2*t6^2 + 3, 2)] sage: for _ in range(10): ....: p = R.random_element(degree=randint(2,8)) ....: for r in p.roots(K, multiplicities=False): ....: assert p(r).is_zero(), "r={} is not a root of p={}".format(r,p) """ from sage.arith.all import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field coeffs = [v.as_finite_field_element(minimal=True) for v in p.list()] l = lcm([c[0].degree() for c in coeffs]) F, phi = self.subfield(l) P = p.parent().change_ring(F) new_coeffs = [self.inclusion(c[0].degree(), l)(c[1]) for c in coeffs] polys = [(g, m, l, phi) for g, m in P(new_coeffs).factor()] roots = [] # a list of pair (root,multiplicity) while polys: g, m, l, phi = polys.pop() if g.degree() == 1: # found a root r = phi(-g.constant_coefficient()) roots.append((r, m)) else: # look at the extension of degree g.degree() which contains at # least one root of g ll = l * g.degree() psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) # note: there is no coercion from the l-th subfield to the ll-th # subfield. The line below does the conversion manually. g = PolynomialRing(FF, 'x')([psi(_) for _ in g]) polys.extend((gg, m, ll, pphi) for gg, _ in g.factor()) if multiplicities: return roots else: return [r[0] for r in roots]
def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): r""" Compute the Molien series of this finite group with respect to the character ``chi``. It can be returned either as a rational function in one variable or a power series in one variable. The base field must be a finite field, the rationals, or a cyclotomic field. Note that the base field characteristic cannot divide the group order (i.e., the non-modular case). ALGORITHM: For a finite group `G` in characteristic zero we construct the Molien series as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\text{det}(I-tg)}, where `I` is the identity matrix and `t` an indeterminate. For characteristic `p` not dividing the order of `G`, let `k` be the base field and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k` and `\omega` as a primitive `N`-th root of unity over `\QQ`. For each `g \in G` define `k_i(g)` to be the positive integer such that `e_i = \lambda^{k_i(g)}` for each eigenvalue `e_i` of `g`. Then the Molien series is computed as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\prod_{i=1}^n(1 - t\omega^{k_i(g)})}, where `t` is an indeterminant. [Dec1998]_ INPUT: - ``chi`` -- (default: trivial character) a linear group character of this group - ``return_series`` -- boolean (default: ``True``) if ``True``, then returns the Molien series as a power series, ``False`` as a rational function - ``prec`` -- integer (default: 20); power series default precision - ``variable`` -- string (default: ``'t'``); Variable name for the Molien series OUTPUT: single variable rational function or power series with integer coefficients EXAMPLES:: sage: MatrixGroup(matrix(QQ,2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: only implemented for finite groups sage: MatrixGroup(matrix(GF(3),2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: characteristic cannot divide group order Tetrahedral Group:: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: Tetra.molien_series(prec=30) 1 + t^8 + 2*t^12 + t^16 + 2*t^20 + 3*t^24 + 2*t^28 + O(t^30) sage: mol = Tetra.molien_series(return_series=False); mol (t^8 - t^4 + 1)/(t^16 - t^12 - t^4 + 1) sage: mol.parent() Fraction Field of Univariate Polynomial Ring in t over Integer Ring sage: chi = Tetra.character(Tetra.character_table()[1]) sage: Tetra.molien_series(chi, prec=30, variable='u') u^6 + u^14 + 2*u^18 + u^22 + 2*u^26 + 3*u^30 + 2*u^34 + O(u^36) sage: chi = Tetra.character(Tetra.character_table()[2]) sage: Tetra.molien_series(chi) t^10 + t^14 + t^18 + 2*t^22 + 2*t^26 + O(t^30) :: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: mol = S3.molien_series(prec=10); mol 1 + t + 2*t^2 + 3*t^3 + 4*t^4 + 5*t^5 + 7*t^6 + 8*t^7 + 10*t^8 + 12*t^9 + O(t^10) sage: mol.parent() Power Series Ring in t over Integer Ring Octahedral Group:: sage: K.<v> = CyclotomicField(8) sage: a = v-v^3 #sqrt(2) sage: i = v^2 sage: Octa = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [(1+i)/a,0, 0,(1-i)/a]) sage: Octa.molien_series(prec=30) 1 + t^8 + t^12 + t^16 + t^18 + t^20 + 2*t^24 + t^26 + t^28 + O(t^30) Icosahedral Group:: sage: K.<v> = CyclotomicField(10) sage: z5 = v^2 sage: i = z5^5 sage: a = 2*z5^3 + 2*z5^2 + 1 #sqrt(5) sage: Ico = MatrixGroup([[z5^3,0, 0,z5^2], [0,1, -1,0], [(z5^4-z5)/a, (z5^2-z5^3)/a, (z5^2-z5^3)/a, -(z5^4-z5)/a]]) sage: Ico.molien_series(prec=40) 1 + t^12 + t^20 + t^24 + t^30 + t^32 + t^36 + O(t^40) :: sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: G.molien_series(chi, prec=10) t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 9*t^6 + 12*t^7 + 15*t^8 + 18*t^9 + 22*t^10 + O(t^11) :: sage: K = GF(5) sage: S = MatrixGroup(SymmetricGroup(4)) sage: G = MatrixGroup([matrix(K,4,4,[K(y) for u in m.list() for y in u])for m in S.gens()]) sage: G.molien_series(return_series=False) 1/(t^10 - t^9 - t^8 + 2*t^5 - t^2 - t + 1) :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: G.molien_series(chi) 3*t^5 + 6*t^11 + 9*t^17 + 12*t^23 + O(t^25) """ if not self.is_finite(): raise NotImplementedError("only implemented for finite groups") if chi is None: chi = self.trivial_character() M = self.matrix_space() R = FractionField(self.base_ring()) N = self.order() if R.characteristic() == 0: P = PolynomialRing(R, variable) t = P.gen() # it is possible the character is over a larger cyclotomic field K = chi.values()[0].parent() if K.degree() != 1: if R.degree() != 1: L = K.composite_fields(R)[0] else: L = K else: L = R mol = P(0) for g in self: mol += L(chi(g)) / (M.identity_matrix() - t * g.matrix()).det().change_ring(L) elif R.characteristic().divides(N): raise NotImplementedError( "characteristic cannot divide group order") else: # char p>0 # find primitive Nth roots of unity over base ring and QQ F = cyclotomic_polynomial(N).change_ring(R) w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0] # don't need to extend further in this case since the order of # the roots of unity in the character divide the order of the group L = CyclotomicField(N, 'v') v = L.gen() # construct Molien series P = PolynomialRing(L, variable) t = P.gen() mol = P(0) for g in self: # construct Phi phi = L(chi(g)) for e in g.matrix().eigenvalues(): # find power such that w**n = e n = 1 while w**n != e and n < N + 1: n += 1 # raise v to that power phi *= (1 - t * v**n) mol += P(1) / phi # We know the coefficients will be integers #mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring(ZZ) # divide by group order mol /= N if return_series: PS = PowerSeriesRing(R, variable, default_prec=prec) return PS(mol) return mol
def q_dimension(self, q=None, prec=None, use_product=False): r""" Return the `q`-dimension of ``self``. Let `B(\lambda)` denote a highest weight crystal. Recall that the degree of the `\mu`-weight space of `B(\lambda)` (under the principal gradation) is equal to `\langle \rho^{\vee}, \lambda - \mu \rangle` where `\langle \rho^{\vee}, \alpha_i \rangle = 1` for all `i \in I` (in particular, take `\rho^{\vee} = \sum_{i \in I} h_i`). The `q`-dimension of a highest weight crystal `B(\lambda)` is defined as .. MATH:: \dim_q B(\lambda) := \sum_{j \geq 0} \dim(B_j) q^j, where `B_j` denotes the degree `j` portion of `B(\lambda)`. This can be expressed as the product .. MATH:: \dim_q B(\lambda) = \prod_{\alpha^{\vee} \in \Delta_+^{\vee}} \left( \frac{1 - q^{\langle \lambda + \rho, \alpha^{\vee} \rangle}}{1 - q^{\langle \rho, \alpha^{\vee} \rangle}} \right)^{\mathrm{mult}\, \alpha}, where `\Delta_+^{\vee}` denotes the set of positive coroots. Taking the limit as `q \to 1` gives the dimension of `B(\lambda)`. For more information, see [Kac]_ Section 10.10. INPUT: - ``q`` -- the (generic) parameter `q` - ``prec`` -- (default: ``None``) The precision of the power series ring to use if the crystal is not known to be finite (i.e. the number of terms returned). If ``None``, then the result is returned as a lazy power series. - ``use_product`` -- (default: ``False``) if we have a finite crystal and ``True``, use the product formula EXAMPLES:: sage: C = crystals.Tableaux(['A',2], shape=[2,1]) sage: qdim = C.q_dimension(); qdim q^4 + 2*q^3 + 2*q^2 + 2*q + 1 sage: qdim(1) 8 sage: len(C) == qdim(1) True sage: C.q_dimension(use_product=True) == qdim True sage: C.q_dimension(prec=20) q^4 + 2*q^3 + 2*q^2 + 2*q + 1 sage: C.q_dimension(prec=2) 2*q + 1 sage: R.<t> = QQ[] sage: C.q_dimension(q=t^2) t^8 + 2*t^6 + 2*t^4 + 2*t^2 + 1 sage: C = crystals.Tableaux(['A',2], shape=[5,2]) sage: C.q_dimension() q^10 + 2*q^9 + 4*q^8 + 5*q^7 + 6*q^6 + 6*q^5 + 6*q^4 + 5*q^3 + 4*q^2 + 2*q + 1 sage: C = crystals.Tableaux(['B',2], shape=[2,1]) sage: qdim = C.q_dimension(); qdim q^10 + 2*q^9 + 3*q^8 + 4*q^7 + 5*q^6 + 5*q^5 + 5*q^4 + 4*q^3 + 3*q^2 + 2*q + 1 sage: qdim == C.q_dimension(use_product=True) True sage: C = crystals.Tableaux(['D',4], shape=[2,1]) sage: C.q_dimension() q^16 + 2*q^15 + 4*q^14 + 7*q^13 + 10*q^12 + 13*q^11 + 16*q^10 + 18*q^9 + 18*q^8 + 18*q^7 + 16*q^6 + 13*q^5 + 10*q^4 + 7*q^3 + 4*q^2 + 2*q + 1 We check with a finite tensor product:: sage: TP = crystals.TensorProduct(C, C) sage: TP.cardinality() 25600 sage: qdim = TP.q_dimension(use_product=True); qdim # long time q^32 + 2*q^31 + 8*q^30 + 15*q^29 + 34*q^28 + 63*q^27 + 110*q^26 + 175*q^25 + 276*q^24 + 389*q^23 + 550*q^22 + 725*q^21 + 930*q^20 + 1131*q^19 + 1362*q^18 + 1548*q^17 + 1736*q^16 + 1858*q^15 + 1947*q^14 + 1944*q^13 + 1918*q^12 + 1777*q^11 + 1628*q^10 + 1407*q^9 + 1186*q^8 + 928*q^7 + 720*q^6 + 498*q^5 + 342*q^4 + 201*q^3 + 117*q^2 + 48*q + 26 sage: qdim(1) # long time 25600 sage: TP.q_dimension() == qdim # long time True The `q`-dimensions of infinite crystals are returned as formal power series:: sage: C = crystals.LSPaths(['A',2,1], [1,0,0]) sage: C.q_dimension(prec=5) 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + O(q^5) sage: C.q_dimension(prec=10) 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 + 9*q^7 + 13*q^8 + 16*q^9 + O(q^10) sage: qdim = C.q_dimension(); qdim 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + O(x^11) sage: qdim.compute_coefficients(15) sage: qdim 1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6 + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + 27*q^11 + 36*q^12 + 44*q^13 + 57*q^14 + 70*q^15 + O(x^16) REFERENCES: .. [Kac] Victor G. Kac. *Infinite-dimensional Lie Algebras*. Third edition. Cambridge University Press, Cambridge, 1990. """ from sage.rings.all import ZZ WLR = self.weight_lattice_realization() I = self.index_set() mg = self.highest_weight_vectors() max_deg = float('inf') if prec is None else prec - 1 def iter_by_deg(gens): next = set(gens) deg = -1 while next and deg < max_deg: deg += 1 yield len(next) todo = next next = set([]) while todo: x = todo.pop() for i in I: y = x.f(i) if y is not None: next.add(y) # def iter_by_deg from sage.categories.finite_crystals import FiniteCrystals if self in FiniteCrystals(): if q is None: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing q = PolynomialRing(ZZ, 'q').gen(0) if use_product: # Since we are in the classical case, all roots occur with multiplicity 1 pos_coroots = [x.associated_coroot() for x in WLR.positive_roots()] rho = WLR.rho() P = q.parent() ret = P.zero() for v in self.highest_weight_vectors(): hw = v.weight() ret += P.prod((1 - q**(rho+hw).scalar(ac)) / (1 - q**rho.scalar(ac)) for ac in pos_coroots) # We do a cast since the result would otherwise live in the fraction field return P(ret) elif prec is None: # If we're here, we may not be a finite crystal. # In fact, we're probably infinite. from sage.combinat.species.series import LazyPowerSeriesRing if q is None: P = LazyPowerSeriesRing(ZZ, names='q') else: P = q.parent() if not isinstance(P, LazyPowerSeriesRing): raise TypeError("the parent of q must be a lazy power series ring") ret = P(iter_by_deg(mg)) ret.compute_coefficients(10) return ret from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic if q is None: q = PowerSeriesRing(ZZ, 'q', default_prec=prec).gen(0) P = q.parent() ret = P.sum(c * q**deg for deg,c in enumerate(iter_by_deg(mg))) if ret.degree() == max_deg and isinstance(P, PowerSeriesRing_generic): ret = P(ret, prec) return ret
def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, absprec = infinity, relprec = infinity): """ TESTS:: sage: K = Qp(13,7) sage: R.<t> = K[] sage: R([K(13), K(1)]) (1 + O(13^7))*t + (13 + O(13^8)) sage: T.<t> = ZZ[] sage: R(t + 2) (1 + O(13^7))*t + (2 + O(13^7)) """ Polynomial.__init__(self, parent, is_gen=is_gen) parentbr = parent.base_ring() from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing if construct: (self._poly, self._valbase, self._relprecs, self._normalized, self._valaddeds, self._list) = x #the last two of these may be None return elif is_gen: self._poly = PolynomialRing(ZZ, parent.variable_name()).gen() self._valbase = 0 self._valaddeds = [infinity, 0] self._relprecs = [infinity, parentbr.precision_cap()] self._normalized = True self._list = None return #First we list the types that are turned into Polynomials if isinstance(x, ZZX): x = Polynomial_integer_dense(PolynomialRing(ZZ, parent.variable_name()), x, construct = True) elif isinstance(x, fraction_field_element.FractionFieldElement) and \ x.denominator() == 1: #Currently we ignore precision information in the denominator. This should be changed eventually x = x.numerator() #We now coerce various types into lists of coefficients. There are fast pathways for some types of polynomials if isinstance(x, Polynomial): if x.parent() is self.parent(): if not absprec is infinity or not relprec is infinity: x._normalize() self._poly = x._poly self._valbase = x._valbase self._valaddeds = x._valaddeds self._relprecs = x._relprecs self._normalized = x._normalized self._list = x._list if not absprec is infinity or not relprec is infinity: self._adjust_prec_info(absprec, relprec) return elif x.base_ring() is ZZ: self._poly = x self._valbase = Integer(0) p = parentbr.prime() self._relprecs = [c.valuation(p) + parentbr.precision_cap() for c in x.list()] self._comp_valaddeds() self._normalized = len(self._valaddeds) == 0 or (min(self._valaddeds) == 0) self._list = None if not absprec is infinity or not relprec is infinity: self._adjust_prec_info(absprec, relprec) return else: x = [parentbr(a) for a in x.list()] check = False elif isinstance(x, dict): zero = parentbr.zero_element() n = max(x.keys()) v = [zero for _ in xrange(n + 1)] for i, z in x.iteritems(): v[i] = z x = v elif isinstance(x, pari_gen): x = [parentbr(w) for w in x.list()] check = False #The default behavior if we haven't already figured out what the type is is to assume it coerces into the base_ring as a constant polynomial elif not isinstance(x, list): x = [x] # constant polynomial # In contrast to other polynomials, the zero element is not distinguished # by having its list empty. Instead, it has list [0] if not x: x = [parentbr.zero_element()] if check: x = [parentbr(z) for z in x] # Remove this -- for p-adics this is terrible, since it kills any non exact zero. #if len(x) == 1 and not x[0]: # x = [] self._list = x self._valaddeds = [a.valuation() for a in x] self._valbase = sage.rings.padics.misc.min(self._valaddeds) if self._valbase is infinity: self._valaddeds = [] self._relprecs = [] self._poly = PolynomialRing(ZZ, parent.variable_name())() self._normalized = True if not absprec is infinity or not relprec is infinity: self._adjust_prec_info(absprec, relprec) else: self._valaddeds = [c - self._valbase for c in self._valaddeds] self._relprecs = [a.precision_absolute() - self._valbase for a in x] self._poly = PolynomialRing(ZZ, parent.variable_name())([a >> self._valbase for a in x]) self._normalized = True if not absprec is infinity or not relprec is infinity: self._adjust_prec_info(absprec, relprec)
def _forward_image(self, f, check=True): r""" Compute the forward image of this subscheme by the morphism ``f``. The forward image is computed through elimination and ``f`` must be a morphism for this to be well defined. In particular, let $X = V(h_1,\ldots, h_t)$ and define the ideal $I = (h_1,\ldots,h_t,y_0-f_0(\bar{x}), \ldots, y_n-f_n(\bar{x}))$. Then the elimination ideal $I_{n+1} = I \cap K[y_0,\ldots,y_n]$ is a homogeneous ideal and $self(X) = V(I_{n+1})$. INPUT: - ``f`` -- a map whose domain contains ``self`` - ``check`` -- Boolean, if `False` no input checking is done OUTPUT: - a subscheme in the codomain of ``f``. EXAMPLES:: sage: PS.<x,y,z> = ProjectiveSpace(QQ, 2) sage: H = End(PS) sage: f = H([x^2, y^2-2*z^2, z^2]) sage: X = PS.subscheme(y-2*z) sage: X._forward_image(f) Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: y - 2*z :: sage: set_verbose(None) sage: PS.<x,y,z,w> = ProjectiveSpace(ZZ, 3) sage: H = End(PS) sage: f = H([y^2, x^2, w^2, z^2]) sage: X = PS.subscheme([z^2+y*w, x-w]) sage: f(X) Closed subscheme of Projective Space of dimension 3 over Integer Ring defined by: y - z, x*z - w^2 :: sage: PS.<x,y,z,w> = ProjectiveSpace(CC, 3) sage: H = End(PS) sage: f = H([x^2 + y^2, y^2, z^2-y^2, w^2]) sage: X = PS.subscheme([z-2*w]) sage: f(X) Closed subscheme of Projective Space of dimension 3 over Complex Field with 53 bits of precision defined by: y + z + (-4.00000000000000)*w :: sage: R.<t> = PolynomialRing(QQ) sage: P.<x,y,z> = ProjectiveSpace(FractionField(R), 2) sage: H = End(P) sage: f = H([x^2 + 2*y*z, t^2*y^2, z^2]) sage: f([t^2*y-z]) Closed subscheme of Projective Space of dimension 2 over Fraction Field of Univariate Polynomial Ring in t over Rational Field defined by: y + (-1/t^2)*z :: sage: set_verbose(-1) sage: PS.<x,y,z> = ProjectiveSpace(Qp(3), 2) sage: H = End(PS) sage: f = H([x^2,2*y^2,z^2]) sage: X = PS.subscheme([2*x-y,z]) sage: f(X) Closed subscheme of Projective Space of dimension 2 over 3-adic Field with capped relative precision 20 defined by: z, x + (1 + 3^2 + 3^4 + 3^6 + 3^8 + 3^10 + 3^12 + 3^14 + 3^16 + 3^18 + O(3^20))*y :: sage: R.<y0,y1,y2,y3> = PolynomialRing(QQ) sage: P.<x,y,z> = ProjectiveSpace(FractionField(R), 2) sage: H = End(P) sage: f = H([y0*x^2+y1*z^2, y2*y^2+y3*z^2, z^2]) sage: X = P.subscheme(x*z) sage: X._forward_image(f) Closed subscheme of Projective Space of dimension 2 over Fraction Field of Multivariate Polynomial Ring in y0, y1, y2, y3 over Rational Field defined by: x*z + (-y1)*z^2 :: sage: P2.<x,y,z> = ProjectiveSpace(QQ, 2) sage: P5.<z0,z1,z2,z3,z4,z5> = ProjectiveSpace(QQ, 5) sage: H = Hom(P2, P5) sage: f = H([x^2,x*y,x*z,y^2,y*z,z^2]) #Veronese map sage: X = P2.subscheme([]) sage: f(X) Closed subscheme of Projective Space of dimension 5 over Rational Field defined by: -z4^2 + z3*z5, -z2*z4 + z1*z5, -z2*z3 + z1*z4, -z2^2 + z0*z5, -z1*z2 + z0*z4, -z1^2 + z0*z3 :: sage: P2.<x,y,z>=ProjectiveSpace(QQ, 2) sage: P3.<u,v,w,t>=ProjectiveSpace(QQ, 3) sage: H = Hom(P2, P3) sage: X = P2.subscheme([x-y,x-z]) sage: f = H([x^2,y^2,z^2,x*y]) sage: f(X) Closed subscheme of Projective Space of dimension 3 over Rational Field defined by: w - t, v - t, u - t :: sage: P1.<u,v> = ProjectiveSpace(QQ, 1) sage: P2.<x,y,z> = ProjectiveSpace(QQ, 2) sage: H = Hom(P2,P1) sage: f = H([x^2,y*z]) sage: X = P2.subscheme([x-y]) sage: f(X) Traceback (most recent call last): ... TypeError: map must be a morphism :: sage: PS.<x,y,z> = ProjectiveSpace(ZZ, 2) sage: H = End(PS) sage: f = H([x^3, x*y^2, x*z^2]) sage: X = PS.subscheme([x-y]) sage: X._forward_image(f) Traceback (most recent call last): ... TypeError: map must be a morphism :: sage: PS.<x,y,z> = ProjectiveSpace(QQ, 2) sage: P1.<u,v> = ProjectiveSpace(QQ, 1) sage: Y = P1.subscheme([u-v]) sage: H = End(PS) sage: f = H([x^2, y^2, z^2]) sage: Y._forward_image(f) Traceback (most recent call last): ... TypeError: subscheme must be in ambient space of domain of map """ dom = f.domain() codom = f.codomain() if check: if not f.is_morphism(): raise TypeError("map must be a morphism") if self.ambient_space() != dom: raise TypeError( "subscheme must be in ambient space of domain of map") CR_dom = dom.coordinate_ring() CR_codom = codom.coordinate_ring() n = CR_dom.ngens() m = CR_codom.ngens() #can't call eliminate if the base ring is polynomial so we do it ourselves #with a lex ordering R = PolynomialRing(f.base_ring(), n + m, 'tempvar', order='lex') Rvars = R.gens()[0:n] phi = CR_dom.hom(Rvars, R) zero = n * [0] psi = R.hom(zero + list(CR_codom.gens()), CR_codom) #set up ideal L = R.ideal([phi(t) for t in self.defining_polynomials()] + [R.gen(n + i) - phi(f[i]) for i in range(m)]) G = L.groebner_basis() #eliminate newL = [] #get only the elimination ideal portion for i in range(len(G) - 1, 0, -1): v = G[i].variables() if all(Rvars[j] not in v for j in range(n)): newL.append(psi(G[i])) return (codom.subscheme(newL))
from isogeny_primes import get_isogeny_primes from .common_utils import EC_Q_ISOGENY_PRIMES from sage.all import QQ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(QQ, "x") LMFDB_DEFAULTS = { "norm_bound": 50, "bound": 1000, "use_PIL": False, "ice_filter": True, "appendix_bound": 1000, "stop_strategy": "auto", } def isogeny_primes(coeffs, **kwargs): del kwargs["label"] f = R(coeffs) K = QQ.extension(f, "a") kwargs = {**LMFDB_DEFAULTS, **kwargs} primes, _ = get_isogeny_primes(K, **kwargs) return sorted(EC_Q_ISOGENY_PRIMES) + sorted(primes.difference(EC_Q_ISOGENY_PRIMES))
def __getitem__(self, arg): """ Extend this ring by one or several elements to create a polynomial ring, a power series ring, or an algebraic extension. This is a convenience method intended primarily for interactive use. .. SEEALSO:: :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`, :func:`~sage.rings.power_series_ring.PowerSeriesRing`, :meth:`~sage.rings.ring.Ring.extension`, :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`, :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`, :meth:`sage.structure.parent.Parent.__getitem__` EXAMPLES: We create several polynomial rings:: sage: ZZ['x'] Univariate Polynomial Ring in x over Integer Ring sage: QQ['x'] Univariate Polynomial Ring in x over Rational Field sage: GF(17)['abc'] Univariate Polynomial Ring in abc over Finite Field of size 17 sage: GF(17)['a,b,c'] Multivariate Polynomial Ring in a, b, c over Finite Field of size 17 sage: GF(17)['a']['b'] Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17 We can create Ore polynomial rings:: sage: k.<t> = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: k['x', Frob] Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: R.<t> = QQ[] sage: der = R.derivation() sage: R['d', der] Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt We can also create power series rings by using double brackets:: sage: QQ[['t']] Power Series Ring in t over Rational Field sage: ZZ[['W']] Power Series Ring in W over Integer Ring sage: ZZ[['x,y,z']] Multivariate Power Series Ring in x, y, z over Integer Ring sage: ZZ[['x','T']] Multivariate Power Series Ring in x, T over Integer Ring Use :func:`~sage.rings.fraction_field.Frac` or :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain the fields of rational functions and Laurent series:: sage: Frac(QQ['t']) Fraction Field of Univariate Polynomial Ring in t over Rational Field sage: Frac(QQ[['t']]) Laurent Series Ring in t over Rational Field sage: QQ[['t']].fraction_field() Laurent Series Ring in t over Rational Field Note that the same syntax can be used to create number fields:: sage: QQ[I] Number Field in I with defining polynomial x^2 + 1 with I = 1*I sage: QQ[I].coerce_embedding() Generic morphism: From: Number Field in I with defining polynomial x^2 + 1 with I = 1*I To: Complex Lazy Field Defn: I -> 1*I :: sage: QQ[sqrt(2)] Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095? sage: QQ[sqrt(2)].coerce_embedding() Generic morphism: From: Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095? To: Real Lazy Field Defn: sqrt2 -> 1.414213562373095? :: sage: QQ[sqrt(2),sqrt(3)] Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field and orders in number fields:: sage: ZZ[I] Order in Number Field in I with defining polynomial x^2 + 1 with I = 1*I sage: ZZ[sqrt(5)] Order in Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790? sage: ZZ[sqrt(2)+sqrt(3)] Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1 with a = 3.146264369941973? Embeddings are found for simple extensions (when that makes sense):: sage: QQi.<i> = QuadraticField(-1, 'i') sage: QQ[i].coerce_embedding() Generic morphism: From: Number Field in i with defining polynomial x^2 + 1 with i = 1*I To: Complex Lazy Field Defn: i -> 1*I TESTS: A few corner cases:: sage: QQ[()] Multivariate Polynomial Ring in no variables over Rational Field sage: QQ[[]] Traceback (most recent call last): ... TypeError: power series rings must have at least one variable These kind of expressions do not work:: sage: QQ['a,b','c'] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[['a,b','c']] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[[['x']]] Traceback (most recent call last): ... TypeError: expected R[...] or R[[...]], not R[[[...]]] Extension towers are built as follows and use distinct generator names:: sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)] sage: K Number Field in a with defining polynomial x^3 - 2 over its base field sage: K.base_field() Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field sage: K.base_field().base_field() Number Field in b with defining polynomial x^3 - 3 Embeddings:: sage: QQ[I](I.pyobject()) I sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1) sage: QQ[expr].coerce_embedding() is None False sage: QQ[sqrt(5)].gen() > 0 True sage: expr = sqrt(2) + I*(cos(pi/4, hold=True) - sqrt(2)/2) sage: QQ[expr].coerce_embedding() Generic morphism: From: Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? To: Real Lazy Field Defn: a -> 1.414213562373095? """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): # Allowing arbitrary iterables would create confusion, but we # may want to support a few more. return tuple(arg) elif isinstance(arg, str): return tuple(arg.split(',')) else: return (arg, ) # 1. If arg is a list, try to return a power series ring. if isinstance(arg, list): if not arg: raise TypeError( "power series rings must have at least one variable") elif len(arg) == 1: # R[["a,b"]], R[[(a,b)]]... if isinstance(arg[0], list): raise TypeError( "expected R[...] or R[[...]], not R[[[...]]]") elts = normalize_arg(arg[0]) else: elts = normalize_arg(arg) from sage.rings.power_series_ring import PowerSeriesRing return PowerSeriesRing(self, elts) if isinstance(arg, tuple): from sage.categories.morphism import Morphism from sage.rings.derivation import RingDerivation if len(arg) == 2 and isinstance(arg[1], (Morphism, RingDerivation)): from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing return OrePolynomialRing(self, arg[1], names=arg[0]) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension elts = normalize_arg(arg) try: minpolys = [a.minpoly() for a in elts] except (AttributeError, NotImplementedError, ValueError, TypeError): minpolys = None if minpolys: # how to pass in names? names = tuple(_gen_names(elts)) if len(elts) == 1: from sage.rings.all import CIF, CLF, RLF elt = elts[0] try: iv = CIF(elt) except (TypeError, ValueError): emb = None else: # First try creating an ANRoot manually, because # extension(..., embedding=CLF(expr)) (or # ...QQbar(expr)) would normalize the expression in # QQbar, which currently is VERY slow in many cases. # This may fail when minpoly has close roots or elt is # a complicated symbolic expression. # TODO: Rewrite using #19362 and/or #17886 and/or # #15600 once those issues are solved. from sage.rings.qqbar import AlgebraicNumber, ANRoot try: elt = AlgebraicNumber(ANRoot(minpolys[0], iv)) except ValueError: pass # Force a real embedding when possible, to get the # right ordered ring structure. if (iv.imag().is_zero() or iv.imag().contains_zero() and elt.imag().is_zero()): emb = RLF(elt) else: emb = CLF(elt) return self.extension(minpolys[0], names[0], embedding=emb) try: # Doing the extension all at once is best, if possible... return self.extension(minpolys, names) except (TypeError, ValueError): # ...but we can also construct it iteratively return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self) # 2. Otherwise, try to return a polynomial ring from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing return PolynomialRing(self, elts)
def interreduction(self, tailreduce=True, sorted=False, report=None, RStrat=None): """ Return symmetrically interreduced form of self INPUT: - ``tailreduce`` -- (bool, default ``True``) If True, the interreduction is also performed on the non-leading monomials. - ``sorted`` -- (bool, default ``False``) If True, it is assumed that the generators of self are already increasingly sorted. - ``report`` -- (object, default ``None``) If not None, some information on the progress of computation is printed - ``RStrat`` -- (:class:`~sage.rings.polynomial.symmetric_reduction.SymmetricReductionStrategy`, default ``None``) A reduction strategy to which the polynomials resulting from the interreduction will be added. If ``RStrat`` already contains some polynomials, they will be used in the interreduction. The effect is to compute in a quotient ring. OUTPUT: A Symmetric Ideal J (sorted list of generators) coinciding with self as an ideal, so that any generator is symmetrically reduced w.r.t. the other generators. Note that the leading coefficients of the result are not necessarily 1. EXAMPLES:: sage: X.<x> = InfinitePolynomialRing(QQ) sage: I=X*(x[1]+x[2],x[1]*x[2]) sage: I.interreduction() Symmetric Ideal (-x_1^2, x_2 + x_1) of Infinite polynomial ring in x over Rational Field Here, we show the ``report`` option:: sage: I.interreduction(report=True) Symmetric interreduction [1/2] > [2/2] :> [1/2] > [2/2] T[1]> > Symmetric Ideal (-x_1^2, x_2 + x_1) of Infinite polynomial ring in x over Rational Field ``[m/n]`` indicates that polynomial number ``m`` is considered and the total number of polynomials under consideration is ``n``. '-> 0' is printed if a zero reduction occurred. The rest of the report is as described in :meth:`sage.rings.polynomial.symmetric_reduction.SymmetricReductionStrategy.reduce`. Last, we demonstrate the use of the optional parameter ``RStrat``:: sage: from sage.rings.polynomial.symmetric_reduction import SymmetricReductionStrategy sage: R = SymmetricReductionStrategy(X) sage: R Symmetric Reduction Strategy in Infinite polynomial ring in x over Rational Field sage: I.interreduction(RStrat=R) Symmetric Ideal (-x_1^2, x_2 + x_1) of Infinite polynomial ring in x over Rational Field sage: R Symmetric Reduction Strategy in Infinite polynomial ring in x over Rational Field, modulo x_1^2, x_2 + x_1 sage: R = SymmetricReductionStrategy(X,[x[1]^2]) sage: I.interreduction(RStrat=R) Symmetric Ideal (x_2 + x_1) of Infinite polynomial ring in x over Rational Field """ DONE = [] j = 0 TODO = [] PARENT = self.ring() for P in self.gens(): if P._p != 0: if P.is_unit(): # self generates all of self.ring() if RStrat is not None: RStrat.add_generator(PARENT(1)) return SymmetricIdeal(self.ring(), [self.ring()(1)], coerce=False) TODO.append(P) if not sorted: TODO = list(set(TODO)) TODO.sort() if hasattr(PARENT, '_P'): CommonR = PARENT._P else: VarList = set([]) for P in TODO: if P._p != 0: if P.is_unit(): # self generates all of PARENT if RStrat is not None: RStrat.add_generator(PARENT(1)) return SymmetricIdeal(PARENT, [PARENT(1)], coerce=False) VarList = VarList.union(P._p.parent().variable_names()) VarList = list(VarList) if not VarList: return SymmetricIdeal(PARENT, [0]) VarList.sort(key=PARENT.varname_key, reverse=True) from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing CommonR = PolynomialRing(self.base_ring(), VarList, order=self.ring()._order) ## Now, the symmetric interreduction starts if not (report is None): print('Symmetric interreduction') from sage.rings.polynomial.symmetric_reduction import SymmetricReductionStrategy if RStrat is None: RStrat = SymmetricReductionStrategy(self.ring(), tailreduce=tailreduce) GroundState = RStrat.gens() while (1): RStrat.setgens(GroundState) DONE = [] for i in range(len(TODO)): if report is not None: print('[%d/%d] ' % (i + 1, len(TODO)), end="") sys.stdout.flush() p = RStrat.reduce(TODO[i], report=report) if p._p != 0: if p.is_unit(): # self generates all of self.ring() return SymmetricIdeal(self.ring(), [self.ring()(1)], coerce=False) RStrat.add_generator(p, good_input=True) DONE.append(p) else: if not (report is None): print("-> 0") DONE.sort() if DONE == TODO: break else: if len(TODO) == len(DONE): import copy bla = copy.copy(TODO) bla.sort() if bla == DONE: break TODO = DONE return SymmetricIdeal(self.ring(), DONE, coerce=False)
def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose): r""" Helper function used by eta_poly_relations. Finds a basis for the space of linear relations between the first qexp_terms of the `q`-expansions of the monomials `\eta_1^i * \eta_2^j` for `0 \le i,j < degree`, and calculates a Groebner basis for the ideal generated by these relations. Liable to return meaningless results if qexp_terms isn't at least `1 + d*(m_1,m_2)` where .. MATH:: m_i = min(0, {\text degree of the pole of $\eta_i$ at $\infty$}) as then 1 will be in the ideal. EXAMPLES:: sage: from sage.modular.etaproducts import _eta_relations_helper sage: r,s = EtaGroup(4).basis() sage: _eta_relations_helper(r,s,4,100,['a','b'],False) [a*b - a + 16] sage: _eta_relations_helper(EtaProduct(26, {2:2,13:2,26:-2,1:-2}),EtaProduct(26, {2:4,13:2,26:-4,1:-2}),3,12,['a','b'],False) # not enough terms, will return rubbish [1] """ indices = [(i, j) for j in range(degree) for i in range(degree)] inf = CuspFamily(eta1.level(), 1) pole_at_infinity = -(min([0, eta1.order_at_cusp(inf)]) + min([0, eta2.order_at_cusp(inf)])) * degree if verbose: print("Trying all coefficients from q^%s to q^%s inclusive" % (-pole_at_infinity, -pole_at_infinity + qexp_terms - 1)) rows = [] for j in range(qexp_terms): rows.append([]) for i in indices: func = (eta1**i[0] * eta2**i[1]).qexp(qexp_terms) for j in range(qexp_terms): rows[j].append(func[j - pole_at_infinity]) M = matrix(rows) V = M.right_kernel() if V.dimension() == 0: if verbose: print("No polynomial relation of order %s valid for %s terms" % (degree, qexp_terms)) return None if V.dimension() >= 1: R = PolynomialRing(QQ, 2, labels) x, y = R.gens() relations = [ sum([ c[v] * x**indices[v][0] * y**indices[v][1] for v in range(len(indices)) ]) for c in V.basis() ] id = R.ideal(relations) return id.groebner_basis()
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 enumerate_totallyreal_fields_rel(F, m, B, a=[], verbose=0, return_seqs=False, return_pari_objects=True): r""" This function enumerates (primitive) totally real field extensions of degree `m>1` of the totally real field F with discriminant `d \leq B`; optionally one can specify the first few coefficients, where the sequence ``a`` corresponds to a polynomial by :: a[d]*x^n + ... + a[0]*x^(n-d) if ``length(a) = d+1``, so in particular always ``a[d] = 1``. .. note:: This is guaranteed to give all primitive such fields, and seems in practice to give many imprimitive ones. INPUT: - ``F`` -- number field, the base field - ``m`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``a`` -- list (default: []), the coefficient list to begin with - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. OUTPUT: - the list of fields with entries ``[d,fabs,f]``, where ``d`` is the discriminant, ``fabs`` is an absolute defining polynomial, and ``f`` is a defining polynomial relative to ``F``, sorted by discriminant. - if ``return_seqs`` is ``True``, then the first field of the list is a list containing the count of four items as explained below - the first entry gives the number of polynomials tested - the second entry gives the number of polynomials with its discriminant having a large enough square divisor - the third entry is the number of irreducible polynomials - the fourth entry is the number of irreducible polynomials with discriminant at most ``B`` EXAMPLES:: sage: ZZx = ZZ['x'] sage: F.<t> = NumberField(x^2-2) sage: enumerate_totallyreal_fields_rel(F, 1, 2000) [[1, [-2, 0, 1], xF - 1]] sage: enumerate_totallyreal_fields_rel(F, 2, 2000) [[1600, x^4 - 6*x^2 + 4, xF^2 + xF - 1]] sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_seqs=True) [[9, 6, 5, 0], [[1600, [4, 0, -6, 0, 1], [-1, 1, 1]]]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_rel(F, 2, 2000)[0][1].parent() Interface to the PARI C library sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_pari_objects=False)[0][0].parent() Integer Ring sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_pari_objects=False)[0][2].parent() Univariate Polynomial Ring in xF over Number Field in t with defining polynomial x^2 - 2 sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_seqs=True)[1][0][1][0].parent() Rational Field AUTHORS: - John Voight (2007-11-01) """ if not isinstance(m, Integer): try: m = Integer(m) except TypeError: raise TypeError("cannot coerce m (= %s) to an integer" % m) if (m < 1): raise ValueError("m must be at least 1.") n = F.degree() * m # Initialize S = [] Srel = [] dB_odlyzko = odlyzko_bound_totallyreal(n) dB = math.ceil(40000 * dB_odlyzko**n) counts = [0, 0, 0, 0] # Trivial case if m == 1: g = pari(F.defining_polynomial()).reverse().Vec() if return_seqs: return [[0, 0, 0, 0], [1, [-1, 1], g]] elif return_pari_objects: return [[1, g, pari('xF-1')]] else: Px = PolynomialRing(QQ, 'xF') return [[ZZ(1), map(QQ, g), Px.gen() - 1]] if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen f_out = [0] * m + [1] T = tr_data_rel(F, m, B, a) if verbose == 2: T.incr(f_out, verbose) else: T.incr(f_out) Fx = PolynomialRing(F, 'xF') nfF = pari( str(F.defining_polynomial()).replace('x', str(F.primitive_element()))) parit = pari(str(F.primitive_element())) while f_out[m] != 0: counts[0] += 1 if verbose: print "==>", f_out, f_str = '' for i in range(len(f_out)): f_str += '(' + str(f_out[i]) + ')*x^' + str(i) if i < len(f_out) - 1: f_str += '+' nf = pari(f_str) if nf.poldegree('t') == 0: nf = nf.subst('x', 'x-t') nf = nf.polresultant(nfF, parit) d = nf.poldisc() #counts[0] += 1 if d > 0 and nf.polsturm_full() == n: da = int_has_small_square_divisor(Integer(d)) if d > dB or d <= B * da: counts[1] += 1 if nf.polisirreducible(): counts[2] += 1 [zk, d] = nf.nfbasis_d() if d <= B: if verbose: print "has discriminant", d, # Find a minimal lattice element counts[3] += 1 ng = pari([nf, zk]).polredabs() # Check if K is contained in the list. found = False ind = bisect.bisect_left(S, [d, ng]) while ind < len(S) and S[ind][0] == d: if S[ind][1] == ng: if verbose: print "but is not new" found = True break ind += 1 if not found: if verbose: print "and is new!" S.insert(ind, [d, ng]) Srel.insert(ind, Fx(f_out)) else: if verbose: print "has discriminant", abs(d), "> B" else: if verbose: print "is not absolutely irreducible" else: if verbose: print "has discriminant", abs( d), "with no large enough square divisor" else: if verbose: if d == 0: print "is not squarefree" else: print "is not totally real" if verbose == 2: T.incr(f_out, verbose=verbose) else: T.incr(f_out) # In the application of Smyth's theorem above, we exclude finitely # many possibilities which we must now throw back in. if m == 2: if Fx([-1, 1, 1]).is_irreducible(): K = F.extension(Fx([-1, 1, 1]), 'tK') Kabs = K.absolute_field('tKabs') Kabs_pari = pari(Kabs.defining_polynomial()) d = K.absolute_discriminant() if abs(d) <= B: ng = Kabs_pari.polredabs() ind = bisect.bisect_left(S, [d, ng]) S.insert(ind, [d, ng]) Srel.insert(ind, Fx([-1, 1, 1])) elif F.degree() == 2: for ff in [[1, -7, 13, -7, 1], [1, -8, 14, -7, 1]]: f = Fx(ff).factor()[0][0] K = F.extension(f, 'tK') Kabs = K.absolute_field('tKabs') Kabs_pari = pari(Kabs.defining_polynomial()) d = K.absolute_discriminant() if abs(d) <= B: ng = Kabs_pari.polredabs() ind = bisect.bisect_left(S, [d, ng]) S.insert(ind, [d, ng]) Srel.insert(ind, f) elif m == 3: if Fx([-1, 6, -5, 1]).is_irreducible(): K = F.extension(Fx([-1, 6, -5, 1]), 'tK') Kabs = K.absolute_field('tKabs') Kabs_pari = pari(Kabs.defining_polynomial()) d = K.absolute_discriminant() if abs(d) <= B: ng = Kabs_pari.polredabs() ind = bisect.bisect_left(S, [d, ng]) S.insert(ind, [d, ng]) Srel.insert(ind, Fx([-1, 6, -5, 1])) # Now check for isomorphic fields S = [[S[i][0], S[i][1], Srel[i]] for i in range(len(S))] weed_fields(S) # Output. if verbose: print "=" * 80 print "Polynomials tested: {}".format(counts[0]) print( "Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print "Irreducible polynomials: {}".format(counts[2]) print "Polynomials with nfdisc <= B: {}".format(counts[3]) for i in range(len(S)): print S[i] if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [ map(ZZ, counts), [[s[0], map(QQ, s[1].reverse().Vec()), s[2].coeffs()] for s in S] ] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[s[0], Px(map(QQ, s[1].list())), s[2]] for s in S]
def ehrhart_polynomial(self, verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, smith_form=None, dualization=None, triangulation=None, triangulation_max_height=None, **kwds): r""" Return the Ehrhart polynomial of this polyhedron. Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a rational polynomial of degree `d` for integer `t`. `L` is called the *Ehrhart polynomial* of `P`. For more information see the :wikipedia:`Ehrhart_polynomial`. INPUT: - ``verbose`` - (boolean, default to ``False``) if ``True``, print the whole output of the LattE command. The following options are passed to the LattE command, for details you should consult `the LattE documentation <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__: - ``dual`` - (boolean) triangulate and signed-decompose in the dual space - ``irrational_primal`` - (boolean) triangulate in the dual space, signed-decompose in the primal space using irrationalization. - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose in the primal space using irrationalization. - ``maxdet`` -- (integer) decompose down to an index (determinant) of ``maxdet`` instead of index 1 (unimodular cones). - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones. - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2' - ``smith_form`` -- (string) either 'ilio' or 'lidia' - ``dualization`` -- (string) either 'cdd' or '4ti2' - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom' - ``triangulation_max_height`` - (integer) use a uniform distribution of height from 1 to this number .. NOTE:: Any additional argument is forwarded to LattE's executable ``count``. All occurrences of '_' will be replaced with a '-'. ALGORITHM: This method calls the program ``count`` from LattE integrale, a program for lattice point enumeration (see https://www.math.ucdavis.edu/~latte/). EXAMPLES:: sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)]) sage: p = P.ehrhart_polynomial() # optional - latte_int sage: p # optional - latte_int 7/2*t^3 + 2*t^2 - 1/2*t + 1 sage: p(1) # optional - latte_int 6 sage: len(P.integral_points()) 6 sage: p(2) # optional - latte_int 36 sage: len((2*P).integral_points()) 36 The unit hypercubes:: sage: from itertools import product sage: def hypercube(d): ....: return Polyhedron(vertices=list(product([0,1],repeat=d))) sage: hypercube(3).ehrhart_polynomial() # optional - latte_int t^3 + 3*t^2 + 3*t + 1 sage: hypercube(4).ehrhart_polynomial() # optional - latte_int t^4 + 4*t^3 + 6*t^2 + 4*t + 1 sage: hypercube(5).ehrhart_polynomial() # optional - latte_int t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1 sage: hypercube(6).ehrhart_polynomial() # optional - latte_int t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1 An empty polyhedron:: sage: P = Polyhedron(ambient_dim=3, vertices=[]) sage: P.ehrhart_polynomial() # optional - latte_int 0 sage: parent(_) # optional - latte_int Univariate Polynomial Ring in t over Rational Field TESTS: Test options:: sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ) sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' '--maxdet=5' --cdd ... ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 sage: p = P.ehrhart_polynomial(dual=True, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --dual --cdd ... ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --irrational-primal --cdd ... ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True) # optional - latte_int This is LattE integrale ... ... Invocation: count --ehrhart-polynomial '--redundancy-check=none' --irrational-all-primal --cdd ... sage: p # optional - latte_int 1/2*t^2 + 3/2*t + 1 Test bad options:: sage: P.ehrhart_polynomial(bim_bam_boum=19) # optional - latte_int Traceback (most recent call last): ... RuntimeError: LattE integrale failed with exit code 1 to execute... """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(QQ, 't') if self.is_empty(): return R.zero() from sage.misc.misc import SAGE_TMP from subprocess import Popen, PIPE ine = self.cdd_Hrepresentation() args = ['count', '--ehrhart-polynomial'] if 'redundancy_check' not in kwds: args.append('--redundancy-check=none') # note: the options below are explicitely written in the function # declaration in order to keep tab completion (see #18211). kwds.update({ 'dual': dual, 'irrational_primal': irrational_primal, 'irrational_all_primal': irrational_all_primal, 'maxdet': maxdet, 'no_decomposition': no_decomposition, 'compute_vertex_cones': compute_vertex_cones, 'smith_form': smith_form, 'dualization': dualization, 'triangulation': triangulation, 'triangulation_max_height': triangulation_max_height }) for key, value in kwds.items(): if value is None or value is False: continue key = key.replace('_', '-') if value is True: args.append('--{}'.format(key)) else: args.append('--{}={}'.format(key, value)) args += ['--cdd', '/dev/stdin'] try: # The cwd argument is needed because latte # always produces diagnostic output files. latte_proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=(None if verbose else PIPE), cwd=str(SAGE_TMP)) except OSError: from sage.misc.package import PackageNotFoundError raise PackageNotFoundError('latte_int') ans, err = latte_proc.communicate(ine) ret_code = latte_proc.poll() if ret_code: if err is None: err = ", see error message above" else: err = ":\n" + err raise RuntimeError( "LattE integrale failed with exit code {} to execute {}". format(ret_code, ' '.join(args)) + err.strip()) p = ans.splitlines()[-2] return R(p)
def numerical_points(self, F=None, **kwds): """ Return some or all numerical approximations of rational points of a projective scheme. This is for dimension 0 subschemes only and the points are determined through a groebner calculation over the base ring and then numerically approximating the roots of the resulting polynomials. If the base ring is a number field, the embedding into ``F`` must be known. INPUT: ``F`` - numerical ring kwds: - ``point_tolerance`` - positive real number (optional, default=10^(-10)). For numerically inexact fields, two points are considered the same if their coordinates are within tolerance. - ``zero_tolerance`` - positive real number (optional, default=10^(-10)). For numerically inexact fields, points are on the subscheme if they satisfy the equations to within tolerance. OUTPUT: A list of points in the ambient space. .. WARNING:: For numerically inexact fields the list of points returned may contain repeated or be missing points due to tolerance. EXAMPLES:: sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: E = P.subscheme([y^3 - x^3 - x*z^2, x*y*z]) sage: L = E(QQ).numerical_points(F=RR); L [(0.000000000000000 : 0.000000000000000 : 1.00000000000000), (1.00000000000000 : 1.00000000000000 : 0.000000000000000)] sage: L[0].codomain() Projective Space of dimension 2 over Real Field with 53 bits of precision :: sage: S.<a> = QQ[] sage: K.<v> = NumberField(a^5 - 7, embedding=CC((7)**(1/5))) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: X = P.subscheme([x^2 - v^2*z^2, y-v*z]) sage: len(X(K).numerical_points(F=CDF)) 2 :: sage: P.<x1, x2, x3> = ProjectiveSpace(QQ, 2) sage: E = P.subscheme([3000*x1^50 + 9875643*x2^2*x3^48 + 12334545*x2^50, x1 + x2]) sage: len(E(P.base_ring()).numerical_points(F=CDF, zero_tolerance=1e-6)) 49 TESTS:: sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: E = P.subscheme([y^3 - x^3 - x*z^2, x*y*z]) sage: E(QQ).numerical_points(F=CDF, point_tolerance=-1) Traceback (most recent call last): ... ValueError: tolerance must be positive :: sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: E = P.subscheme([y^3 - x^3 - x*z^2, x*y*z]) sage: E(QQ).numerical_points(F=CC, zero_tolerance=-1) Traceback (most recent call last): ... ValueError: tolerance must be positive :: sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: E = P.subscheme([y^3 - x^3 - x*z^2, x*y*z]) sage: E(QQ).numerical_points(F=QQbar) Traceback (most recent call last): ... TypeError: F must be a numerical field """ from sage.schemes.projective.projective_space import is_ProjectiveSpace if F is None: F = CC if F not in Fields() or not hasattr(F, 'precision'): raise TypeError('F must be a numerical field') X = self.codomain() if X.base_ring() not in NumberFields(): raise TypeError('base ring must be a number field') PP = X.ambient_space().change_ring(F) if not is_ProjectiveSpace(X) and X.base_ring() in Fields(): #Then it must be a subscheme dim_ideal = X.defining_ideal().dimension() if dim_ideal < 1: # no points return [] if dim_ideal == 1: # if X zero-dimensional pt_tol = RR(kwds.pop('point_tolerance', 10**(-10))) zero_tol = RR(kwds.pop('zero_tolerance', 10**(-10))) if pt_tol <= 0 or zero_tol <= 0: raise ValueError("tolerance must be positive") rat_points = set() PS = X.ambient_space() N = PS.dimension_relative() BR = X.base_ring() #need a lexicographic ordering for elimination R = PolynomialRing(BR, N + 1, PS.variable_names(), order='lex') RF = R.change_ring(F) I = R.ideal(X.defining_polynomials()) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. for k in range(N + 1): #create the elimination ideal for the kth affine patch G = I.substitute({R.gen(k): 1}).groebner_basis() G = [RF(g) for g in G] if G != [1]: P = {} #keep track that we know the kth coordinate is 1 P.update({RF.gen(k): 1}) points = [P] #work backwards from solving each equation for the possible #values of the next coordinate for i in range(len(G) - 1, -1, -1): new_points = [] good = 0 for P in points: #substitute in our dictionary entry that has the values #of coordinates known so far. This results in a single #variable polynomial (by elimination) L = G[i].substitute(P) if len(RF(L).variables()) == 1: for pol in L.univariate_polynomial().roots( ring=F, multiplicities=False): r = L.variables()[0] varindex = RF.gens().index(r) P.update({RF.gen(varindex): pol}) new_points.append(copy(P)) good = 1 else: new_points.append(P) good = 1 if good: points = new_points #the dictionary entries now have values for all coordinates #they are approximate solutions to the equations #make them into projective points polys = [ g.change_ring(F) for g in X.defining_polynomials() ] for i in range(len(points)): if len(points[i]) == N + 1: S = PP([ points[i][RF.gen(j)] for j in range(N + 1) ]) S.normalize_coordinates() if all(g(list(S)) < zero_tol for g in polys): rat_points.add(S) # remove duplicate element using tolerance #since they are normalized we can just compare coefficients dupl_points = list(rat_points) for i in range(len(dupl_points)): u = dupl_points[i] for j in range(i + 1, len(dupl_points)): v = dupl_points[j] if all((u[k] - v[k]).abs() < pt_tol for k in range(len(u))): rat_points.remove(u) break rat_points = sorted(rat_points) return rat_points raise NotImplementedError( 'numerical approximation of points only for dimension 0 subschemes' )
def matching_polynomial(self, algorithm="Godsil", name=None): r""" Computes the matching polynomial. If `p(G, k)` denotes the number of `k`-matchings (matchings with `k` edges) in `G`, then the *matching polynomial* is defined as [Godsil93]_: .. MATH:: \mu(x)=\sum_{k \geq 0} (-1)^k p(G,k) x^{n-2k} INPUT: - ``algorithm`` - a string which must be either "Godsil" (default) or "rook"; "rook" is usually faster for larger graphs. - ``name`` - optional string for the variable name in the polynomial. EXAMPLE:: sage: BipartiteGraph(graphs.CubeGraph(3)).matching_polynomial() x^8 - 12*x^6 + 42*x^4 - 44*x^2 + 9 :: sage: x = polygen(QQ) sage: g = BipartiteGraph(graphs.CompleteBipartiteGraph(16, 16)) sage: bool(factorial(16)*laguerre(16,x^2) == g.matching_polynomial(algorithm='rook')) True Compute the matching polynomial of a line with `60` vertices:: sage: from sage.functions.orthogonal_polys import chebyshev_U sage: g = next(graphs.trees(60)) sage: chebyshev_U(60, x/2) == BipartiteGraph(g).matching_polynomial(algorithm='rook') True The matching polynomial of a tree graphs is equal to its characteristic polynomial:: sage: g = graphs.RandomTree(20) sage: p = g.characteristic_polynomial() sage: p == BipartiteGraph(g).matching_polynomial(algorithm='rook') True TESTS:: sage: g = BipartiteGraph(matrix.ones(4,3)) sage: g.matching_polynomial() x^7 - 12*x^5 + 36*x^3 - 24*x sage: g.matching_polynomial(algorithm="rook") x^7 - 12*x^5 + 36*x^3 - 24*x """ if algorithm == "Godsil": return Graph.matching_polynomial(self, complement=False, name=name) elif algorithm == "rook": from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing A = self.reduced_adjacency_matrix() a = A.rook_vector() m = A.nrows() n = A.ncols() b = [0]*(m + n + 1) for i in range(min(m, n) + 1): b[m + n - 2*i] = a[i]*(-1)**i if name is None: name = 'x' K = PolynomialRing(A.base_ring(), name) p = K(b) return p else: raise ValueError('algorithm must be one of "Godsil" or "rook".')