Exemplo n.º 1
0
    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))
    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))
Exemplo n.º 3
0
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]