def canonical_scheme(self, t=None): """ Return the canonical scheme. This is a scheme that contains this hypergeometric motive in its cohomology. EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: H = Hyp(cyclotomic=([3],[4])) sage: H.gamma_list() [-1, 2, 3, -4] sage: H.canonical_scheme() Spectrum of Quotient of Multivariate Polynomial Ring in X0, X1, Y0, Y1 over Fraction Field of Univariate Polynomial Ring in t over Rational Field by the ideal (X0 + X1 - 1, Y0 + Y1 - 1, (-t)*X0^2*X1^3 + 27/64*Y0*Y1^4) sage: H = Hyp(gamma_list=[-2, 3, 4, -5]) sage: H.canonical_scheme() Spectrum of Quotient of Multivariate Polynomial Ring in X0, X1, Y0, Y1 over Fraction Field of Univariate Polynomial Ring in t over Rational Field by the ideal (X0 + X1 - 1, Y0 + Y1 - 1, (-t)*X0^3*X1^4 + 1728/3125*Y0^2*Y1^5) REFERENCES: [Kat1991]_, section 5.4 """ if t is None: t = FractionField(QQ['t']).gen() basering = t.parent() gamma_pos = [u for u in self.gamma_list() if u > 0] gamma_neg = [u for u in self.gamma_list() if u < 0] N_pos = len(gamma_pos) N_neg = len(gamma_neg) varX = ['X{}'.format(i) for i in range(N_pos)] varY = ['Y{}'.format(i) for i in range(N_neg)] ring = PolynomialRing(basering, varX + varY) gens = ring.gens() X = gens[:N_pos] Y = gens[N_pos:] eq0 = ring.sum(X) - 1 eq1 = ring.sum(Y) - 1 eq2_pos = ring.prod(X[i] ** gamma_pos[i] for i in range(N_pos)) eq2_neg = ring.prod(Y[j] ** -gamma_neg[j] for j in range(N_neg)) ideal = ring.ideal([eq0, eq1, self.M_value() * eq2_neg - t * eq2_pos]) return Spec(ring.quotient(ideal))
class PetersonPolynomials(SageObject): """ A class that computes the Peterson polynomials and their expression as Dickson polynomials. """ def __init__(self, p, n): """ TESTS:: sage: from yacop.modules.dickson import PetersonPolynomials sage: P=PetersonPolynomials(3,2) ; P Peterson element factory for prime 3, index 2 sage: for idx in range(20): ....: print("%-3d : %s" % (idx,P.omega(idx))) 0 : 1 1 : x1^2 + x2^2 2 : x1^4 + x1^2*x2^2 + x2^4 3 : x1^6 + x1^4*x2^2 + x1^2*x2^4 + x2^6 4 : x1^6*x2^2 + x1^4*x2^4 + x1^2*x2^6 5 : -x1^10 + x1^6*x2^4 + x1^4*x2^6 - x2^10 6 : x1^12 - x1^10*x2^2 + x1^6*x2^6 - x1^2*x2^10 + x2^12 7 : x1^12*x2^2 - x1^10*x2^4 - x1^4*x2^10 + x1^2*x2^12 8 : x1^12*x2^4 - x1^10*x2^6 - x1^6*x2^10 + x1^4*x2^12 9 : x1^18 + x1^12*x2^6 + x1^6*x2^12 + x2^18 10 : x1^18*x2^2 + x1^10*x2^10 + x1^2*x2^18 11 : x1^18*x2^4 - x1^12*x2^10 - x1^10*x2^12 + x1^4*x2^18 12 : x1^18*x2^6 + x1^12*x2^12 + x1^6*x2^18 13 : 0 14 : x1^28 - x1^18*x2^10 - x1^10*x2^18 + x2^28 15 : -x1^30 + x1^28*x2^2 + x1^18*x2^12 + x1^12*x2^18 + x1^2*x2^28 - x2^30 16 : -x1^30*x2^2 + x1^28*x2^4 + x1^4*x2^28 - x1^2*x2^30 17 : -x1^30*x2^4 + x1^28*x2^6 + x1^6*x2^28 - x1^4*x2^30 18 : x1^36 - x1^30*x2^6 + x1^18*x2^18 - x1^6*x2^30 + x2^36 19 : x1^36*x2^2 - x1^28*x2^10 - x1^10*x2^28 + x1^2*x2^36 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from yacop.modules.classifying_spaces import BZpGeneric from sage.categories.tensor import tensor self.p = p self.n = n self.B = BZpGeneric(p) factors = tuple([ self.B, ] * n) self.BT = tensor(factors) self.tc = self.BT.tensor_constructor(factors) deg = -1 fac = 2 # if p>2 else 1 self.b = self.B.monomial(fac * deg) self.binv = self.B.monomial(-fac * deg) self.bt = tensor([ self.b, ] * n) self.bti = tensor([ self.binv, ] * n) self.P = SteenrodAlgebra(p, generic=True).P self.Q = PolynomialRing( GF(p), ["x%d" % (i + 1) for i in range(n)] + [ "t", ], ) def _repr_(self): return "Peterson element factory for prime %d, index %d" % (self.p, self.n) @cached_method def dicksons(self): """ TESTS:: sage: from yacop.modules.dickson import PetersonPolynomials sage: P=PetersonPolynomials(2,3) sage: for p in P.dicksons(): ....: print(p) x1^4*x2^2*x3 + x1^2*x2^4*x3 + x1^4*x2*x3^2 + x1*x2^4*x3^2 + x1^2*x2*x3^4 + x1*x2^2*x3^4 x1^4*x2^2 + x1^2*x2^4 + x1^4*x2*x3 + x1*x2^4*x3 + x1^4*x3^2 + x1^2*x2^2*x3^2 + x2^4*x3^2 + x1^2*x3^4 + x1*x2*x3^4 + x2^2*x3^4 x1^4 + x1^2*x2^2 + x2^4 + x1^2*x2*x3 + x1*x2^2*x3 + x1^2*x3^2 + x1*x2*x3^2 + x2^2*x3^2 + x3^4 sage: P=PetersonPolynomials(5,2) sage: for p in P.dicksons(): ....: print(p) x1^20*x2^4 + x1^16*x2^8 + x1^12*x2^12 + x1^8*x2^16 + x1^4*x2^20 x1^20 + x1^16*x2^4 + x1^12*x2^8 + x1^8*x2^12 + x1^4*x2^16 + x2^20 """ xi = self.Q.gens()[:-1] t = self.Q.gens()[self.n] pol = self.Q.prod(t + self.Q.sum(a * x for (a, x) in zip(cf, xi)) for cf in GF(self.p)**self.n) return [ pol.coefficient(t**(self.p**i)) * (-1)**(self.n - i) for i in range(self.n) ] def is_invariant(self, qelem): """ TESTS:: sage: from yacop.modules.dickson import PetersonPolynomials sage: P=PetersonPolynomials(2,3) sage: [P.is_invariant(d) for d in P.dicksons()] [True, True, True] sage: [(n,P.is_invariant(P.omega(n))) for n in range(10)] [(0, True), (1, False), (2, False), (3, False), (4, True), (5, False), (6, True), (7, True), (8, True), (9, False)] """ if self.n == 1: return True Q = self.Q x1, x2 = Q.gens()[0:2] # we already know the element is symmetric in the variables # so we only check invariance under x1 -> x1+x2 return (qelem - qelem.subs({x1: x1 + x2})).is_zero() # def toQ(self, x): return self.Q.sum(cf * self.Q.prod(a**(e >> 1) for (a, e) in zip(self.Q.gens(), expo)) for (expo, cf) in x) def opelem(self, op): return self.toQ((op * self.bt) * self.bti) def opconjelem(self, op): return self.toQ((op % self.bt) * self.bti) @cached_method def omega(self, n): return self._omega_fast(n) def _omega_safe(self, n): return self.opelem(self.P(n).antipode()) def _omega_exponents(self, sum, len): if len == 1: cf = self.gamma(sum) if cf: yield ( cf, [ sum, ], ) return if len > 1: for smd in range(sum + 1): cf = self.gamma(smd) if cf: for (c, x) in self._omega_exponents(sum - smd, len - 1): yield ( cf * c, [ smd, ] + x, ) def _omega_fast(self, n): """ TESTS:: sage: from yacop.modules.dickson import PetersonPolynomials sage: P=PetersonPolynomials(5,3) sage: for n in range(25,31): ....: assert P._omega_safe(n) == P._omega_fast(n) sage: P=PetersonPolynomials(2,4) sage: for n in range(10,13): ....: assert P._omega_safe(n) == P._omega_fast(n) """ from sage.combinat.integer_vector_weighted import WeightedIntegerVectors from sage.misc.misc_c import prod pmo = self.p - 1 ans = [] for (cf, expos) in self._omega_exponents(n, self.n): if 0 == cf % self.p: continue ans.append(cf * self.Q.prod(a**(pmo * e) for (a, e) in zip(self.Q.gens(), expos))) return self.Q.sum(ans) @staticmethod def _milnor_basis(p, n): from sage.algebras.steenrod.steenrod_algebra_bases import milnor_basis if p == 2: for x in milnor_basis(n, 2): yield x else: for (x, y) in milnor_basis(n, p): if len(x) == 0: yield y def gamma(self, n): """ the gamma(n) in the definition of omega(n) """ return binom_modp(self.p, -(self.p - 1) * n, n) @staticmethod def decompose(p, n): """ Decompose a omega_n into a product of Dicksonians """ inv = p**(p - 2) tdeg = 2 * (p - 1) if p > 2 else 1 pmo = p - 1 # FIXME: why does this not depend on the generic/non-generic flag for p=2? for expos in PetersonPolynomials._milnor_basis(p, tdeg * n): sum = 0 cf = 1 for a in expos: sum += a cf *= binom_modp(p, sum, a) if 0 == cf: break if 0 == cf: continue cf *= binom_modp(p, -n * pmo, sum) if 0 != cf: yield [cf, expos]