Beispiel #1
0
def normalize_coefficients(self, c):
    r"""
    If our coefficient ring is the field of fractions over a univariate
    polynomial ring over the rationals, then we should clear both the
    numerator and denominator of the denominators of their
    coefficients.

    INPUT:

    - ``self`` -- a Jack basis of the symmetric functions
    - ``c`` -- a coefficient in the base ring of ``self``

    OUTPUT:

    - divide numerator and denominator by the greatest common divisor

    EXAMPLES::

        sage: JP = SymmetricFunctions(FractionField(QQ['t'])).jack().P()
        sage: t = JP.base_ring().gen()
        sage: a = 2/(1/2*t+1/2)
        sage: JP._normalize_coefficients(a)
        4/(t + 1)
        sage: a = 1/(1/3+1/6*t)
        sage: JP._normalize_coefficients(a)
        6/(t + 2)
        sage: a = 24/(4*t^2 + 12*t + 8)
        sage: JP._normalize_coefficients(a)
        6/(t^2 + 3*t + 2)
    """
    BR = self.base_ring()
    if is_FractionField(BR) and BR.base_ring() == QQ:
        denom = c.denominator()
        numer = c.numerator()

        # Clear the denominators
        a = lcm([i.denominator() for i in denom.coefficients(sparse=False)])
        b = lcm([i.denominator() for i in numer.coefficients(sparse=False)])
        l = Integer(a).lcm(Integer(b))
        denom *= l
        numer *= l

        # Divide through by the gcd of the numerators
        a = gcd([i.numerator() for i in denom.coefficients(sparse=False)])
        b = gcd([i.numerator() for i in numer.coefficients(sparse=False)])
        l = Integer(a).gcd(Integer(b))

        denom = denom // l
        numer = numer // l

        return c.parent()(numer, denom)
    else:
        return c
Beispiel #2
0
    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]
Beispiel #3
0
    def ncusps(self):
        r"""
        Return the number of orbits of cusps (regular or otherwise) for this subgroup.

        EXAMPLE::

            sage: GammaH(33,[2]).ncusps()
            8
            sage: GammaH(32079, [21676]).ncusps()
            28800

        AUTHORS:

        - Jordi Quer

        """
        N = self.level()
        H = self._list_of_elements_in_H()
        c = ZZ(0)
        for d in [d for d in N.divisors() if d**2 <= N]:
            Nd = lcm(d,N//d)
            Hd = set([x % Nd for x in H])
            lenHd = len(Hd)
            if Nd-1 not in Hd: lenHd *= 2
            summand = euler_phi(d)*euler_phi(N//d)//lenHd
            if d**2 == N:
                c = c + summand
            else:
                c = c + 2*summand
        return c
Beispiel #4
0
    def _make_monic(self, f):
        r"""
        Let alpha be a root of f.  This function returns a monic
        integral polynomial g and an element d of the base field such
        that g(alpha*d) = 0.
        
        EXAMPLES::
        
            sage: K.<x> = FunctionField(QQ); R.<y> = K[];
            sage: L.<alpha> = K.extension(x^2*y^5 - 1/x)
            sage: g, d = L._make_monic(L.polynomial()); g,d
            (y^5 - x^12, x^3)
            sage: g(alpha*d)
            0
        """
        R = f.base_ring()
        if not isinstance(R, RationalFunctionField):
            raise NotImplementedError
        
        # make f monic
        n = f.degree()
        c = f.leading_coefficient()
        if c != 1:
            f = f / c

        # find lcm of denominators
        # would be good to replace this by minimal...
        d = lcm([b.denominator() for b in f.list() if b])
        if d != 1:
            x = f.parent().gen()
            g = (d**n) * f(x/d)
        else:
            g = f
        return g, d
Beispiel #5
0
def bw_shift_rec(dop, shift=ZZ.zero()):
    Scalars = utilities.mypushout(dop.base_ring().base_ring(), shift.parent())
    if dop.parent().is_D():
        dop = DifferentialOperator(dop)  # compatibility bugware
        rop = dop._my_to_S()
    else:  # more compatibility bugware
        Pols_n = PolynomialRing(dop.base_ring().base_ring(), 'n')
        rop = dop.to_S(ore_algebra.OreAlgebra(Pols_n, 'Sn'))
    Pols_n, n = rop.base_ring().change_ring(Scalars).objgen()
    Rops = ore_algebra.OreAlgebra(Pols_n, 'Sn')
    ordrec = rop.order()
    rop = Rops([p(n - ordrec + shift) for p in rop])
    # Clear_denominators
    den = lcm([p.denominator() for p in rop])
    rop = den * rop
    # Remove constant common factors to make the recurrence smaller
    if Scalars is QQ:
        g = gcd(c for p in rop for c in p)
    # elif utilities.is_QQi(Scalars): # XXX: too slow (and not general enough)
    #     ZZi = Scalars.maximal_order()
    #     g = ZZi.zero()
    #     for c in (c1 for p in rop for c1 in p):
    #         g = ZZi(g).gcd(ZZi(c)) # gcd returns a nfe
    #         if g.is_one():
    #             g = None
    #             break
    else:
        g = None
    if g is not None:
        rop = (1 / g) * rop
    coeff = [rop[ordrec - k] for k in range(ordrec + 1)]
    return BwShiftRec(coeff)
Beispiel #6
0
    def generalised_level(self):
        r"""
        Return the generalised level of self, i.e. the least common multiple of
        the widths of all cusps.

        If self is *even*, Wohlfart's theorem tells us that this is equal to
        the (conventional) level of self when self is a congruence subgroup.
        This can fail if self is odd, but the actual level is at most twice the
        generalised level. See the paper by Kiming, Schuett and Verrill for
        more examples.

        EXAMPLE::

            sage: Gamma0(18).generalised_level()
            18
            sage: sage.modular.arithgroup.arithgroup_perm.HsuExample18().generalised_level()
            24

        In the following example, the actual level is twice the generalised
        level. This is the group `G_2` from Example 17 of K-S-V.

        ::

            sage: G = CongruenceSubgroup(8, [ [1,1,0,1], [3,-1,4,-1] ])
            sage: G.level()
            8
            sage: G.generalised_level()
            4
        """
        return arith.lcm([self.cusp_width(c) for c in self.cusps()])
    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]
Beispiel #8
0
        def dim_RR(M):
            den = lcm([e.denominator() for e in M.list()])
            mat = matrix(R, M.nrows(),
                         [(den * e).numerator() for e in M.list()])

            # initialise pivot_row and conflicts list
            pivot_row = [[] for i in range(n)]
            conflicts = []
            for i in range(n):
                bestp = -1
                best = -1
                for c in range(n):
                    d = mat[i, c].degree()
                    if d >= best:
                        bestp = c
                        best = d

                if best >= 0:
                    pivot_row[bestp].append((i, best))
                    if len(pivot_row[bestp]) > 1:
                        conflicts.append(bestp)

            # while there is a conflict, do a simple transformation
            while conflicts:
                c = conflicts.pop()
                row = pivot_row[c]
                i, ideg = row.pop()
                j, jdeg = row.pop()

                if jdeg > ideg:
                    i, j = j, i
                    ideg, jdeg = jdeg, ideg

                coeff = -mat[i, c].lc() / mat[j, c].lc()
                s = coeff * one.shift(ideg - jdeg)

                mat.add_multiple_of_row(i, j, s)

                row.append((j, jdeg))

                bestp = -1
                best = -1
                for c in range(n):
                    d = mat[i, c].degree()
                    if d >= best:
                        bestp = c
                        best = d

                if best >= 0:
                    pivot_row[bestp].append((i, best))
                    if len(pivot_row[bestp]) > 1:
                        conflicts.append(bestp)

            dim = 0
            for j in range(n):
                i, ideg = pivot_row[j][0]
                k = den.degree() - ideg + 1
                if k > 0:
                    dim += k
            return dim
Beispiel #9
0
    def generalised_level(self):
        r"""
        Return the generalised level of self, i.e. the least common multiple of
        the widths of all cusps.

        If self is *even*, Wohlfart's theorem tells us that this is equal to
        the (conventional) level of self when self is a congruence subgroup.
        This can fail if self is odd, but the actual level is at most twice the
        generalised level. See the paper by Kiming, Schuett and Verrill for
        more examples.

        EXAMPLE::

            sage: Gamma0(18).generalised_level()
            18
            sage: sage.modular.arithgroup.arithgroup_perm.HsuExample18().generalised_level()
            24

        In the following example, the actual level is twice the generalised
        level. This is the group `G_2` from Example 17 of K-S-V.

        ::

            sage: G = CongruenceSubgroup(8, [ [1,1,0,1], [3,-1,4,-1] ])
            sage: G.level()
            8
            sage: G.generalised_level()
            4
        """
        return arith.lcm([self.cusp_width(c) for c in self.cusps()])
def to_trivariate_polynomial(K, f):
    r"""
    Convert ``f`` from a univariate polynomial over K into a trivariate
    polynomial and a denominator.

    INPUT:

    - ``K`` -- a function field
    - ``f`` -- univariate polynomial over K

    OUTPUT:

    - trivariate polynomial F, denominator d

    Here `F` is a polynomial in `k[x, y, z]` and d a polynomial in `k[x]`.

    """
    v = [to_bivariate_polynomial(K, c) for c in f.list()]
    denom = lcm([b for a, b in v])
    S = denom.parent()
    k = S.base_ring()
    z, y, x = k['%s,%s,%s'%(f.parent().variable_name(), K.variable_name(),
                K.base_field().variable_name())].gens()
    F = z.parent().zero()
    for i in range(len(v)):
        a, b = v[i]
        F += (denom(x) * a(y, x) / b(x)).numerator() * z**i
    d = denom(x)
    return F, d
Beispiel #11
0
def __common_minimal_basering(chi, psi):
    """
    Find the smallest basering over which chi and psi are valued, and
    return new chi and psi valued in that ring.

    EXAMPLES::

        sage: sage.modular.modform.eis_series.__common_minimal_basering(DirichletGroup(1)[0], DirichletGroup(1)[0])
        (Dirichlet character modulo 1 of conductor 1, Dirichlet character modulo 1 of conductor 1)

        sage: sage.modular.modform.eis_series.__common_minimal_basering(DirichletGroup(3).0, DirichletGroup(5).0)
        (Dirichlet character modulo 3 of conductor 3 mapping 2 |--> -1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> zeta4)

        sage: sage.modular.modform.eis_series.__common_minimal_basering(DirichletGroup(12).0, DirichletGroup(36).0)
        (Dirichlet character modulo 12 of conductor 4 mapping 7 |--> -1, 5 |--> 1, Dirichlet character modulo 36 of conductor 4 mapping 19 |--> -1, 29 |--> 1)
    """
    chi = chi.minimize_base_ring()
    psi = psi.minimize_base_ring()
    n = lcm(chi.base_ring().zeta().multiplicative_order(),
                  psi.base_ring().zeta().multiplicative_order())
    if n <= 2:
        K = QQ
    else:
        K = CyclotomicField(n)
    chi = chi.change_ring(K)
    psi = psi.change_ring(K)
    return chi, psi
Beispiel #12
0
def __common_minimal_basering(chi, psi):
    """
    Find the smallest basering over which chi and psi are valued, and
    return new chi and psi valued in that ring.

    EXAMPLES::

        sage: sage.modular.modform.eis_series.__common_minimal_basering(DirichletGroup(1)[0], DirichletGroup(1)[0])
        (Dirichlet character modulo 1 of conductor 1, Dirichlet character modulo 1 of conductor 1)

        sage: sage.modular.modform.eis_series.__common_minimal_basering(DirichletGroup(3).0, DirichletGroup(5).0)
        (Dirichlet character modulo 3 of conductor 3 mapping 2 |--> -1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> zeta4)

        sage: sage.modular.modform.eis_series.__common_minimal_basering(DirichletGroup(12).0, DirichletGroup(36).0)
        (Dirichlet character modulo 12 of conductor 4 mapping 7 |--> -1, 5 |--> 1, Dirichlet character modulo 36 of conductor 4 mapping 19 |--> -1, 29 |--> 1)
    """
    chi = chi.minimize_base_ring()
    psi = psi.minimize_base_ring()
    n = lcm(chi.base_ring().zeta().multiplicative_order(),
                  psi.base_ring().zeta().multiplicative_order())
    if n <= 2:
        K = QQ
    else:
        K = CyclotomicField(n)
    chi = chi.change_ring(K)
    psi = psi.change_ring(K)
    return chi, psi
Beispiel #13
0
    def ncusps(self):
        r"""
        Return the number of orbits of cusps (regular or otherwise) for this subgroup.

        EXAMPLES::

            sage: GammaH(33,[2]).ncusps()
            8
            sage: GammaH(32079, [21676]).ncusps()
            28800

        AUTHORS:

        - Jordi Quer

        """
        N = self.level()
        H = self._list_of_elements_in_H()
        c = ZZ(0)
        for d in [d for d in N.divisors() if d**2 <= N]:
            Nd = lcm(d, N // d)
            Hd = set([x % Nd for x in H])
            lenHd = len(Hd)
            if Nd - 1 not in Hd: lenHd *= 2
            summand = euler_phi(d) * euler_phi(N // d) // lenHd
            if d**2 == N:
                c = c + summand
            else:
                c = c + 2 * summand
        return c
Beispiel #14
0
    def _make_monic(self, f):
        r"""
        Let alpha be a root of f.  This function returns a monic
        integral polynomial g and an element d of the base field such
        that g(alpha*d) = 0.
        
        EXAMPLES::
        
            sage: K.<x> = FunctionField(QQ); R.<y> = K[];
            sage: L.<alpha> = K.extension(x^2*y^5 - 1/x)
            sage: g, d = L._make_monic(L.polynomial()); g,d
            (y^5 - x^12, x^3)
            sage: g(alpha*d)
            0
        """
        R = f.base_ring()
        if not isinstance(R, RationalFunctionField):
            raise NotImplementedError
        
        # make f monic
        n = f.degree()
        c = f.leading_coefficient()
        if c != 1:
            f = f / c

        # find lcm of denominators
        # would be good to replace this by minimal...
        d = lcm([b.denominator() for b in f.list() if b])
        if d != 1:
            x = f.parent().gen()
            g = (d**n) * f(x/d)
        else:
            g = f
        return g, d
Beispiel #15
0
def find_gens_list(klist, r = 0, bound = None, verbose = True):
    """
    Use an elliptic curve variation of Rain's method to find a generator
    of a subfield of degree r within the ambient fields in klist.
    """
    p = klist[0].characteristic()
    ngcd = gcd([k.degree() for k in klist])
    nlcm = lcm([k.degree() for k in klist])
    if r == 0:
        r = ngcd
    assert all(k.degree() % r == 0 for k in klist)

    # This might be useful if elliptic curves defined over an 
    # extension of F_p are used.
    kb = k.base_ring()

    # List of candidates for l.
    lT = find_l(kb, r, bound)
    if lT is None:
        raise RuntimeError, "no suitable l found"

    # Find an elliptic curve with the given trace. 
    E = find_elliptic_curve(kb, lT)
    if E is None:
        raise RuntimeError, "no suitable elliptic curve found"

    return tuple(find_unique_orbit(E.change_ring(k), E.cardinality(extension_degree=k.degree()), lT[0], r) for k in klist)
Beispiel #16
0
    def primitive(self, signed=True):
        """
        Return hyperplane defined by primitive equation.

        INPUT:

        - ``signed`` -- boolean (optional, default: ``True``); whether
          to preserve the overall sign

        OUTPUT:

        Hyperplane whose linear expression has common factors and
        denominators cleared. That is, the same hyperplane (with the
        same sign) but defined by a rescaled equation. Note that
        different linear expressions must define different hyperplanes
        as comparison is used in caching.

        If ``signed``, the overall rescaling is by a positive constant
        only.

        EXAMPLES::

            sage: H.<x,y> = HyperplaneArrangements(QQ)
            sage: h = -1/3*x + 1/2*y - 1;  h
            Hyperplane -1/3*x + 1/2*y - 1
            sage: h.primitive()
            Hyperplane -2*x + 3*y - 6
            sage: h == h.primitive()
            False
            sage: (4*x + 8).primitive()
            Hyperplane x + 0*y + 2

            sage: (4*x - y - 8).primitive(signed=True)   # default
            Hyperplane 4*x - y - 8
            sage: (4*x - y - 8).primitive(signed=False)
            Hyperplane -4*x + y + 8
        """
        from sage.arith.all import lcm, gcd

        coeffs = self.coefficients()
        try:
            d = lcm([x.denom() for x in coeffs])
            n = gcd([x.numer() for x in coeffs])
        except AttributeError:
            return self
        if not signed:
            for x in coeffs:
                if x > 0:
                    break
                if x < 0:
                    d = -d
                    break
        parent = self.parent()
        d = parent.base_ring()(d)
        n = parent.base_ring()(n)
        if n == 0:
            n = parent.base_ring().one()
        return parent(self * d / n)
 def __init__(self, dop):
     if not dop:
         raise ValueError("operator must be nonzero")
     if not dop.parent().is_D():
         raise ValueError("expected an operator in K(x)[D]")
     _, _, _, dop = dop.numerator()._normalize_base_ring()
     den = lcm(c.denominator() for c in dop)
     dop *= den
     super(PlainDifferentialOperator, self).__init__(dop.parent(), dop)
Beispiel #18
0
    def primitive(self, signed=True):
        """
        Return hyperplane defined by primitive equation.

        INPUT:

        - ``signed`` -- boolean (optional, default: ``True``); whether
          to preserve the overall sign

        OUTPUT:

        Hyperplane whose linear expression has common factors and
        denominators cleared. That is, the same hyperplane (with the
        same sign) but defined by a rescaled equation. Note that
        different linear expressions must define different hyperplanes
        as comparison is used in caching.

        If ``signed``, the overall rescaling is by a positive constant
        only.

        EXAMPLES::

            sage: H.<x,y> = HyperplaneArrangements(QQ)
            sage: h = -1/3*x + 1/2*y - 1;  h
            Hyperplane -1/3*x + 1/2*y - 1
            sage: h.primitive()
            Hyperplane -2*x + 3*y - 6
            sage: h == h.primitive()
            False
            sage: (4*x + 8).primitive()
            Hyperplane x + 0*y + 2

            sage: (4*x - y - 8).primitive(signed=True)   # default
            Hyperplane 4*x - y - 8
            sage: (4*x - y - 8).primitive(signed=False)
            Hyperplane -4*x + y + 8
        """
        from sage.arith.all import lcm, gcd
        coeffs = self.coefficients()
        try:
            d = lcm([x.denom() for x in coeffs])
            n = gcd([x.numer() for x in coeffs])
        except AttributeError:
            return self
        if not signed:
            for x in coeffs:
                if x > 0:
                    break
                if x < 0:
                    d = -d
                    break
        parent = self.parent()
        d = parent.base_ring()(d)
        n = parent.base_ring()(n)
        if n == 0:
            n = parent.base_ring().one()
        return parent(self * d / n)
Beispiel #19
0
 def arith_prod_of_partitions(l1, l2):
     # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by
     # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes
     # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2`
     # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a
     # partition of `nm`.
     term_iterable = chain.from_iterable(repeat(lcm(pair), gcd(pair))
                                         for pair in product(l1, l2))
     return Partition(sorted(term_iterable, reverse=True))
Beispiel #20
0
 def arith_prod_of_partitions(l1, l2):
     # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by
     # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes
     # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2`
     # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a
     # partition of `nm`.
     term_iterable = chain.from_iterable(repeat(lcm(pair), gcd(pair))
                                         for pair in product(l1, l2))
     return Partition(sorted(term_iterable, reverse=True))
    def __add__(self, other):
        if isinstance(other, QExpansion):
            common_param_level = lcm(self.param_level, other.param_level)
            self.pad(common_param_level)
            other.pad(common_param_level)

            return QExpansion(level=self.level,
                              weight=self.weight,
                              series=self.series + other.series,
                              param_level=self.param_level)

        raise ValueError
def eisenstein_series_at_inf(phi, psi, k, prec=10, t=1, base_ring=None):
    r"""
    Return Fourier expansion of Eistenstein series at a cusp.

    INPUT:

    - ``phi`` -- Dirichlet character.
    - ``psi`` -- Dirichlet character.
    - ``k`` -- integer, the weight of the Eistenstein series.
    - ``prec`` -- integer (default: 10).
    - ``t`` -- integer (default: 1).

    OUTPUT:

    The Fourier expansion of the Eisenstein series $E_k^{\phi,\psi, t}$ (as
    defined by [Diamond-Shurman]) at the specific cusp.

    EXAMPLES:
    sage: phi = DirichletGroup(3)[1]
    sage: psi = DirichletGroup(5)[1]
    sage: E = eisenstein_series_at_inf(phi, psi, 4)
    """
    N1, N2 = phi.level(), psi.level()
    N = N1 * N2
    #The Fourier expansion of the Eisenstein series at infinity is in the field Q(zeta_Ncyc)
    Ncyc = lcm([euler_phi(N1), euler_phi(N2)])
    if base_ring == None:
        base_ring = CyclotomicField(Ncyc)
    Q = PowerSeriesRing(base_ring, 'q')
    q = Q.gen()
    s = O(q**prec)

    #Weight 2 with trivial characters is calculated separately
    if k == 2 and phi.conductor() == 1 and psi.conductor() == 1:
        if t == 1:
            raise TypeError('E_2 is not a modular form.')
        s = 1 / 24 * (t - 1)
        for m in srange(1, prec):
            for n in srange(1, prec / m):
                s += n * (q**(m * n) - t * q**(m * n * t))
        return s + O(q**prec)

    if psi.level() == 1 and k == 1:
        s -= phi.bernoulli(k) / k
    elif phi.level() == 1:
        s -= psi.bernoulli(k) / k

    for m in srange(1, prec / t):
        for n in srange(1, prec / t / m + 1):
            s += 2 * base_ring(phi(m)) * base_ring(
                psi(n)) * n**(k - 1) * q**(m * n * t)
    return s + O(q**prec)
Beispiel #23
0
def _heckebasis(M):
    r"""
    Return a basis of the Hecke algebra of M as a ZZ-module.

    INPUT:

    - ``M`` -- a Hecke module

    OUTPUT:

    a list of Hecke algebra elements represented as matrices

    EXAMPLES::

        sage: M = ModularSymbols(11,2,1)
        sage: sage.modular.hecke.algebra._heckebasis(M)
        [Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by:
        [1 0]
        [0 1],
        Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by:
        [0 1]
        [0 5]]
    """
    d = M.rank()
    VV = QQ**(d**2)
    WW = ZZ**(d**2)
    MM = MatrixSpace(QQ, d)
    MMZ = MatrixSpace(ZZ, d)
    S = []
    Denom = []
    B = []
    B1 = []
    for i in range(1, M.hecke_bound() + 1):
        v = M.hecke_operator(i).matrix()
        den = v.denominator()
        Denom.append(den)
        S.append(v)
    den = lcm(Denom)
    for m in S:
        B.append(WW((den * m).list()))
    UU = WW.submodule(B)
    B = UU.basis()
    for u in B:
        u1 = u.list()
        m1 = M.hecke_algebra()(MM(u1), check=False)
        #m1 = MM(u1)
        B1.append((1 / den) * m1)
    return B1
Beispiel #24
0
def _heckebasis(M):
    r"""
    Return a basis of the Hecke algebra of M as a ZZ-module.

    INPUT:

    - ``M`` -- a Hecke module

    OUTPUT:

    a list of Hecke algebra elements represented as matrices

    EXAMPLES::

        sage: M = ModularSymbols(11,2,1)
        sage: sage.modular.hecke.algebra._heckebasis(M)
        [Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by:
        [1 0]
        [0 1],
        Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by:
        [0 1]
        [0 5]]
    """
    d = M.rank()
    VV = QQ**(d**2)
    WW = ZZ**(d**2)
    MM = MatrixSpace(QQ,d)
    MMZ = MatrixSpace(ZZ,d)
    S = []; Denom = []; B = []; B1 = []
    for i in range(1, M.hecke_bound() + 1):
        v = M.hecke_operator(i).matrix()
        den = v.denominator()
        Denom.append(den)
        S.append(v)
    den = lcm(Denom)
    for m in S:
        B.append(WW((den*m).list()))
    UU = WW.submodule(B)
    B = UU.basis()
    for u in B:
        u1 = u.list()
        m1 = M.hecke_algebra()(MM(u1), check=False)
        #m1 = MM(u1)
        B1.append((1/den)*m1)
    return B1
Beispiel #25
0
    def _upper_bound_for_longest_cycle(self, s):
        """
        EXAMPLES::

            sage: cis = species.PartitionSpecies().cycle_index_series()
            sage: cis._upper_bound_for_longest_cycle([4])
            4
            sage: cis._upper_bound_for_longest_cycle([3,1])
            3
            sage: cis._upper_bound_for_longest_cycle([2,2])
            2
            sage: cis._upper_bound_for_longest_cycle([2,1,1])
            2
            sage: cis._upper_bound_for_longest_cycle([1,1,1,1])
            1
        """
        if s == []:
            return 1
        return min(self._card(sum(s)), lcm(list(s)))
    def __mul__(self, other):
        if isinstance(other, QExpansion):
            common_param_level = lcm(self.param_level, other.param_level)
            self.pad(common_param_level)
            other.pad(common_param_level)

            return QExpansion(level=self.level,
                              weight=2 * self.weight,
                              series=self.series * other.series,
                              param_level=self.param_level)

        R = self.series.base_ring()
        if other in R:
            return QExpansion(level=self.level,
                              weight=self.weight,
                              series=other * self.series,
                              param_level=self.param_level)

        raise ValueError
Beispiel #27
0
    def _upper_bound_for_longest_cycle(self, s):
        """
        EXAMPLES::

            sage: cis = species.PartitionSpecies().cycle_index_series()
            sage: cis._upper_bound_for_longest_cycle([4])
            4
            sage: cis._upper_bound_for_longest_cycle([3,1])
            3
            sage: cis._upper_bound_for_longest_cycle([2,2])
            2
            sage: cis._upper_bound_for_longest_cycle([2,1,1])
            2
            sage: cis._upper_bound_for_longest_cycle([1,1,1,1])
            1
        """
        if s == []:
            return 1
        return min(self._card(sum(s)), lcm(list(s)))
Beispiel #28
0
    def exponent(self):
        """
        Return the exponent of this finite abelian group.

        OUTPUT: Integer

        EXAMPLES::

            sage: t = J0(33).hecke_operator(7)
            sage: G = t.kernel()[0]; G
            Finite subgroup with invariants [2, 2, 2, 2, 4, 4] over QQ of Abelian variety J0(33) of dimension 3
            sage: G.exponent()
            4
        """
        try:
            return self.__exponent
        except AttributeError:
            e = lcm(self.invariants())
            self.__exponent = e
            return e
Beispiel #29
0
    def exponent(self):
        """
        Return the exponent of this finite abelian group.

        OUTPUT: Integer

        EXAMPLES::

            sage: t = J0(33).hecke_operator(7)
            sage: G = t.kernel()[0]; G
            Finite subgroup with invariants [2, 2, 2, 2, 4, 4] over QQ of Abelian variety J0(33) of dimension 3
            sage: G.exponent()
            4
        """
        try:
            return self.__exponent
        except AttributeError:
            e = lcm(self.invariants())
            self.__exponent = e
            return e
Beispiel #30
0
    def additive_order(self):
        """
        Return the additive order of this element.

        EXAMPLES::

            sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
            sage: Q = V/W; Q
            Finitely generated module V/W over Integer Ring with invariants (4, 12)
            sage: Q.0.additive_order()
            4
            sage: Q.1.additive_order()
            12
            sage: (Q.0+Q.1).additive_order()
            12
            sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1])
            sage: Q = V/W; Q
            Finitely generated module V/W over Integer Ring with invariants (12, 0)
            sage: Q.0.additive_order()
            12
            sage: type(Q.0.additive_order())
            <class 'sage.rings.integer.Integer'>
            sage: Q.1.additive_order()
            +Infinity
        """
        Q = self.parent()
        I = Q.invariants()
        v = self.vector()

        from sage.rings.infinity import infinity
        from sage.rings.finite_rings.integer_mod import Mod
        from sage.rings.integer import Integer
        from sage.arith.all import lcm
        n = Integer(1)
        for i, a in enumerate(I):
            if a == 0:
                if v[i] != 0:
                    return infinity
            else:
                n = lcm(n, Mod(v[i], a).additive_order())
        return n
Beispiel #31
0
    def clear_denominators(self):
        r"""
        scales by the least common multiple of the denominators.

        OUTPUT: None.

        EXAMPLES::

            sage: R.<t> = PolynomialRing(QQ)
            sage: P.<x,y,z> = ProjectiveSpace(FractionField(R), 2)
            sage: Q = P([t, 3/t^2, 1])
            sage: Q.clear_denominators(); Q
            (t^3 : 3 : t^2)

        ::

            sage: R.<x> = PolynomialRing(QQ)
            sage: K.<w> = NumberField(x^2 - 3)
            sage: P.<x,y,z> = ProjectiveSpace(K, 2)
            sage: Q = P([1/w, 3, 0])
            sage: Q.clear_denominators(); Q
            (w : 9 : 0)

        ::

            sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
            sage: X = P.subscheme(x^2 - y^2);
            sage: Q = X([1/2, 1/2, 1]);
            sage: Q.clear_denominators(); Q
            (1 : 1 : 2)

        ::

            sage: PS.<x,y> = ProjectiveSpace(QQ, 1)
            sage: Q = PS.point([1, 2/3], False); Q
            (1 : 2/3)
            sage: Q.clear_denominators(); Q
            (3 : 2)
        """
        self.scale_by(lcm([t.denominator() for t in self]))
Beispiel #32
0
    def nregcusps(self):
        r"""
        Return the number of orbits of regular cusps for this subgroup. A cusp is regular
        if we may find a parabolic element generating the stabiliser of that
        cusp whose eigenvalues are both +1 rather than -1. If G contains -1,
        all cusps are regular.

        EXAMPLES::

            sage: GammaH(20, [17]).nregcusps()
            4
            sage: GammaH(20, [17]).nirregcusps()
            2
            sage: GammaH(3212, [2045, 2773]).nregcusps()
            1440
            sage: GammaH(3212, [2045, 2773]).nirregcusps()
            720

        AUTHOR:

        - Jordi Quer
        """
        if self.is_even():
            return self.ncusps()

        N = self.level()
        H = self._list_of_elements_in_H()

        c = ZZ(0)
        for d in [d for d in divisors(N) if d**2 <= N]:
            Nd = lcm(d,N//d)
            Hd = set([x%Nd for x in H])
            if Nd - 1 not in Hd:
                summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd))
                if d**2==N:
                    c = c + summand
                else:
                    c = c + 2*summand
        return c
Beispiel #33
0
    def nregcusps(self):
        r"""
        Return the number of orbits of regular cusps for this subgroup. A cusp is regular
        if we may find a parabolic element generating the stabiliser of that
        cusp whose eigenvalues are both +1 rather than -1. If G contains -1,
        all cusps are regular.

        EXAMPLES::

            sage: GammaH(20, [17]).nregcusps()
            4
            sage: GammaH(20, [17]).nirregcusps()
            2
            sage: GammaH(3212, [2045, 2773]).nregcusps()
            1440
            sage: GammaH(3212, [2045, 2773]).nirregcusps()
            720

        AUTHOR:

        - Jordi Quer
        """
        if self.is_even():
            return self.ncusps()

        N = self.level()
        H = self._list_of_elements_in_H()

        c = ZZ(0)
        for d in [d for d in divisors(N) if d**2 <= N]:
            Nd = lcm(d, N // d)
            Hd = set([x % Nd for x in H])
            if Nd - 1 not in Hd:
                summand = euler_phi(d) * euler_phi(N // d) // (2 * len(Hd))
                if d**2 == N:
                    c = c + summand
                else:
                    c = c + 2 * summand
        return c
Beispiel #34
0
    def additive_order(self):
        """
        Return the additive order of this element.

        EXAMPLES::

            sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2])
            sage: Q = V/W; Q
            Finitely generated module V/W over Integer Ring with invariants (4, 12)
            sage: Q.0.additive_order()
            4
            sage: Q.1.additive_order()
            12
            sage: (Q.0+Q.1).additive_order()
            12
            sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1])
            sage: Q = V/W; Q
            Finitely generated module V/W over Integer Ring with invariants (12, 0)
            sage: Q.0.additive_order()
            12
            sage: type(Q.0.additive_order())
            <type 'sage.rings.integer.Integer'>
            sage: Q.1.additive_order()
            +Infinity
        """
        Q = self.parent()
        I = Q.invariants()
        v = self.vector()

        from sage.rings.all import infinity, Mod, Integer
        from sage.arith.all import lcm
        n = Integer(1)
        for i, a in enumerate(I):
            if a == 0:
                if v[i] != 0:
                    return infinity
            else:
                n = lcm(n, Mod(v[i],a).additive_order())
        return n
Beispiel #35
0
def find_gens_list(klist, r=0, omax=Infinity, use_lucas=True, verbose=False):
    '''
    Use Rain's method to find a generator of a subfield of degree r
    within the ambient fields in klist.

    The algorithm is done in two steps:

    1. Find a small integer m, such that the m-th roots of unity
       generate an extension of the subfields, together with some
       additional constraints detailed below. This is done by
       `find_root_order`.

    2. Find a uniquely defined Galois orbit of m-th Gaussian periods.
       Return arbitrary elements of these orbits.
       This is done by `find_unique_orbit`.

    Read the docstrings of `find_root_order` and `find_unique_orbit`
    to find out more on the algorithm.
    '''
    p = klist[0].characteristic()
    ngcd = gcd([k.degree() for k in klist])
    nlcm = lcm([k.degree() for k in klist])
    if r == 0:
        r = ngcd
    assert all(k.degree() % r == 0 for k in klist)

    # Find a suitable m, see doc below
    o, G = find_root_order(p, [ngcd, nlcm], r, verbose=verbose)
    if o > omax:
        raise RuntimeError

    # Construct extension of the fields, if needed
    #
    # Note: `find_unique_orbit` is horribly slow if k is not a
    # field. Some composita tricks would be welcome.
    return tuple(
        find_gen_with_data(k, r, o, G, use_lucas=use_lucas, verbose=verbose)
        for k in klist)
Beispiel #36
0
def define_qexpansions_from_dirichlet_character(p, prec, eps, num_coefficients, magma):
    QQp = Qp(p,prec)
    N = eps.modulus()
    g1qexp = sage_character_to_magma(eps,N=N,magma=magma).ModularForms(1).EisensteinSeries()[1].qExpansion(num_coefficients + 20).Eltseq().sage()  # DEBUG

    den = lcm([QQ(o).denominator() for o in g1qexp])
    g1qexp = [ZZ(den * o) for o in g1qexp]
    print len(g1qexp)

    g0 = ModFormqExp(g1qexp, QQp, weight=1, character = eps, level = N)

    weight = 1
    alpha = 1

    qexp_plus = [QQp(o) for o in g1qexp]
    qexp_minus = [QQp(o) for o in g1qexp]
    for i in range(len(g1qexp) // p):
        qexp_plus[p * i] += g1qexp[i]
        qexp_minus[p * i] -= g1qexp[i]

    gammaplus = ModFormqExp(qexp_plus, QQp, weight=1, level = N)
    gammaminus = ModFormqExp(qexp_minus, QQp, weight=1, level = N)
    return gammaplus, gammaminus, g0
Beispiel #37
0
def find_gens_list(klist, r = 0, omax = Infinity, use_lucas = True, verbose = False):
    '''
    Use Rain's method to find a generator of a subfield of degree r
    within the ambient fields in klist.

    The algorithm is done in two steps:

    1. Find a small integer m, such that the m-th roots of unity
       generate an extension of the subfields, together with some
       additional constraints detailed below. This is done by
       `find_root_order`.

    2. Find a uniquely defined Galois orbit of m-th Gaussian periods.
       Return arbitrary elements of these orbits.
       This is done by `find_unique_orbit`.

    Read the docstrings of `find_root_order` and `find_unique_orbit`
    to find out more on the algorithm.
    '''
    p = klist[0].characteristic()
    ngcd = gcd([k.degree() for k in klist])
    nlcm = lcm([k.degree() for k in klist])
    if r == 0:
        r = ngcd
    assert all(k.degree() % r == 0 for k in klist)

    # Find a suitable m, see doc below
    o, G = find_root_order(p, [ngcd, nlcm], r, verbose=verbose)
    if o > omax:
        raise RuntimeError

    # Construct extension of the fields, if needed
    #
    # Note: `find_unique_orbit` is horribly slow if k is not a
    # field. Some composita tricks would be welcome.
    return tuple(find_gen_with_data(k, r, o, G, use_lucas=use_lucas, verbose=verbose) for k in klist)
def to_bivariate_polynomial(K, a):
    r"""
    Convert ``a`` from an element of ``K`` to a bivariate polynomial and a denominator.

    INPUT:

    - ``K`` -- a function field, given as a finite extension of a rational function field.
    - ``a`` -- an element of ``K``

    OUTPUT:

    - a bivariate polynomial F
    - a univariate polynomial d

    such that `a = F(y, x)/d(x)`.

    """
    v = a.list()
    denom = lcm([c.denominator() for c in v])
    S = denom.parent()
    y, x = S.base_ring()['%s,%s'%(K.variable_name(), K.base_field().variable_name())].gens()
    phi = S.hom([x])
    F, d = sum([phi((denom * v[i]).numerator()) * y**i for i in range(len(v))]), denom
    return F, d
Beispiel #39
0
    def normal_cone(self):
        r"""
        Return the (closure of the) normal cone of the triangulation.

        Recall that a regular triangulation is one that equals the
        "crease lines" of a convex piecewise-linear function. This
        support function is not unique, for example, you can scale it
        by a positive constant. The set of all piecewise-linear
        functions with fixed creases forms an open cone. This cone can
        be interpreted as the cone of normal vectors at a point of the
        secondary polytope, which is why we call it normal cone. See
        [GKZ1994]_ Section 7.1 for details.

        OUTPUT:

        The closure of the normal cone. The `i`-th entry equals the
        value of the piecewise-linear function at the `i`-th point of
        the configuration.

        For an irregular triangulation, the normal cone is empty. In
        this case, a single point (the origin) is returned.

        EXAMPLES::

            sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal')
            sage: triangulation
            (<0,1,3>, <1,2,3>)
            sage: N = triangulation.normal_cone();  N
            4-d cone in 4-d lattice
            sage: N.rays()
            ( 0,  0,  0, -1),
            ( 0,  0,  1,  1),
            ( 0,  0, -1, -1),
            ( 1,  0,  0,  1),
            (-1,  0,  0, -1),
            ( 0,  1,  0, -1),
            ( 0, -1,  0,  1)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring
            sage: N.dual().rays()
            (1, -1, 1, -1)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring

        TESTS::

            sage: polytopes.simplex(2).triangulate().normal_cone()
            3-d cone in 3-d lattice
            sage: _.dual().is_trivial()
            True
        """
        if not self.point_configuration().base_ring().is_subring(QQ):
            raise NotImplementedError(
                'Only base rings ZZ and QQ are supported')
        from ppl import Constraint_System, Linear_Expression, C_Polyhedron
        from sage.matrix.constructor import matrix
        from sage.arith.all import lcm
        pc = self.point_configuration()
        cs = Constraint_System()
        for facet in self.interior_facets():
            s0, s1 = self._boundary_simplex_dictionary()[facet]
            p = set(s0).difference(facet).pop()
            q = set(s1).difference(facet).pop()
            origin = pc.point(p).reduced_affine_vector()
            base_indices = [i for i in s0 if i != p]
            base = matrix([
                pc.point(i).reduced_affine_vector() - origin
                for i in base_indices
            ])
            sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin)
            relation = [0] * pc.n_points()
            relation[p] = sum(sol) - 1
            relation[q] = 1
            for i, base_i in enumerate(base_indices):
                relation[base_i] = -sol[i]
            rel_denom = lcm([QQ(r).denominator() for r in relation])
            relation = [ZZ(r * rel_denom) for r in relation]
            ex = Linear_Expression(relation, 0)
            cs.insert(ex >= 0)
        from sage.modules.free_module import FreeModule
        ambient = FreeModule(ZZ, self.point_configuration().n_points())
        if cs.empty():
            cone = C_Polyhedron(ambient.dimension(), 'universe')
        else:
            cone = C_Polyhedron(cs)
        from sage.geometry.cone import _Cone_from_PPL
        return _Cone_from_PPL(cone, lattice=ambient)
Beispiel #40
0
def cyclotomic_restriction(L,K):
    r"""
    Given two cyclotomic fields L and K, compute the compositum
    M of K and L, and return a function and the index [M:K]. The
    function is a map that acts as follows (here `M = Q(\zeta_m)`):

    INPUT:

    element alpha in L

    OUTPUT:

    a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`,
    where we view alpha as living in `M`. (Note that `\zeta_m`
    generates `M`, not `L`.)

    EXAMPLES::

        sage: L = CyclotomicField(12) ; N = CyclotomicField(33) ; M = CyclotomicField(132)
        sage: z, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N)
        sage: n
        2

        sage: z(L.0)
        -zeta33^19*x
        sage: z(L.0)(M.0)
        zeta132^11

        sage: z(L.0^3-L.0+1)
        (zeta33^19 + zeta33^8)*x + 1
        sage: z(L.0^3-L.0+1)(M.0)
        zeta132^33 - zeta132^11 + 1
        sage: z(L.0^3-L.0+1)(M.0) - M(L.0^3-L.0+1)
        0
    """
    if not L.has_coerce_map_from(K):
        M = CyclotomicField(lcm(L.zeta_order(), K.zeta_order()))
        f = cyclotomic_restriction_tower(M,K)
        def g(x):
            """
            Function returned by cyclotomic restriction.

            INPUT:

            element alpha in L

            OUTPUT:

            a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`,
            where we view alpha as living in `M`. (Note that `\zeta_m`
            generates `M`, not `L`.)

            EXAMPLES::

                sage: L = CyclotomicField(12)
                sage: N = CyclotomicField(33)
                sage: g, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N)
                sage: g(L.0)
                -zeta33^19*x
            """
            return f(M(x))
        return g, euler_phi(M.zeta_order())//euler_phi(K.zeta_order())
    else:
        return cyclotomic_restriction_tower(L,K), \
               euler_phi(L.zeta_order())//euler_phi(K.zeta_order())
Beispiel #41
0
    def _reduce_conic(self):
        r"""
        Return the reduced form of the conic, i.e. a conic with base field
        `K=F(t)` and coefficients `a,b,c` such that `a,b,c \in F[t]`,
        `\gcd(a,b)=\gcd(b,c)=\gcd(c,a)=1` and `abc` is square-free.
        
        Assumes `self` is in diagonal form.
        
        OUTPUT:
        
        A tuple (coefficients, multipliers), the coefficients of the conic
        in reduced form and multipliers `\lambda, \mu, \nu \in F(t)^*` such
        that `(x,y,z) \in F(t)` is a solution of the reduced conic if and only
        if `(\lambda x, \mu y, \nu z)` is a solution of `self`.
        
        ALGORITHM:
        
        The algorithm used is the algorithm ReduceConic in [HC2006]_.
        
        EXAMPLES::
        
            sage: K.<t> = FractionField(PolynomialRing(QQ, 't'))
            sage: C = Conic(K, [t^2-2, 2*t^3, -2*t^3-13*t^2-2*t+18])
            sage: C._reduce_conic()
            ([t^2 - 2, 2*t, -2*t^3 - 13*t^2 - 2*t + 18], [t, 1, t])
        """

        # start with removing fractions
        coeff = [
            self.coefficients()[0],
            self.coefficients()[3],
            self.coefficients()[5]
        ]
        coeff = lcm(lcm(coeff[0].denominator(), coeff[1].denominator()),
                    coeff[2].denominator()) * vector(coeff)
        # go to base ring of fraction field
        coeff = [self.base().base()(x) for x in coeff]
        coeff = vector(coeff) / gcd(coeff)
        # remove common divisors
        labda = mu = nu = 1
        g1 = g2 = g3 = 0
        ca, cb, cc = coeff
        while g1 != 1 or g2 != 1 or g3 != 1:
            g1 = gcd(ca, cb)
            ca = ca / g1
            cb = cb / g1
            cc = cc * g1
            nu = g1 * nu
            g2 = gcd(ca, cc)
            ca = ca / g2
            cc = cc / g2
            cb = cb * g2
            mu = g2 * mu
            g3 = gcd(cb, cc)
            cb = cb / g3
            cc = cc / g3
            ca = ca * g3
            labda = g3 * labda
        coeff = [ca, cb, cc]
        multipliers = [labda, mu, nu]

        # remove squares
        for i, x in enumerate(coeff):
            if is_FractionField(x.parent()):
                # go to base ring of fraction field
                x = self.base().base()(x)

            try:
                decom = x.squarefree_decomposition()
            except (NotImplementedError, AttributeError):
                decom = x.factor()
            x = decom.unit()
            x2 = 1
            for factor in decom:
                if factor[1] > 1:
                    if factor[1] % 2 == 0:
                        x2 *= factor[0]**(factor[1] // 2)
                    else:
                        x *= factor[0]
                        x2 *= factor[0]**((factor[1] - 1) // 2)
                else:
                    x *= factor[0]
            for j, y in enumerate(multipliers):
                if j != i:
                    multipliers[j] = y * x2
            coeff[i] = self.base_ring().base().coerce(x)

        return (coeff, multipliers)
Beispiel #42
0
def carmichael_lambda(n):
    r"""
    Return the Carmichael function of a positive integer ``n``.

    The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest
    positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all
    `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k`
    is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`.

    INPUT:

    - ``n`` -- a positive integer.

    OUTPUT:

    - The Carmichael function of ``n``.

    ALGORITHM:

    If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd
    prime and let `k` be a positive integer. Then
    `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then
    `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is
    composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the
    prime factorization of `n`. Then

    .. MATH::

        \lambda(n)
        = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t})
        = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t}))

    EXAMPLES:

    The Carmichael function of all positive integers up to and including 10::

        sage: from sage.crypto.util import carmichael_lambda
        sage: list(map(carmichael_lambda, [1..10]))
        [1, 1, 2, 2, 4, 2, 6, 2, 6, 4]

    The Carmichael function of the first ten primes::

        sage: list(map(carmichael_lambda, primes_first_n(10)))
        [1, 2, 4, 6, 10, 12, 16, 18, 22, 28]

    Cases where the Carmichael function is equivalent to the Euler phi
    function::

        sage: carmichael_lambda(2) == euler_phi(2)
        True
        sage: carmichael_lambda(4) == euler_phi(4)
        True
        sage: p = random_prime(1000, lbound=3, proof=True)
        sage: k = randint(1, 1000)
        sage: carmichael_lambda(p^k) == euler_phi(p^k)
        True

    A case where `\lambda(n) \neq \varphi(n)`::

        sage: k = randint(1, 1000)
        sage: carmichael_lambda(2^k) == 2^(k - 2)
        True
        sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k)
        False

    Verifying the current implementation of the Carmichael function using
    another implementation. The other implementation that we use for
    verification is an exhaustive search for the exponent of the
    multiplicative group `(\ZZ/n\ZZ)^{\ast}`. ::

        sage: from sage.crypto.util import carmichael_lambda
        sage: n = randint(1, 500)
        sage: c = carmichael_lambda(n)
        sage: def coprime(n):
        ....:     return [i for i in range(n) if gcd(i, n) == 1]
        sage: def znpower(n, k):
        ....:     L = coprime(n)
        ....:     return list(map(power_mod, L, [k]*len(L), [n]*len(L)))
        sage: def my_carmichael(n):
        ....:     for k in range(1, n):
        ....:         L = znpower(n, k)
        ....:         ones = [1] * len(L)
        ....:         T = [L[i] == ones[i] for i in xrange(len(L))]
        ....:         if all(T):
        ....:             return k
        sage: c == my_carmichael(n)
        True

    Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}`
    for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`.
    Here, we verify Carmichael's theorem. ::

        sage: from sage.crypto.util import carmichael_lambda
        sage: n = randint(1, 1000)
        sage: c = carmichael_lambda(n)
        sage: ZnZ = IntegerModRing(n)
        sage: M = ZnZ.list_of_elements_of_multiplicative_group()
        sage: ones = [1] * len(M)
        sage: P = [power_mod(a, c, n) for a in M]
        sage: P == ones
        True

    TESTS:

    The input ``n`` must be a positive integer::

        sage: from sage.crypto.util import carmichael_lambda
        sage: carmichael_lambda(0)
        Traceback (most recent call last):
        ...
        ValueError: Input n must be a positive integer.
        sage: carmichael_lambda(randint(-10, 0))
        Traceback (most recent call last):
        ...
        ValueError: Input n must be a positive integer.

    Bug reported in :trac:`8283`::

        sage: from sage.crypto.util import carmichael_lambda
        sage: type(carmichael_lambda(16))
        <type 'sage.rings.integer.Integer'>

    REFERENCES:

    .. [Carmichael2010] Carmichael function,
      http://en.wikipedia.org/wiki/Carmichael_function
    """
    n = Integer(n)
    # sanity check
    if n < 1:
        raise ValueError("Input n must be a positive integer.")

    L = n.factor()
    t = []

    # first get rid of the prime factor 2
    if n & 1 == 0:
        e = L[0][1]
        L = L[1:]   # now, n = 2**e * L.value()
        if e < 3:   # for 1 <= k < 3, lambda(2**k) = 2**(k - 1)
            e = e - 1
        else:       # for k >= 3, lambda(2**k) = 2**(k - 2)
            e = e - 2
        t.append(1 << e)  # 2**e

    # then other prime factors
    t += [p**(k - 1) * (p - 1) for p, k in L]

    # finish the job
    return lcm(t)
Beispiel #43
0
def mod5family(a, b):
    """
    Formulas for computing the family of elliptic curves with
    congruent mod-5 representation.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.mod5family import mod5family
        sage: mod5family(0,1)
        Elliptic Curve defined by y^2 = x^3 + (t^30+30*t^29+435*t^28+4060*t^27+27405*t^26+142506*t^25+593775*t^24+2035800*t^23+5852925*t^22+14307150*t^21+30045015*t^20+54627300*t^19+86493225*t^18+119759850*t^17+145422675*t^16+155117520*t^15+145422675*t^14+119759850*t^13+86493225*t^12+54627300*t^11+30045015*t^10+14307150*t^9+5852925*t^8+2035800*t^7+593775*t^6+142506*t^5+27405*t^4+4060*t^3+435*t^2+30*t+1) over Fraction Field of Univariate Polynomial Ring in t over Rational Field
    """
    J = 4*a**3 / (4*a**3+27*b**2)

    alpha = [0 for _ in range(21)]
    alpha[0] = 1
    alpha[1] = 0
    alpha[2] = 190*(J - 1)
    alpha[3] = -2280*(J - 1)**2
    alpha[4] = 855*(J - 1)**2*(-17 + 16*J)
    alpha[5] = 3648*(J - 1)**3*(17 - 9*J)
    alpha[6] = 11400*(J - 1)**3*(17 - 8*J)
    alpha[7] = -27360*(J - 1)**4*(17 + 26*J)
    alpha[8] = 7410*(J - 1)**4*(-119 - 448*J + 432*J**2)
    alpha[9] = 79040*(J - 1)**5*(17 + 145*J - 108*J**2)
    alpha[10] = 8892*(J - 1)**5*(187 + 2640*J - 5104*J**2 + 1152*J**3)
    alpha[11] = 98800*(J - 1)**6*(-17 - 388*J + 864*J**2)
    alpha[12] = 7410*(J - 1)**6*(-187 - 6160*J + 24464*J**2 - 24192*J**3)
    alpha[13] = 54720*(J - 1)**7*(17 + 795*J - 3944*J**2 + 9072*J**3)
    alpha[14] = 2280*(J - 1)**7*(221 + 13832*J - 103792*J**2 + 554112*J**3 - 373248*J**4)
    alpha[15] = 1824*(J - 1)**8*(-119 - 9842*J + 92608*J**2 - 911520*J**3 + 373248*J**4)
    alpha[16] = 4275*(J - 1)**8*(-17 - 1792*J + 23264*J**2 - 378368*J**3 + 338688*J**4)
    alpha[17] = 18240*(J - 1)**9*(1 + 133*J - 2132*J**2 + 54000*J**3 - 15552*J**4)
    alpha[18] = 190*(J - 1)**9*(17 + 2784*J - 58080*J**2 + 2116864*J**3 - 946944*J**4 + 2985984*J**5)
    alpha[19] = 360*(J - 1)**10*(-1 + 28*J - 1152*J**2)*(1 + 228*J + 176*J**2 + 1728*J**3)
    alpha[20] = (J - 1)**10*(-19 - 4560*J + 144096*J**2 - 9859328*J**3 - 8798976*J**4 - 226934784*J**5 + 429981696*J**6)

    beta = [0 for _ in range(31)]
    beta[0] = 1
    beta[1] = 30
    beta[2] = -435*(J - 1)
    beta[3] = 580*(J - 1)*(-7 + 9*J)
    beta[4] = 3915*(J - 1)**2*(7 - 8*J)
    beta[5] = 1566*(J - 1)**2*(91 - 78*J + 48*J**2)
    beta[6] = -84825*(J - 1)**3*(7 + 16*J)
    beta[7] = 156600*(J - 1)**3*(-13 - 91*J + 92*J**2)
    beta[8] = 450225*(J - 1)**4*(13 + 208*J - 144*J**2)
    beta[9] = 100050*(J - 1)**4*(143 + 4004*J - 5632*J**2 + 1728*J**3)
    beta[10] = 30015*(J - 1)**5*(-1001 - 45760*J + 44880*J**2 - 6912*J**3)
    beta[11] = 600300*(J - 1)**5*(-91 - 6175*J + 9272*J**2 - 2736*J**3)
    beta[12] = 950475*(J - 1)**6*(91 + 8840*J - 7824*J**2)
    beta[13] = 17108550*(J - 1)**6*(7 + 926*J - 1072*J**2 + 544*J**3)
    beta[14] = 145422675*(J - 1)**7*(-1 - 176*J + 48*J**2 - 384*J**3)
    beta[15] = 155117520*(J - 1)**8*(1 + 228*J + 176*J**2 + 1728*J**3)
    beta[16] = 145422675*(J - 1)**8*(1 + 288*J + 288*J**2 + 5120*J**3 - 6912*J**4)
    beta[17] = 17108550*(J - 1)**8*(7 + 2504*J + 3584*J**2 + 93184*J**3 - 283392*J**4 + 165888*J**5)
    beta[18] = 950475*(J - 1)**9*(-91 - 39936*J - 122976*J**2 - 2960384*J**3 + 11577600*J**4 - 5971968*J**5)
    beta[19] = 600300*(J - 1)**9*(-91 - 48243*J - 191568*J**2 - 6310304*J**3 + 40515072*J**4 - 46455552*J**5 + 11943936*J**6)
    beta[20] = 30015*(J - 1)**10*(1001 + 634920*J + 3880800*J**2 + 142879744*J**3 - 1168475904*J**4 + 1188919296*J**5 - 143327232*J**6)
    beta[21] = 100050*(J - 1)**10*(143 + 107250*J + 808368*J**2 + 38518336*J**3 - 451953408*J**4 + 757651968*J**5 - 367276032*J**6)
    beta[22] = 450225*(J - 1)**11*(-13 - 11440*J - 117216*J**2 - 6444800*J**3 + 94192384*J**4 - 142000128*J**5 + 95551488*J**6)
    beta[23] = 156600*(J - 1)**11*(-13 - 13299*J - 163284*J**2 - 11171552*J**3 +  217203840*J**4 - 474406656*J**5 + 747740160*J**6 - 429981696*J**7)
    beta[24] = 6525*(J - 1)**12*(91 + 107536*J + 1680624*J**2 + 132912128*J**3 -\
          3147511552*J**4 + 6260502528*J**5 - 21054173184*J**6 + 10319560704*J**7)
    beta[25] = 1566*(J - 1)**12*(91 + 123292*J + 2261248*J**2 + 216211904*J**3 - \
          6487793920*J**4 + 17369596928*J**5 - 97854234624*J**6 + 96136740864*J**7 - 20639121408*J**8)
    beta[26] = 3915*(J - 1)**13*(-7 - 10816*J - 242352*J**2 - 26620160*J**3 + 953885440*J**4 - \
          2350596096*J**5 + 26796552192*J**6 - 13329432576*J**7)
    beta[27] = 580*(J - 1)**13*(-7 - 12259*J - 317176*J**2 - 41205008*J**3 + \
          1808220160*J**4 - 5714806016*J**5 + 93590857728*J**6 - 70131806208*J**7 - 36118462464*J**8)
    beta[28] = 435*(J - 1)**14*(1 + 1976*J + 60720*J**2 + 8987648*J**3 - 463120640*J**4 + 1359157248*J**5 - \
          40644882432*J**6 - 5016453120*J**7 + 61917364224*J**8)
    beta[29] = 30*(J - 1)**14*(1 + 2218*J + 77680*J**2 + 13365152*J**3 - \
          822366976*J**4 + 2990693888*J**5 - 118286217216*J**6 - 24514928640*J**7 + 509958291456*J**8 - 743008370688*J**9)
    beta[30] = (J - 1)**15*(-1 - 2480*J - 101040*J**2 - 19642496*J**3 + 1399023872*J**4 - \
          4759216128*J**5 + 315623485440*J**6 + 471904911360*J**7 - 2600529297408*J**8 + 8916100448256*J**9)

    R = PolynomialRing(QQ, 't')
    b4 = a * R(alpha)
    b6 = b * R(beta)

    c2 = b4
    c3 = b6
    d = lcm(c2.denominator(), c3.denominator())
    F = FractionField(R)

    E = EllipticCurve(F, [c2*d**4, c3*d**6])
    return E
    def pthpowers(self, p, Bound):
        """
        Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound.

        Let `u_n` be a binary recurrence sequence.  A ``p`` th power in `u_n` is a solution
        to `u_n = y^p` for some integer `y`.  There are only finitely many ``p`` th powers in
        any recurrence sequence [SS].

        INPUT:

        - ``p`` - a rational prime integer (the fixed p in `u_n = y^p`)

        - ``Bound`` - a natural number (the maximum index `n` in `u_n = y^p` that is checked).

        OUTPUT:

        - A list of the indices of all ``p`` th powers less bounded by ``Bound``.  If the sequence is degenerate and there are many ``p`` th powers, raises ``ValueError``.

        EXAMPLES::

            sage: R = BinaryRecurrenceSequence(1,1)        #the Fibonacci sequence
            sage: R.pthpowers(2, 10**30)        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06]
            [0, 1, 2, 12]

            sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence
            sage: S.pthpowers(3,10**30)    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30
            [0, 1, 2]

            sage: Q = BinaryRecurrenceSequence(3,3,2,1)
            sage: Q.pthpowers(11,10**30)          # long time (7.5 seconds)
            [1]

        If the sequence is degenerate, and there are are no ``p`` th powers, returns `[]`.  Otherwise, if
        there are many ``p`` th powers, raises ``ValueError``.

        ::

            sage: T = BinaryRecurrenceSequence(2,0,1,2)
            sage: T.is_degenerate()
            True
            sage: T.is_geometric()
            True
            sage: T.pthpowers(7,10**30)
            Traceback (most recent call last):
            ...
            ValueError: The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers.

            sage: L = BinaryRecurrenceSequence(4,0,2,2)
            sage: [L(i).factor() for i in range(10)]
            [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17]
            sage: L.is_quasigeometric()
            True
            sage: L.pthpowers(2,10**30)
            []

        NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``.

        """

        #Thanks to Jesse Silliman for helpful conversations!

        #Reset the dictionary of good primes, as this depends on p
        self._PGoodness = {}
        #Starting lower bound on good primes
        self._ell = 1

        #If the sequence is geometric, then the `n`th term is `a*r^n`.  Thus the
        #property of being a ``p`` th power is periodic mod ``p``.  So there are either
        #no ``p`` th powers if there are none in the first ``p`` terms, or many if there
        #is at least one in the first ``p`` terms.

        if self.is_geometric() or self.is_quasigeometric():
            no_powers = True
            for i in range(1, 6 * p + 1):
                if _is_p_power(self(i), p):
                    no_powers = False
                    break
            if no_powers:
                if _is_p_power(self.u0, p):
                    return [0]
                return []
            else:
                raise ValueError(
                    "The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers."
                )

        #If the sequence is degenerate without being geometric or quasigeometric, there
        #may be many ``p`` th powers or no ``p`` th powers.

        elif (self.b**2 + 4 * self.c) == 0:

            #This is the case if the matrix F is not diagonalizable, ie b^2 +4c = 0, and alpha/beta = 1.

            alpha = self.b / 2

            #In this case, u_n = u_0*alpha^n + (u_1 - u_0*alpha)*n*alpha^(n-1) = alpha^(n-1)*(u_0 +n*(u_1 - u_0*alpha)),
            #that is, it is a geometric term (alpha^(n-1)) times an arithmetic term (u_0 + n*(u_1-u_0*alpha)).

            #Look at classes n = k mod p, for k = 1,...,p.

            for k in range(1, p + 1):

                #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k)
                #must thus be a pth power.  This is a linear equation in m, namely, A + B*m, where

                A = (alpha**(k - 1) * self.u0 + k *
                     (alpha**(k - 1) * self.u1 - self.u0 * alpha**k))
                B = p * (alpha**(k - 1) * self.u1 - self.u0 * alpha**k)

                #This linear equation represents a pth power iff A is a pth power mod B.

                if _is_p_power_mod(A, p, B):
                    raise ValueError(
                        "The degenerate binary recurrence sequence has many pth powers."
                    )
            return []

        #We find ``p`` th powers using an elementary sieve.  Term `u_n` is a ``p`` th
        #power if and only if it is a ``p`` th power modulo every prime `\\ell`.  This condition
        #gives nontrivial information if ``p`` divides the order of the multiplicative group of
        #`\\Bold(F)_{\\ell}`, i.e. if `\\ell` is ` 1 \mod{p}`, as then only `1/p` terms are ``p`` th
        #powers modulo `\\ell``.

        #Thus, given such an `\\ell`, we get a set of necessary congruences for the index modulo the
        #the period of the sequence mod `\\ell`.  Then we intersect these congruences for many primes
        #to get a tight list modulo a growing modulus.  In order to keep this step manageable, we
        #only use primes `\\ell` that are have particularly smooth periods.

        #Some congruences in the list will remain as the modulus grows.  If a congruence remains through
        #7 rounds of increasing the modulus, then we check if this corresponds to a perfect power (if
        #it does, we add it to our list of indices corresponding to ``p`` th powers).  The rest of the congruences
        #are transient and grow with the modulus.  Once the smallest of these is greater than the bound,
        #the list of known indices corresponding to ``p`` th powers is complete.

        else:

            if Bound < 3 * p:

                powers = []
                ell = p + 1

                while not is_prime(ell):
                    ell = ell + p

                F = GF(ell)
                a0 = F(self.u0)
                a1 = F(self.u1)  #a0 and a1 are variables for terms in sequence
                bf, cf = F(self.b), F(self.c)

                for n in range(Bound):  # n is the index of the a0

                    #Check whether a0 is a perfect power mod ell
                    if _is_p_power_mod(a0, p, ell):
                        #if a0 is a perfect power mod ell, check if nth term is ppower
                        if _is_p_power(self(n), p):
                            powers.append(n)

                    a0, a1 = a1, bf * a1 + cf * a0  #step up the variables

            else:

                powers = [
                ]  #documents the indices of the sequence that provably correspond to pth powers
                cong = [
                    0
                ]  #list of necessary congruences on the index for it to correspond to pth powers
                Possible_count = {
                }  #keeps track of the number of rounds a congruence lasts in cong

                #These parameters are involved in how we choose primes to increase the modulus
                qqold = 1  #we believe that we know complete information coming from primes good by qqold
                M1 = 1  #we have congruences modulo M1, this may not be the tightest list
                M2 = p  #we want to move to have congruences mod M2
                qq = 1  #the largest prime power divisor of M1 is qq

                #This loop ups the modulus.
                while True:

                    #Try to get good data mod M2

                    #patience of how long we should search for a "good prime"
                    patience = 0.01 * _estimated_time(
                        lcm(M2, p * next_prime_power(qq)), M1, len(cong), p)
                    tries = 0

                    #This loop uses primes to get a small set of congruences mod M2.
                    while True:

                        #only proceed if took less than patience time to find the next good prime
                        ell = _next_good_prime(p, self, qq, patience, qqold)
                        if ell:

                            #gather congruence data for the sequence mod ell, which will be mod period(ell) = modu
                            cong1, modu = _find_cong1(p, self, ell)

                            CongNew = [
                            ]  #makes a new list from cong that is now mod M = lcm(M1, modu) instead of M1
                            M = lcm(M1, modu)
                            for k in range(M // M1):
                                for i in cong:
                                    CongNew.append(k * M1 + i)
                            cong = set(CongNew)

                            M1 = M

                            killed_something = False  #keeps track of when cong1 can rule out a congruence in cong

                            #CRT by hand to gain speed
                            for i in list(cong):
                                if not (
                                        i % modu in cong1
                                ):  #congruence in cong is inconsistent with any in cong1
                                    cong.remove(i)  #remove that congruence
                                    killed_something = True

                            if M1 == M2:
                                if not killed_something:
                                    tries += 1
                                    if tries == 2:  #try twice to rule out congruences
                                        cong = list(cong)
                                        qqold = qq
                                        qq = next_prime_power(qq)
                                        M2 = lcm(M2, p * qq)
                                        break

                        else:
                            qq = next_prime_power(qq)
                            M2 = lcm(M2, p * qq)
                            cong = list(cong)
                            break

                    #Document how long each element of cong has been there
                    for i in cong:
                        if i in Possible_count:
                            Possible_count[i] = Possible_count[i] + 1
                        else:
                            Possible_count[i] = 1

                    #Check how long each element has persisted, if it is for at least 7 cycles,
                    #then we check to see if it is actually a perfect power
                    for i in Possible_count:
                        if Possible_count[i] == 7:
                            n = Integer(i)
                            if n < Bound:
                                if _is_p_power(self(n), p):
                                    powers.append(n)

                    #check for a contradiction
                    if len(cong) > len(powers):
                        if cong[len(powers)] > Bound:
                            break
                    elif M1 > Bound:
                        break

            return powers
Beispiel #45
0
    def has_rational_point(self, point = False, obstruction = False,
                           algorithm = 'default', read_cache = True):
        r"""
        Returns True if and only if ``self`` has a point defined over `\QQ`.

        If ``point`` and ``obstruction`` are both False (default), then
        the output is a boolean ``out`` saying whether ``self`` has a
        rational point.

        If ``point`` or ``obstruction`` is True, then the output is
        a pair ``(out, S)``, where ``out`` is as above and the following
        holds:

         - if ``point`` is True and ``self`` has a rational point,
           then ``S`` is a rational point,

         - if ``obstruction`` is True and ``self`` has no rational point,
           then ``S`` is a prime such that no rational point exists
           over the completion at ``S`` or `-1` if no point exists over `\RR`.

        Points and obstructions are cached, whenever they are found.
        Cached information is used if and only if ``read_cache`` is True.

        ALGORITHM:

        The parameter ``algorithm``
        specifies the algorithm to be used:

         - ``'qfsolve'`` -- Use PARI/GP function ``qfsolve``

         - ``'rnfisnorm'`` -- Use PARI's function rnfisnorm
           (cannot be combined with ``obstruction = True``)

         - ``'local'`` -- Check if a local solution exists for all primes
           and infinite places of `\QQ` and apply the Hasse principle
           (cannot be combined with ``point = True``)

         - ``'default'`` -- Use ``'qfsolve'``

         - ``'magma'`` (requires Magma to be installed) --
           delegates the task to the Magma computer algebra
           system.

        EXAMPLES::

            sage: C = Conic(QQ, [1, 2, -3])
            sage: C.has_rational_point(point = True)
            (True, (1 : 1 : 1))
            sage: D = Conic(QQ, [1, 3, -5])
            sage: D.has_rational_point(point = True)
            (False, 3)
            sage: P.<X,Y,Z> = QQ[]
            sage: E = Curve(X^2 + Y^2 + Z^2); E
            Projective Conic Curve over Rational Field defined by X^2 + Y^2 + Z^2
            sage: E.has_rational_point(obstruction = True)
            (False, -1)

        The following would not terminate quickly with
        ``algorithm = 'rnfisnorm'`` ::

            sage: C = Conic(QQ, [1, 113922743, -310146482690273725409])
            sage: C.has_rational_point(point = True)
            (True, (-76842858034579/5424 : -5316144401/5424 : 1))
            sage: C.has_rational_point(algorithm = 'local', read_cache = False)
            True
            sage: C.has_rational_point(point=True, algorithm='magma', read_cache=False) # optional - magma
            (True, (30106379962113/7913 : 12747947692/7913 : 1))

        TESTS:

        Create a bunch of conics over `\QQ`, check if ``has_rational_point`` runs without errors
        and returns consistent answers for all algorithms. Check if all points returned are valid. ::

            sage: l = Sequence(cartesian_product_iterator([[-1, 0, 1] for i in range(6)]))
            sage: c = [Conic(QQ, a) for a in l if a != [0,0,0] and a != (0,0,0,0,0,0)]
            sage: d = []
            sage: d = [[C]+[C.has_rational_point(algorithm = algorithm, read_cache = False, obstruction = (algorithm != 'rnfisnorm'), point = (algorithm != 'local')) for algorithm in ['local', 'qfsolve', 'rnfisnorm']] for C in c[::10]] # long time: 7 seconds
            sage: assert all([e[1][0] == e[2][0] and e[1][0] == e[3][0] for e in d])
            sage: assert all([e[0].defining_polynomial()(Sequence(e[i][1])) == 0 for e in d for i in [2,3] if e[1][0]])
        """
        if read_cache:
            if self._rational_point is not None:
                if point or obstruction:
                    return True, self._rational_point
                else:
                    return True
            if self._local_obstruction is not None:
                if point or obstruction:
                    return False, self._local_obstruction
                else:
                    return False
            if (not point) and self._finite_obstructions == [] and \
               self._infinite_obstructions == []:
                if obstruction:
                    return True, None
                return True
        if self.has_singular_point():
            if point:
                return self.has_singular_point(point = True)
            if obstruction:
                return True, None
            return True
        if algorithm == 'default' or algorithm == 'qfsolve':
            M = self.symmetric_matrix()
            M *= lcm([ t.denominator() for t in M.list() ])
            pt = qfsolve(M)
            if pt in ZZ:
                if self._local_obstruction is None:
                    self._local_obstruction = pt
                if point or obstruction:
                    return False, pt
                return False
            pt = self.point([pt[0], pt[1], pt[2]])
            if point or obstruction:
                return True, pt
            return True
        ret = ProjectiveConic_number_field.has_rational_point( \
                                           self, point = point, \
                                           obstruction = obstruction, \
                                           algorithm = algorithm, \
                                           read_cache = read_cache)
        if point or obstruction:
            if is_RingHomomorphism(ret[1]):
                ret[1] = -1
        return ret
Beispiel #46
0
def row_reduced_form(M, transformation=False):
    """
    This function computes a row reduced form of a matrix over a rational
    function field `k(x)`, for `k` a field.

    INPUT:

     - `M` - a matrix over `k(x)` or `k[x]` for `k` a field.
     - `transformation` - A boolean (default: `False`). If this boolean is set to `True` a second matrix is output (see OUTPUT).
     
    OUTPUT:

    If `transformation` is `False`, the output is `W`, a row reduced form of `M`.
    
    If `transformation` is `True`, this function will output a pair `(W,N)` consisting of two matrices over `k(x)`:

    1. `W` - a row reduced form of `M`.
    2. `N` - an invertible matrix over `k(x)` satisfying `NW = M`.

    EXAMPLES:

    The fuction expects matrices over the rational function field, but
    other examples below show how one can provide matrices over the ring
    of polynomials (whose quotient field is the rational function field).

    ::

        sage: R.<t> = GF(3)['t']
        sage: K = FractionField(R)
        sage: import sage.matrix.matrix_misc
        sage: sage.matrix.matrix_misc.row_reduced_form(matrix([[(t-1)^2/t],[(t-1)]]))
        [(2*t + 1)/t]
        [          0]

    The last example shows the usage of the transformation parameter.
        
    ::
        sage: Fq.<a> = GF(2^3)
        sage: Fx.<x> = Fq[]
        sage: A = matrix(Fx,[[x^2+a,x^4+a],[x^3,a*x^4]])
        sage: from sage.matrix.matrix_misc import row_reduced_form
        sage: row_reduced_form(A,transformation=True)
        (
        [(a^2 + 1)*x^3 + x^2 + a                       a]  [      1 a^2 + 1]
        [                    x^3                   a*x^4], [      0                 1]
        )
            
    NOTES:

    See docstring for row_reduced_form method of matrices for
    more information.
    """

    # determine whether M has polynomial or rational function coefficients
    R0 = M.base_ring()

    #Compute the base polynomial ring
    if R0 in _Fields:
        R = R0.base()
    else:
        R = R0
    from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
    if not is_PolynomialRing(R) or not R.base_ring().is_field():
        raise TypeError(
            "the coefficients of M must lie in a univariate polynomial ring over a field"
        )

    t = R.gen()

    # calculate least-common denominator of matrix entries and clear
    # denominators. The result lies in R
    from sage.arith.all import lcm
    from sage.matrix.constructor import matrix
    from sage.misc.functional import numerator
    if R0 in _Fields:
        den = lcm([a.denominator() for a in M.list()])
        num = matrix([[numerator(_) for _ in v] for v in (M * den).rows()])
    else:
        # No need to clear denominators
        num = M

    r = [list(v) for v in num.rows()]

    if transformation:
        N = matrix(num.nrows(), num.nrows(), R(1)).rows()

    rank = 0
    num_zero = 0
    if M.is_zero():
        num_zero = len(r)
    while rank != len(r) - num_zero:
        # construct matrix of leading coefficients
        v = []
        for w in map(list, r):
            # calculate degree of row (= max of degree of entries)
            d = max([e.numerator().degree() for e in w])

            # extract leading coefficients from current row
            x = []
            for y in w:
                if y.degree() >= d and d >= 0:
                    x.append(y.coefficients(sparse=False)[d])
                else:
                    x.append(0)
            v.append(x)
        l = matrix(v)

        # count number of zero rows in leading coefficient matrix
        # because they do *not* contribute interesting relations
        num_zero = 0
        for v in l.rows():
            is_zero = 1
            for w in v:
                if w != 0:
                    is_zero = 0
            if is_zero == 1:
                num_zero += 1

        # find non-trivial relations among the columns of the
        # leading coefficient matrix
        kern = l.kernel().basis()
        rank = num.nrows() - len(kern)

        # do a row operation if there's a non-trivial relation
        if not rank == len(r) - num_zero:
            for rel in kern:
                # find the row of num involved in the relation and of
                # maximal degree
                indices = []
                degrees = []
                for i in range(len(rel)):
                    if rel[i] != 0:
                        indices.append(i)
                        degrees.append(max([e.degree() for e in r[i]]))

                # find maximum degree among rows involved in relation
                max_deg = max(degrees)

                # check if relation involves non-zero rows
                if max_deg != -1:
                    i = degrees.index(max_deg)
                    rel /= rel[indices[i]]

                    for j in range(len(indices)):
                        if j != i:
                            # do the row operation
                            v = []
                            for k in range(len(r[indices[i]])):
                                v.append(r[indices[i]][k] + rel[indices[j]] *
                                         t**(max_deg - degrees[j]) *
                                         r[indices[j]][k])
                            r[indices[i]] = v

                            if transformation:
                                # If the user asked for it, record the row operation
                                v = []
                                for k in range(len(N[indices[i]])):
                                    v.append(N[indices[i]][k] +
                                             rel[indices[j]] *
                                             t**(max_deg - degrees[j]) *
                                             N[indices[j]][k])
                                N[indices[i]] = v

                    # remaining relations (if any) are no longer valid,
                    # so continue onto next step of algorithm
                    break
    if is_PolynomialRing(R0):
        A = matrix(R, r)
    else:
        A = matrix(R, r) / den
    if transformation:
        return (A, matrix(N))
    else:
        return A
Beispiel #47
0
def update(G,B,h):
    """
    Update ``G`` using the list of critical pairs ``B`` and the
    polynomial ``h`` as presented in [BW93]_, page 230. For this,
    Buchberger's first and second criterion are tested.

    This function uses the Gebauer-Moeller Installation.

    INPUT:

    - ``G`` - an intermediate Groebner basis
    - ``B`` - a list of critical pairs
    - ``h`` - a polynomial

    OUTPUT:
        ``G,B`` where ``G`` and ``B`` are updated

    EXAMPLE::

        sage: from sage.rings.polynomial.toy_d_basis import update
        sage: A.<x,y> = PolynomialRing(ZZ, 2)
        sage: G = set([3*x^2 + 7, 2*y + 1, x^3 - y^2 + 7*x - y + 1])
        sage: B = set([])
        sage: h = x^2*y - x^2 + y - 3
        sage: update(G,B,h)
        ({2*y + 1, 3*x^2 + 7, x^2*y - x^2 + y - 3, x^3 - y^2 + 7*x - y + 1},
         {(x^2*y - x^2 + y - 3, 2*y + 1),
          (x^2*y - x^2 + y - 3, 3*x^2 + 7),
          (x^2*y - x^2 + y - 3, x^3 - y^2 + 7*x - y + 1)})
    """
    R = h.parent()
    LCM = R.monomial_lcm

    lt_divides = lambda x,y: (R.monomial_divides( LM(h), LM(g) ) and LC(h).divides(LC(g)) )
    lt_pairwise_prime = lambda x,y : R.monomial_pairwise_prime(LM(x),LM(y)) and gcd(LC(x),LC(y)) == 1
    lcm_divides = lambda f,g1,h: R.monomial_divides( LCM(LM(h),LM(f[1])),  LCM(LM(h),LM(g1)) ) and \
                  lcm(LC(h),LC(f[1])).divides(lcm(LC(h),LC(g1)))

    C = set([(h,g) for g in G])

    D = set()
    while C != set():
        (h,g1) = C.pop()


        if lt_pairwise_prime(h,g) or \
           (\
               not any( lcm_divides(f,g1,h) for f in C ) \
               and
               not any( lcm_divides(f,g1,h) for f in D ) \
            ):
            D.add( (h,g1) )

    E = set()

    while D != set():
        (h,g) = D.pop()
        if not lt_pairwise_prime(h,g):
            E.add( (h,g) )

    B_new = set()
    while B != set():
        g1,g2 = B.pop()

        lcm_lg1_lg2 = lcm(LC(g1),LC(g2)) * LCM(LM(g1),LM(g2))
        if not lt_divides(lcm_lg1_lg2, h) or \
               lcm(LC(g1),LC( h)) * R.monomial_lcm(LM(g1),LM( h)) == lcm_lg1_lg2 or \
               lcm(LC( h),LC(g2)) * R.monomial_lcm(LM( h),LM(g2)) == lcm_lg1_lg2 :
            B_new.add( (g1,g2) )

    B_new = B_new.union( E )

    G_new = set()
    while G != set():
        g = G.pop()
        if not lt_divides(g,h):
            G_new.add(g)

    G_new.add(h)

    return G_new,B_new
    def pthpowers(self, p, Bound):
        """
        Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound.

        Let `u_n` be a binary recurrence sequence.  A ``p`` th power in `u_n` is a solution
        to `u_n = y^p` for some integer `y`.  There are only finitely many ``p`` th powers in
        any recurrence sequence [SS].

        INPUT:

        - ``p`` - a rational prime integer (the fixed p in `u_n = y^p`)

        - ``Bound`` - a natural number (the maximum index `n` in `u_n = y^p` that is checked).

        OUTPUT:

        - A list of the indices of all ``p`` th powers less bounded by ``Bound``.  If the sequence is degenerate and there are many ``p`` th powers, raises ``ValueError``.

        EXAMPLES::

            sage: R = BinaryRecurrenceSequence(1,1)        #the Fibonacci sequence
            sage: R.pthpowers(2, 10**30)        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06]
            [0, 1, 2, 12]

            sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence
            sage: S.pthpowers(3,10**30)    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30
            [0, 1, 2]

            sage: Q = BinaryRecurrenceSequence(3,3,2,1)
            sage: Q.pthpowers(11,10**30)          # long time (7.5 seconds)
            [1]

        If the sequence is degenerate, and there are are no ``p`` th powers, returns `[]`.  Otherwise, if
        there are many ``p`` th powers, raises ``ValueError``.

        ::

            sage: T = BinaryRecurrenceSequence(2,0,1,2)
            sage: T.is_degenerate()
            True
            sage: T.is_geometric()
            True
            sage: T.pthpowers(7,10**30)
            Traceback (most recent call last):
            ...
            ValueError: The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers.

            sage: L = BinaryRecurrenceSequence(4,0,2,2)
            sage: [L(i).factor() for i in range(10)]
            [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17]
            sage: L.is_quasigeometric()
            True
            sage: L.pthpowers(2,10**30)
            []

        NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``.

        """

        #Thanks to Jesse Silliman for helpful conversations!

        #Reset the dictionary of good primes, as this depends on p
        self._PGoodness = {}
        #Starting lower bound on good primes
        self._ell = 1

        #If the sequence is geometric, then the `n`th term is `a*r^n`.  Thus the
        #property of being a ``p`` th power is periodic mod ``p``.  So there are either
        #no ``p`` th powers if there are none in the first ``p`` terms, or many if there
        #is at least one in the first ``p`` terms.

        if self.is_geometric() or self.is_quasigeometric():
            no_powers = True
            for i in range(1,6*p+1):
                if _is_p_power(self(i), p) :
                    no_powers = False
                    break
            if no_powers:
                if _is_p_power(self.u0,p):
                    return [0]
                return []
            else :
                raise ValueError("The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers.")

        #If the sequence is degenerate without being geometric or quasigeometric, there
        #may be many ``p`` th powers or no ``p`` th powers.

        elif (self.b**2+4*self.c) == 0 :

            #This is the case if the matrix F is not diagonalizable, ie b^2 +4c = 0, and alpha/beta = 1.

            alpha = self.b/2

            #In this case, u_n = u_0*alpha^n + (u_1 - u_0*alpha)*n*alpha^(n-1) = alpha^(n-1)*(u_0 +n*(u_1 - u_0*alpha)),
            #that is, it is a geometric term (alpha^(n-1)) times an arithmetic term (u_0 + n*(u_1-u_0*alpha)).

            #Look at classes n = k mod p, for k = 1,...,p.

            for k in range(1,p+1):

                #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k)
                #must thus be a pth power.  This is a linear equation in m, namely, A + B*m, where

                A = (alpha**(k-1)*self.u0 + k*(alpha**(k-1)*self.u1 - self.u0*alpha**k))
                B = p*(alpha**(k-1)*self.u1 - self.u0*alpha**k)

                #This linear equation represents a pth power iff A is a pth power mod B.

                if _is_p_power_mod(A, p, B):
                    raise ValueError("The degenerate binary recurrence sequence has many pth powers.")
            return []

        #We find ``p`` th powers using an elementary sieve.  Term `u_n` is a ``p`` th
        #power if and only if it is a ``p`` th power modulo every prime `\\ell`.  This condition
        #gives nontrivial information if ``p`` divides the order of the multiplicative group of
        #`\\Bold(F)_{\\ell}`, i.e. if `\\ell` is ` 1 \mod{p}`, as then only `1/p` terms are ``p`` th
        #powers modulo `\\ell``.

        #Thus, given such an `\\ell`, we get a set of necessary congruences for the index modulo the
        #the period of the sequence mod `\\ell`.  Then we intersect these congruences for many primes
        #to get a tight list modulo a growing modulus.  In order to keep this step manageable, we
        #only use primes `\\ell` that are have particularly smooth periods.

        #Some congruences in the list will remain as the modulus grows.  If a congruence remains through
        #7 rounds of increasing the modulus, then we check if this corresponds to a perfect power (if
        #it does, we add it to our list of indices corresponding to ``p`` th powers).  The rest of the congruences
        #are transient and grow with the modulus.  Once the smallest of these is greater than the bound,
        #the list of known indices corresponding to ``p`` th powers is complete.

        else:

            if Bound < 3 * p :

                powers = []
                ell = p + 1

                while not is_prime(ell):
                    ell = ell + p

                F = GF(ell)
                a0 = F(self.u0); a1 = F(self.u1) #a0 and a1 are variables for terms in sequence
                bf, cf = F(self.b), F(self.c)

                for n in range(Bound): # n is the index of the a0

                    #Check whether a0 is a perfect power mod ell
                    if _is_p_power_mod(a0, p, ell) :
                        #if a0 is a perfect power mod ell, check if nth term is ppower
                        if _is_p_power(self(n), p):
                            powers.append(n)

                    a0, a1 = a1, bf*a1 + cf*a0        #step up the variables

            else :

                powers = []        #documents the indices of the sequence that provably correspond to pth powers
                cong = [0]        #list of necessary congruences on the index for it to correspond to pth powers
                Possible_count = {}    #keeps track of the number of rounds a congruence lasts in cong

                #These parameters are involved in how we choose primes to increase the modulus
                qqold = 1        #we believe that we know complete information coming from primes good by qqold
                M1 = 1            #we have congruences modulo M1, this may not be the tightest list
                M2 = p            #we want to move to have congruences mod M2
                qq = 1            #the largest prime power divisor of M1 is qq

                #This loop ups the modulus.
                while True:

                    #Try to get good data mod M2

                    #patience of how long we should search for a "good prime"
                    patience = 0.01 * _estimated_time(lcm(M2,p*next_prime_power(qq)), M1, len(cong), p)
                    tries = 0

                    #This loop uses primes to get a small set of congruences mod M2.
                    while True:

                        #only proceed if took less than patience time to find the next good prime
                        ell = _next_good_prime(p, self, qq, patience, qqold)
                        if ell:

                            #gather congruence data for the sequence mod ell, which will be mod period(ell) = modu
                            cong1, modu = _find_cong1(p, self, ell)

                            CongNew = []        #makes a new list from cong that is now mod M = lcm(M1, modu) instead of M1
                            M = lcm(M1, modu)
                            for k in range(M // M1):
                                for i in cong:
                                    CongNew.append(k * M1 + i)
                            cong = set(CongNew)

                            M1 = M

                            killed_something = False        #keeps track of when cong1 can rule out a congruence in cong

                            #CRT by hand to gain speed
                            for i in list(cong):
                                if not (i % modu in cong1):        #congruence in cong is inconsistent with any in cong1
                                    cong.remove(i)            #remove that congruence
                                    killed_something = True

                            if M1 == M2:
                                if not killed_something:
                                    tries += 1
                                    if tries == 2:            #try twice to rule out congruences
                                        cong = list(cong)
                                        qqold = qq
                                        qq = next_prime_power(qq)
                                        M2 = lcm(M2,p*qq)
                                        break

                        else :
                            qq = next_prime_power(qq)
                            M2 = lcm(M2,p*qq)
                            cong = list(cong)
                            break

                    #Document how long each element of cong has been there
                    for i in cong:
                        if i in Possible_count:
                            Possible_count[i] = Possible_count[i] + 1
                        else :
                            Possible_count[i] = 1

                    #Check how long each element has persisted, if it is for at least 7 cycles,
                    #then we check to see if it is actually a perfect power
                    for i in Possible_count:
                        if Possible_count[i] == 7:
                            n = Integer(i)
                            if n < Bound:
                                if _is_p_power(self(n),p):
                                    powers.append(n)

                    #check for a contradiction
                    if len(cong) > len(powers):
                        if cong[len(powers)] > Bound:
                            break
                    elif M1 > Bound:
                        break

            return powers
    def period(self, m):
        """
        Return the period of the binary recurrence sequence modulo
        an integer ``m``.

        If `n_1` is congruent to `n_2` modulu ``period(m)``, then `u_{n_1}` is
        is congruent to `u_{n_2}` modulo ``m``.

        INPUT:

        - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated).

        OUTPUT:

        - The integer (the period of the sequence modulo m)

        EXAMPLES:

        If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence
        mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS06]).

        ::

            sage: R = BinaryRecurrenceSequence(1,1)
            sage: R.period(31)
            30

            sage: [R(i) % 4 for i in range(12)]
            [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
            sage: R.period(4)
            6

        This function works for degenerate sequences as well.

        ::

            sage: S = BinaryRecurrenceSequence(2,0,1,2)
            sage: S.is_degenerate()
            True
            sage: S.is_geometric()
            True
            sage: [S(i) % 17 for i in range(16)]
            [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9]
            sage: S.period(17)
            8

        Note: the answer is cached.
        """

        #If we have already computed the period mod m, then we return the stored value.

        if m in self._period_dict:
            return self._period_dict[m]

        else:
            R = Integers(m)
            A = matrix(R, [[0,1],[self.c,self.b]])
            w = matrix(R, [[self.u0],[self.u1]])
            Fac = list(m.factor())
            Periods = {}

            #To compute the period mod m, we compute the least integer n such that A^n*w == w.  This necessarily
            #divides the order of A as a matrix in GL_2(Z/mZ).

            #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm.
            #To compute the period mod p^e, we first compute the order mod p.  Then the period mod p^e
            #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to
            #the identity mod p is of order (p^{e-1})^4.  So we compute the period mod p^e by successively
            #multiplying the period mod p by powers of p.

            for i in Fac:
                p = i[0]; e = i[1]
                #first compute the period mod p
                if p in self._period_dict:
                    perp = self._period_dict[p]
                else:
                    F = A.change_ring(GF(p))
                    v = w.change_ring(GF(p))
                    FF = F**(p-1)
                    p1fac = list((p-1).factor())

                    #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1).
                    #The order divides p-1 if it is diagonalizable.  In any case, det(F^(p-1))=1,
                    #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]].
                    #The order of the subgroup of matrices of this form is p, so the order must divide
                    #p(p-1) -- in fact it must be a multiple of p.  If this is not the case, then the
                    #order divides (p-1)(p+1).  As the period divides the order of the matrix in GL_2(F_p),
                    #these conditions hold for the period as well.

                    #check if the order divides (p-1)
                    if FF*v == v:
                        M = p-1
                        Mfac = p1fac

                    #check if the trace is 2, then the order is a multiple of p dividing p*(p-1)
                    elif (FF).trace() == 2:
                        M = p-1
                        Mfac = p1fac
                        F = F**p        #replace F by F^p as now we only need to determine the factor dividing (p-1)

                    #otherwise it will divide (p+1)(p-1)
                    else :
                        M = (p+1)*(p-1)
                        p2fac = list((p+1).factor())        #factor the (p+1) and (p-1) terms separately and then combine for speed
                        Mfac_dic = {}
                        for i in list(p1fac + p2fac):
                            if i[0] not in Mfac_dic:
                                Mfac_dic[i[0]] = i[1]
                            else :
                                Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1]
                        Mfac = [(i,Mfac_dic[i]) for i in Mfac_dic]

                    #Now use a fast order algorithm to compute the period.  We know that the period divides
                    #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors.  As
                    #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j).  After
                    #all factors have been iterated over, the result is the period mod p.

                    Mfac = list(Mfac)
                    C=[]

                    #expand the list of prime factors so every factor is with multiplicity 1

                    for i in range(len(Mfac)):
                        for j in range(Mfac[i][1]):
                            C.append(Mfac[i][0])

                    Mfac = C
                    n = M
                    for i in Mfac:
                        b = Integer(n/i)
                        if F**b*v == v:
                            n = b
                    perp = n

                #Now compute the period mod p^e by stepping up by multiples of p
                F = A.change_ring(Integers(p**e))
                v = w.change_ring(Integers(p**e))
                FF = F**perp
                if FF*v == v:
                    perpe = perp
                else :
                    tries = 0
                    while True:
                        tries += 1
                        FF = FF**p
                        if FF*v == v:
                            perpe = perp*p**tries
                            break
                Periods[p] = perpe

            #take the lcm of the periods mod all distinct primes dividing m
            period = 1
            for p in Periods:
                period = lcm(Periods[p],period)

            self._period_dict[m] = period        #cache the period mod m
            return period
Beispiel #50
0
def Omega_ge(a, exponents):
    r"""
    Return `\Omega_{\ge}` of the expression specified by the input.

    To be more precise, calculate

    .. MATH::

        \Omega_{\ge} \frac{\mu^a}{
        (1 - z_0 \mu^{e_0}) \dots (1 - z_{n-1} \mu^{e_{n-1}})}

    and return its numerator and a factorization of its denominator.
    Note that `z_0`, ..., `z_{n-1}` only appear in the output, but not in the
    input.

    INPUT:

    - ``a`` -- an integer

    - ``exponents`` -- a tuple of integers

    OUTPUT:

    A pair representing a quotient as follows: Its first component is the
    numerator as a Laurent polynomial, its second component a factorization
    of the denominator as a tuple of Laurent polynomials, where each
    Laurent polynomial `z` represents a factor `1 - z`.

    The parents of these Laurent polynomials is always a
    Laurent polynomial ring in `z_0`, ..., `z_{n-1}` over `\ZZ`, where
    `n` is the length of ``exponents``.

    EXAMPLES::

        sage: from sage.rings.polynomial.omega import Omega_ge
        sage: Omega_ge(0, (1, -2))
        (1, (z0, z0^2*z1))
        sage: Omega_ge(0, (1, -3))
        (1, (z0, z0^3*z1))
        sage: Omega_ge(0, (1, -4))
        (1, (z0, z0^4*z1))

        sage: Omega_ge(0, (2, -1))
        (z0*z1 + 1, (z0, z0*z1^2))
        sage: Omega_ge(0, (3, -1))
        (z0*z1^2 + z0*z1 + 1, (z0, z0*z1^3))
        sage: Omega_ge(0, (4, -1))
        (z0*z1^3 + z0*z1^2 + z0*z1 + 1, (z0, z0*z1^4))

        sage: Omega_ge(0, (1, 1, -2))
        (-z0^2*z1*z2 - z0*z1^2*z2 + z0*z1*z2 + 1, (z0, z1, z0^2*z2, z1^2*z2))
        sage: Omega_ge(0, (2, -1, -1))
        (z0*z1*z2 + z0*z1 + z0*z2 + 1, (z0, z0*z1^2, z0*z2^2))
        sage: Omega_ge(0, (2, 1, -1))
        (-z0*z1*z2^2 - z0*z1*z2 + z0*z2 + 1, (z0, z1, z0*z2^2, z1*z2))

    ::

        sage: Omega_ge(0, (2, -2))
        (-z0*z1 + 1, (z0, z0*z1, z0*z1))
        sage: Omega_ge(0, (2, -3))
        (z0^2*z1 + 1, (z0, z0^3*z1^2))
        sage: Omega_ge(0, (3, 1, -3))
        (-z0^3*z1^3*z2^3 + 2*z0^2*z1^3*z2^2 - z0*z1^3*z2
         + z0^2*z2^2 - 2*z0*z2 + 1,
         (z0, z1, z0*z2, z0*z2, z0*z2, z1^3*z2))

    ::

        sage: Omega_ge(0, (3, 6, -1))
        (-z0*z1*z2^8 - z0*z1*z2^7 - z0*z1*z2^6 - z0*z1*z2^5 - z0*z1*z2^4 +
         z1*z2^5 - z0*z1*z2^3 + z1*z2^4 - z0*z1*z2^2 + z1*z2^3 -
         z0*z1*z2 + z0*z2^2 + z1*z2^2 + z0*z2 + z1*z2 + 1,
         (z0, z1, z0*z2^3, z1*z2^6))

    TESTS::

        sage: Omega_ge(0, (2, 2, 1, 1, 1, 1, 1, -1, -1))[0].number_of_terms()  # long time
        27837

    ::

        sage: Omega_ge(1, (2,))
        (1, (z0,))
    """
    import logging
    logger = logging.getLogger(__name__)
    logger.info('Omega_ge: a=%s, exponents=%s', a, exponents)

    from sage.arith.all import lcm, srange
    from sage.rings.integer_ring import ZZ
    from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
    from sage.rings.number_field.number_field import CyclotomicField

    if not exponents or any(e == 0 for e in exponents):
        raise NotImplementedError

    rou = sorted(set(abs(e) for e in exponents) - set([1]))
    ellcm = lcm(rou)
    B = CyclotomicField(ellcm, 'zeta')
    zeta = B.gen()
    z_names = tuple('z{}'.format(i) for i in range(len(exponents)))
    L = LaurentPolynomialRing(B, ('t',) + z_names, len(z_names) + 1)
    t = L.gens()[0]
    Z = LaurentPolynomialRing(ZZ, z_names, len(z_names))
    powers = {i: L(zeta**(ellcm//i)) for i in rou}
    powers[2] = L(-1)
    powers[1] = L(1)
    exponents_and_values = tuple(
        (e, tuple(powers[abs(e)]**j * z for j in srange(abs(e))))
        for z, e in zip(L.gens()[1:], exponents))
    x = tuple(v for e, v in exponents_and_values if e > 0)
    y = tuple(v for e, v in exponents_and_values if e < 0)

    def subs_power(expression, var, exponent):
        r"""
        Substitute ``var^exponent`` by ``var`` in ``expression``.

        It is assumed that ``var`` only occurs with exponents
        divisible by ``exponent``.
        """
        p = tuple(var.dict().popitem()[0]).index(1)  # var is the p-th generator
        def subs_e(e):
            e = list(e)
            assert e[p] % exponent == 0
            e[p] = e[p] // exponent
            return tuple(e)
        parent = expression.parent()
        result = parent({subs_e(e): c for e, c in iteritems(expression.dict())})
        return result

    def de_power(expression):
        expression = Z(expression)
        for e, var in zip(exponents, Z.gens()):
            if abs(e) == 1:
                continue
            expression = subs_power(expression, var, abs(e))
        return expression

    logger.debug('Omega_ge: preparing denominator')
    factors_denominator = tuple(de_power(1 - factor)
                                for factor in _Omega_factors_denominator_(x, y))

    logger.debug('Omega_ge: preparing numerator')
    numerator = de_power(_Omega_numerator_(a, x, y, t))

    logger.info('Omega_ge: completed')
    return numerator, factors_denominator
Beispiel #51
0
def row_reduced_form(M,transformation=False):
    """
    This function computes a row reduced form of a matrix over a rational
    function field `k(x)`, for `k` a field.

    INPUT:

     - `M` - a matrix over `k(x)` or `k[x]` for `k` a field.
     - `transformation` - A boolean (default: `False`). If this boolean is set to `True` a second matrix is output (see OUTPUT).

    OUTPUT:

    If `transformation` is `False`, the output is `W`, a row reduced form of `M`.

    If `transformation` is `True`, this function will output a pair `(W,N)` consisting of two matrices over `k(x)`:

    1. `W` - a row reduced form of `M`.
    2. `N` - an invertible matrix over `k(x)` satisfying `NW = M`.

    EXAMPLES:

    The function expects matrices over the rational function field, but
    other examples below show how one can provide matrices over the ring
    of polynomials (whose quotient field is the rational function field).

    ::

        sage: R.<t> = GF(3)['t']
        sage: K = FractionField(R)
        sage: import sage.matrix.matrix_misc
        sage: sage.matrix.matrix_misc.row_reduced_form(matrix([[(t-1)^2/t],[(t-1)]]))
        doctest:...: DeprecationWarning: Row reduced form will soon be supported only for matrices of polynomials.
        See http://trac.sagemath.org/21024 for details.
        [        0]
        [(t + 2)/t]

    The last example shows the usage of the transformation parameter.

    ::
        sage: Fq.<a> = GF(2^3)
        sage: Fx.<x> = Fq[]
        sage: A = matrix(Fx,[[x^2+a,x^4+a],[x^3,a*x^4]])
        sage: from sage.matrix.matrix_misc import row_reduced_form
        sage: row_reduced_form(A,transformation=True)
        (
        [          x^2 + a           x^4 + a]  [1 0]
        [x^3 + a*x^2 + a^2               a^2], [a 1]
        )

    NOTES:

    See docstring for row_reduced_form method of matrices for
    more information.
    """
    from sage.misc.superseded import deprecation
    deprecation(21024, "Row reduced form will soon be supported only for matrices of polynomials.")

    # determine whether M has polynomial or rational function coefficients
    R0 = M.base_ring()

    #Compute the base polynomial ring
    if R0 in _Fields:
        R = R0.base()
    else:
        R = R0
    from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
    if not is_PolynomialRing(R) or not R.base_ring().is_field():
        raise TypeError("the coefficients of M must lie in a univariate polynomial ring over a field")

    t = R.gen()

    # calculate least-common denominator of matrix entries and clear
    # denominators. The result lies in R
    from sage.arith.all import lcm
    from sage.matrix.constructor import matrix
    from sage.misc.functional import numerator
    if R0 in _Fields:
        den = lcm([a.denominator() for a in M.list()])
        num = matrix([[numerator(_) for _ in v] for v in (M*den).rows()])
    else:
        # No need to clear denominators
        num = M

    if transformation:
        A, N = num.row_reduced_form(transformation=True)
    else:
        A = num.row_reduced_form(transformation=False)

    if not is_PolynomialRing(R0):
        A = ~den * A

    if transformation:
        return (A, N)
    else:
        return A
Beispiel #52
0
def row_reduced_form(M,transformation=False):
    """
    This function computes a row reduced form of a matrix over a rational
    function field `k(x)`, for `k` a field.

    INPUT:

     - `M` - a matrix over `k(x)` or `k[x]` for `k` a field.
     - `transformation` - A boolean (default: `False`). If this boolean is set to `True` a second matrix is output (see OUTPUT).
     
    OUTPUT:

    If `transformation` is `False`, the output is `W`, a row reduced form of `M`.
    
    If `transformation` is `True`, this function will output a pair `(W,N)` consisting of two matrices over `k(x)`:

    1. `W` - a row reduced form of `M`.
    2. `N` - an invertible matrix over `k(x)` satisfying `NW = M`.

    EXAMPLES:

    The fuction expects matrices over the rational function field, but
    other examples below show how one can provide matrices over the ring
    of polynomials (whose quotient field is the rational function field).

    ::

        sage: R.<t> = GF(3)['t']
        sage: K = FractionField(R)
        sage: import sage.matrix.matrix_misc
        sage: sage.matrix.matrix_misc.row_reduced_form(matrix([[(t-1)^2/t],[(t-1)]]))
        [(2*t + 1)/t]
        [          0]

    The last example shows the usage of the transformation parameter.
        
    ::
        sage: Fq.<a> = GF(2^3)
        sage: Fx.<x> = Fq[]
        sage: A = matrix(Fx,[[x^2+a,x^4+a],[x^3,a*x^4]])
        sage: from sage.matrix.matrix_misc import row_reduced_form
        sage: row_reduced_form(A,transformation=True)
        (
        [(a^2 + 1)*x^3 + x^2 + a                       a]  [      1 a^2 + 1]
        [                    x^3                   a*x^4], [      0                 1]
        )
            
    NOTES:

    See docstring for row_reduced_form method of matrices for
    more information.
    """

    # determine whether M has polynomial or rational function coefficients
    R0 = M.base_ring()

    #Compute the base polynomial ring
    if R0 in _Fields:
        R = R0.base()
    else:
        R = R0
    from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
    if not is_PolynomialRing(R) or not R.base_ring().is_field():
        raise TypeError("the coefficients of M must lie in a univariate polynomial ring over a field")

    t = R.gen()

    # calculate least-common denominator of matrix entries and clear
    # denominators. The result lies in R
    from sage.arith.all import lcm
    from sage.matrix.constructor import matrix
    from sage.misc.functional import numerator
    if R0 in _Fields:
        den = lcm([a.denominator() for a in M.list()])
        num = matrix([[numerator(_) for _ in v] for v in (M*den).rows()])
    else:
        # No need to clear denominators
        num = M

    r = [list(v) for v in num.rows()]

    if transformation:
        N = matrix(num.nrows(), num.nrows(), R(1)).rows()


    rank = 0
    num_zero = 0
    if M.is_zero():
        num_zero = len(r)
    while rank != len(r) - num_zero:
        # construct matrix of leading coefficients
        v = []
        for w in map(list, r):
            # calculate degree of row (= max of degree of entries)
            d = max([e.numerator().degree() for e in w])

            # extract leading coefficients from current row
            x = []
            for y in w:
                if y.degree() >= d and d >= 0:   x.append(y.coefficients(sparse=False)[d])
                else:                            x.append(0)
            v.append(x)
        l = matrix(v)

        # count number of zero rows in leading coefficient matrix
        # because they do *not* contribute interesting relations
        num_zero = 0
        for v in l.rows():
            is_zero = 1
            for w in v:
                if w != 0:
                    is_zero = 0
            if is_zero == 1:
                num_zero += 1

        # find non-trivial relations among the columns of the
        # leading coefficient matrix
        kern = l.kernel().basis()
        rank = num.nrows() - len(kern)

        # do a row operation if there's a non-trivial relation
        if not rank == len(r) - num_zero:
            for rel in kern:
                # find the row of num involved in the relation and of
                # maximal degree
                indices = []
                degrees = []
                for i in range(len(rel)):
                    if rel[i] != 0:
                        indices.append(i)
                        degrees.append(max([e.degree() for e in r[i]]))

                # find maximum degree among rows involved in relation
                max_deg = max(degrees)

                # check if relation involves non-zero rows
                if max_deg != -1:
                    i = degrees.index(max_deg)
                    rel /= rel[indices[i]]

                    for j in range(len(indices)):
                        if j != i:
                            # do the row operation
                            v = []
                            for k in range(len(r[indices[i]])):
                                v.append(r[indices[i]][k] + rel[indices[j]] * t**(max_deg-degrees[j]) * r[indices[j]][k])
                            r[indices[i]] = v

                            if transformation:
                                # If the user asked for it, record the row operation 
                                v = []
                                for k in range(len(N[indices[i]])):
                                    v.append(N[indices[i]][k] + rel[indices[j]] * t**(max_deg-degrees[j]) * N[indices[j]][k])
                                N[indices[i]] = v

                    # remaining relations (if any) are no longer valid,
                    # so continue onto next step of algorithm
                    break
    if is_PolynomialRing(R0):
        A = matrix(R, r)
    else:
        A = matrix(R, r)/den
    if transformation:
        return (A, matrix(N))
    else:
        return A
def CyclicSievingPolynomial(L, cyc_act=None, order=None, get_order=False):
    """
    Returns the unique polynomial p of degree smaller than order such that the triple
    ( L, cyc_act, p ) exhibits the CSP. If ``cyc_act`` is None, ``L`` is expected to contain the orbit lengths.

    INPUT:

    - L -- if cyc_act is None: list of orbit sizes, otherwise list of objects

    - cyc_act -- (default:None) function taking an element of L and returning an element of L (must define a bijection on L)

    - order -- (default:None) if set to an integer, this cyclic order of cyc_act is used (must be an integer multiple of the order of cyc_act)
                               otherwise, the order of cyc_action is used

    - get_order -- (default:False) if True, a tuple [p,n] is returned where p as above, and n is the order

    EXAMPLES::

        sage: from sage.combinat.cyclic_sieving_phenomenon import CyclicSievingPolynomial
        sage: S42 = [ Set(S) for S in subsets([1,2,3,4]) if len(S) == 2 ]; S42
        [{1, 2}, {1, 3}, {2, 3}, {1, 4}, {2, 4}, {3, 4}]
        sage: cyc_act = lambda S: Set( i.mod(4)+1 for i in S)
        sage: cyc_act([1,3])
        {2, 4}
        sage: cyc_act([1,4])
        {1, 2}
        sage: CyclicSievingPolynomial( S42, cyc_act )
        q^3 + 2*q^2 + q + 2
        sage: CyclicSievingPolynomial( S42, cyc_act, get_order=True )
        [q^3 + 2*q^2 + q + 2, 4]
        sage: CyclicSievingPolynomial( S42, cyc_act, order=8 )
        q^6 + 2*q^4 + q^2 + 2
        sage: CyclicSievingPolynomial([4,2])
        q^3 + 2*q^2 + q + 2

    TESTS:

    We check that :trac:`13997` is handled::

        sage: CyclicSievingPolynomial( S42, cyc_act, order=8, get_order=True )
        [q^6 + 2*q^4 + q^2 + 2, 8]
    """
    if cyc_act:
        orbits = orbit_decomposition( L, cyc_act )
    else:
        orbits = [list(range(k)) for k in L]

    R = QQ['q']
    q = R.gen()
    p = R(0)

    orbit_sizes = {}
    for orbit in orbits:
        l = len(orbit)
        if l in orbit_sizes:
            orbit_sizes[l] += 1
        else:
            orbit_sizes[l] = 1

    n = lcm(list(orbit_sizes))

    if order:
        if order.mod(n) != 0:
            raise ValueError("The given order is not valid as it is not a multiple of the order of the given cyclic action")
    else:
        order = n

    for i in range(n):
        if i == 0:
            j = sum(orbit_sizes.values())
        else:
            j = sum(orbit_sizes[l] for l in orbit_sizes if ZZ(i).mod(n/l) == 0)
        p += j*q**i

    p = p(q**ZZ(order/n))

    if get_order:
        return [p, order]
    else:
        return p
Beispiel #54
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        ALGORITHM:

        Uses the PARI/GP function ``qfparam``.

        EXAMPLES ::

            sage: c = Conic([1,1,-1])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Rational Field
              To:   Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              Defn: Defined on coordinates by sending (x : y) to
                    (2*x*y : x^2 - y^2 : x^2 + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              To:   Projective Space of dimension 1 over Rational Field
              Defn: Defined on coordinates by sending (x : y : z) to
                    (1/2*x : -1/2*y + 1/2*z))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + 2*y^2 + z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            Q = PolynomialRing(QQ, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            M = self.symmetric_matrix()
            M *= lcm([ t.denominator() for t in M.list() ])
            par1 = qfparam(M, point)
            B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)])
            # self is in the image of B and does not lie on a line,
            # hence B is invertible
            A = B.inverse()
            par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]]
            par = ([Q(pol(x/y)*y**2) for pol in par1], par2)
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
 def _reduce_conic(self):
     r"""
     Return the reduced form of the conic, i.e. a conic with base field
     `K=F(t)` and coefficients `a,b,c` such that `a,b,c \in F[t]`,
     `\gcd(a,b)=\gcd(b,c)=\gcd(c,a)=1` and `abc` is square-free.
     
     Assumes `self` is in diagonal form.
     
     OUTPUT:
     
     A tuple (coefficients, multipliers), the coefficients of the conic
     in reduced form and multipliers `\lambda, \mu, \nu \in F(t)^*` such
     that `(x,y,z) \in F(t)` is a solution of the reduced conic if and only
     if `(\lambda x, \mu y, \nu z)` is a solution of `self`.
     
     ALGORITMH:
     
     The algorithm used is the algorithm ReduceConic in [HC2006]_.
     
     EXAMPLES::
     
         sage: K.<t> = FractionField(PolynomialRing(QQ, 't'))
         sage: C = Conic(K, [t^2-2, 2*t^3, -2*t^3-13*t^2-2*t+18])
         sage: C._reduce_conic()
         ([t^2 - 2, 2*t, -2*t^3 - 13*t^2 - 2*t + 18], [t, 1, t])
     """
     
     # start with removing fractions
     coeff = [self.coefficients()[0], self.coefficients()[3],
             self.coefficients()[5]]
     coeff = lcm(lcm(coeff[0].denominator(), coeff[1].denominator()),
             coeff[2].denominator()) * vector(coeff)
     # go to base ring of fraction field
     coeff = [self.base().base()(x) for x in coeff]
     coeff = vector(coeff) / gcd(coeff)
     # remove common divisors
     labda = mu = nu = 1
     g1 = g2 = g3 = 0
     ca, cb, cc = coeff
     while g1 != 1 or g2 != 1 or g3 != 1:
         g1 = gcd(ca,cb); ca = ca/g1; cb = cb/g1; cc = cc*g1; nu = g1*nu
         g2 = gcd(ca,cc); ca = ca/g2; cc = cc/g2; cb = cb*g2; mu = g2*mu
         g3 = gcd(cb,cc); cb = cb/g3; cc = cc/g3; ca = ca*g3;
         labda = g3*labda
     coeff = [ca, cb, cc]
     multipliers = [labda, mu, nu]
     
     # remove squares
     for i, x in enumerate(coeff):
         if is_FractionField(x.parent()):
             # go to base ring of fraction field
             x = self.base().base()(x)
         
         try:
             decom = x.squarefree_decomposition()
         except (NotImplementedError, AttributeError):
             decom = x.factor()
         x = decom.unit(); x2 = 1
         for factor in decom:
             if factor[1] > 1:
                 if factor[1] % 2 == 0:
                     x2 = x2 * factor[0] ** (factor[1] / 2)
                 else:
                     x = x * factor[0]
                     x2 = x2 * factor[0] ** ((factor[1]-1) / 2)
             else:
                 x = x * factor[0]
         for j, y in enumerate(multipliers):
             if j != i:
                 multipliers[j] = y * x2
         coeff[i] = self.base_ring().base().coerce(x);
     
     return (coeff, multipliers)
    def period(self, m):
        """
        Return the period of the binary recurrence sequence modulo
        an integer ``m``.

        If `n_1` is congruent to `n_2` modulu ``period(m)``, then `u_{n_1}` is
        is congruent to `u_{n_2}` modulo ``m``.

        INPUT:

        - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated).

        OUTPUT:

        - The integer (the period of the sequence modulo m)

        EXAMPLES:

        If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence
        mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS06]).

        ::

            sage: R = BinaryRecurrenceSequence(1,1)
            sage: R.period(31)
            30

            sage: [R(i) % 4 for i in range(12)]
            [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
            sage: R.period(4)
            6

        This function works for degenerate sequences as well.

        ::

            sage: S = BinaryRecurrenceSequence(2,0,1,2)
            sage: S.is_degenerate()
            True
            sage: S.is_geometric()
            True
            sage: [S(i) % 17 for i in range(16)]
            [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9]
            sage: S.period(17)
            8

        Note: the answer is cached.
        """

        #If we have already computed the period mod m, then we return the stored value.

        if m in self._period_dict:
            return self._period_dict[m]

        else:
            R = Integers(m)
            A = matrix(R, [[0, 1], [self.c, self.b]])
            w = matrix(R, [[self.u0], [self.u1]])
            Fac = list(m.factor())
            Periods = {}

            #To compute the period mod m, we compute the least integer n such that A^n*w == w.  This necessarily
            #divides the order of A as a matrix in GL_2(Z/mZ).

            #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm.
            #To compute the period mod p^e, we first compute the order mod p.  Then the period mod p^e
            #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to
            #the identity mod p is of order (p^{e-1})^4.  So we compute the period mod p^e by successively
            #multiplying the period mod p by powers of p.

            for i in Fac:
                p = i[0]
                e = i[1]
                #first compute the period mod p
                if p in self._period_dict:
                    perp = self._period_dict[p]
                else:
                    F = A.change_ring(GF(p))
                    v = w.change_ring(GF(p))
                    FF = F**(p - 1)
                    p1fac = list((p - 1).factor())

                    #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1).
                    #The order divides p-1 if it is diagonalizable.  In any case, det(F^(p-1))=1,
                    #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]].
                    #The order of the subgroup of matrices of this form is p, so the order must divide
                    #p(p-1) -- in fact it must be a multiple of p.  If this is not the case, then the
                    #order divides (p-1)(p+1).  As the period divides the order of the matrix in GL_2(F_p),
                    #these conditions hold for the period as well.

                    #check if the order divides (p-1)
                    if FF * v == v:
                        M = p - 1
                        Mfac = p1fac

                    #check if the trace is 2, then the order is a multiple of p dividing p*(p-1)
                    elif (FF).trace() == 2:
                        M = p - 1
                        Mfac = p1fac
                        F = F**p  #replace F by F^p as now we only need to determine the factor dividing (p-1)

                    #otherwise it will divide (p+1)(p-1)
                    else:
                        M = (p + 1) * (p - 1)
                        p2fac = list(
                            (p + 1).factor()
                        )  #factor the (p+1) and (p-1) terms separately and then combine for speed
                        Mfac_dic = {}
                        for i in list(p1fac + p2fac):
                            if i[0] not in Mfac_dic:
                                Mfac_dic[i[0]] = i[1]
                            else:
                                Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1]
                        Mfac = [(i, Mfac_dic[i]) for i in Mfac_dic]

                    #Now use a fast order algorithm to compute the period.  We know that the period divides
                    #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors.  As
                    #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j).  After
                    #all factors have been iterated over, the result is the period mod p.

                    Mfac = list(Mfac)
                    C = []

                    #expand the list of prime factors so every factor is with multiplicity 1

                    for i in range(len(Mfac)):
                        for j in range(Mfac[i][1]):
                            C.append(Mfac[i][0])

                    Mfac = C
                    n = M
                    for i in Mfac:
                        b = Integer(n / i)
                        if F**b * v == v:
                            n = b
                    perp = n

                #Now compute the period mod p^e by stepping up by multiples of p
                F = A.change_ring(Integers(p**e))
                v = w.change_ring(Integers(p**e))
                FF = F**perp
                if FF * v == v:
                    perpe = perp
                else:
                    tries = 0
                    while True:
                        tries += 1
                        FF = FF**p
                        if FF * v == v:
                            perpe = perp * p**tries
                            break
                Periods[p] = perpe

            #take the lcm of the periods mod all distinct primes dividing m
            period = 1
            for p in Periods:
                period = lcm(Periods[p], period)

            self._period_dict[m] = period  #cache the period mod m
            return period
    def get_twodim_cocycle(self,sign = 1,use_magma = True,bound = 5, hecke_data = None, return_all = False, outfile = None):
        F = self.group().base_ring()
        if F == QQ:
            F = NumberField(PolynomialRing(QQ,'x').gen(),names='r')
        component_list = []
        good_components = []
        if F.signature()[1] == 0 or (F.signature() == (0,1) and 'G' not in self.group()._grouptype):
            Tinf = self.hecke_matrix(oo).transpose()
            K = (Tinf-sign).kernel().change_ring(QQ)
            if K.dimension() >= 2:
                component_list.append((K, [(oo,Tinf)]))
            fwrite('Too charpoly = %s'%Tinf.charpoly().factor(),outfile)
        else:
            K = Matrix(QQ,self.dimension(),self.dimension(),0).kernel()
            if K.dimension() >= 2:
                component_list.append((K, []))
        disc = self.S_arithgroup().Gpn._O_discriminant
        discnorm = disc.norm()
        try:
            N = ZZ(discnorm.gen())
        except AttributeError:
            N = ZZ(discnorm)

        if F == QQ:
            x = QQ['x'].gen()
            F = NumberField(x,names='a')
        q = ZZ(1)
        g0 = None
        num_hecke_operators = 0
        if hecke_data is not None:
            qq = F.ideal(hecke_data[0])
            pol = hecke_data[1]
            Aq = self.hecke_matrix(qq.gens_reduced()[0], use_magma = use_magma).transpose().change_ring(QQ)
            fwrite('ell = (%s,%s), T_ell charpoly = %s'%(qq.norm(), qq.gens_reduced()[0], Aq.charpoly().factor()),outfile)
            U0 = component_list[0][0].intersection(pol.subs(Aq).left_kernel())
            if U0.dimension() != 2:
                raise ValueError('Hecke data does not suffice to cut out space')
            good_component = (U0.denominator() * U0,component_list[0][1] + [(qq.gens_reduced()[0],Aq)])
            row0 = good_component[0].matrix().rows()[0]
            col0 = [QQ(o) for o in row0.list()]
            clcm = lcm([o.denominator() for o in col0])
            col0 = [ZZ(clcm * o ) for o in col0]
            # phi1 = sum([a * phi for a,phi in zip(col0,self.gens())],self(0))
            # phi2 = self.apply_hecke_operator(phi1,qq.gens_reduced()[0], use_magma = use_magma)
            # return [phi1, phi2], [(ell, o.restrict(good_component[0])) for ell, o in good_component[1]]
            flist = []
            for row0 in good_component[0].matrix().rows():
                col0 = [QQ(o) for o in row0.list()]
                clcm = lcm([o.denominator() for o in col0])
                col0 = [ZZ(clcm * o ) for o in col0]
                flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0)))
            return flist,[(ell, o.restrict(good_component[0])) for ell, o in good_component[1]]
        while len(component_list) > 0 and num_hecke_operators < bound:
            verbose('num_hecke_ops = %s'%num_hecke_operators)
            verbose('len(components_list) = %s'%len(component_list))
            q = q.next_prime()
            verbose('q = %s'%q)
            fact = F.ideal(q).factor()
            dfact = F.ideal(disc.gens_reduced()[0]).factor()
            for qq,e in fact:
                verbose('Trying qq = %s'%qq)
                if qq in [o for o,_ in dfact]:
                    verbose('Skipping because qq divides D...')
                    continue
                if  ZZ(qq.norm()).is_prime() and not qq.divides(F.ideal(disc.gens_reduced()[0])):
                    try:
                        Aq = self.hecke_matrix(qq.gens_reduced()[0],g0 = g0,use_magma = use_magma).transpose().change_ring(QQ)
                    except (RuntimeError,TypeError) as e:
                        verbose('Skipping qq (=%s) because Hecke matrix could not be computed...'%qq.gens_reduced()[0])
                        continue
                    except KeyboardInterrupt:
                        verbose('Skipping qq (=%s) by user request...'%qq.gens_reduced()[0])
                        num_hecke_operators += 1
                        sleep(1)
                        continue
                    verbose('Computed hecke matrix at qq = %s'%qq)
                    fwrite('ell = (%s,%s), T_ell charpoly = %s'%(qq.norm(), qq.gens_reduced()[0], Aq.charpoly().factor()),outfile)
                    old_component_list = component_list
                    component_list = []
                    num_hecke_operators += 1
                    for U,hecke_data in old_component_list:
                        V = Aq.decomposition_of_subspace(U)
                        for U0,is_irred in V:
                            if U0.dimension() == 1:
                                continue
                            if U0.dimension() == 2 and is_irred:
                                good_components.append((U0.denominator() * U0,hecke_data+[(qq.gens_reduced()[0],Aq)]))
                            else: # U0.dimension() > 2 or not is_irred
                                component_list.append((U0.denominator() * U0,hecke_data + [(qq.gens_reduced()[0],Aq)]))
                    if len(good_components) > 0 and not return_all:
                        flist = []
                        for row0 in good_components[0][0].matrix().rows():
                            col0 = [QQ(o) for o in row0.list()]
                            clcm = lcm([o.denominator() for o in col0])
                            col0 = [ZZ(clcm * o ) for o in col0]
                            flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0)))
                        return flist,[(ell, o.restrict(good_components[0][0])) for ell, o in good_components[0][1]]
                    if len(component_list) == 0 or num_hecke_operators >= bound:
                        break

        if len(good_components) == 0:
            if not return_all:
                raise ValueError('Group does not seem to be attached to an abelian surface')
            else:
                return []
        if return_all:
            ans = []
            for K,hecke_data in good_components:
                flist = []
                for row0 in K.matrix().rows():
                    col0 = [QQ(o) for o in row0.list()]
                    clcm = lcm([o.denominator() for o in col0])
                    col0 = [ZZ(clcm * o ) for o in col0]
                    flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0)))
                ans.append((flist,[(ell, o.restrict(K)) for ell, o in hecke_data]))
            return ans
        else:
            flist = []
            for row0 in good_components[0][0].matrix().rows():
                col0 = [QQ(o) for o in row0.list()]
                clcm = lcm([o.denominator() for o in col0])
                col0 = [ZZ(clcm * o ) for o in col0]
                flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0)))
            return flist,[(ell, o.restrict(good_components[0][0])) for ell, o in good_components[0][1]]
def cyclotomic_restriction(L, K):
    r"""
    Given two cyclotomic fields L and K, compute the compositum
    M of K and L, and return a function and the index [M:K]. The
    function is a map that acts as follows (here `M = Q(\zeta_m)`):

    INPUT:

    element alpha in L

    OUTPUT:

    a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`,
    where we view alpha as living in `M`. (Note that `\zeta_m`
    generates `M`, not `L`.)

    EXAMPLES::

        sage: L = CyclotomicField(12) ; N = CyclotomicField(33) ; M = CyclotomicField(132)
        sage: z, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N)
        sage: n
        2

        sage: z(L.0)
        -zeta33^19*x
        sage: z(L.0)(M.0)
        zeta132^11

        sage: z(L.0^3-L.0+1)
        (zeta33^19 + zeta33^8)*x + 1
        sage: z(L.0^3-L.0+1)(M.0)
        zeta132^33 - zeta132^11 + 1
        sage: z(L.0^3-L.0+1)(M.0) - M(L.0^3-L.0+1)
        0
    """
    if not L.has_coerce_map_from(K):
        M = CyclotomicField(lcm(L.zeta_order(), K.zeta_order()))
        f = cyclotomic_restriction_tower(M, K)

        def g(x):
            r"""
            Function returned by cyclotomic restriction.

            INPUT:

            element alpha in L

            OUTPUT:

            a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`,
            where we view alpha as living in `M`. (Note that `\zeta_m`
            generates `M`, not `L`.)

            EXAMPLES::

                sage: L = CyclotomicField(12)
                sage: N = CyclotomicField(33)
                sage: g, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N)
                sage: g(L.0)
                -zeta33^19*x
            """
            return f(M(x))

        return g, euler_phi(M.zeta_order()) // euler_phi(K.zeta_order())
    else:
        return cyclotomic_restriction_tower(L,K), \
               euler_phi(L.zeta_order())//euler_phi(K.zeta_order())
Beispiel #59
0
    def normal_cone(self):
        r"""
        Return the (closure of the) normal cone of the triangulation.

        Recall that a regular triangulation is one that equals the
        "crease lines" of a convex piecewise-linear function. This
        support function is not unique, for example, you can scale it
        by a positive constant. The set of all piecewise-linear
        functions with fixed creases forms an open cone. This cone can
        be interpreted as the cone of normal vectors at a point of the
        secondary polytope, which is why we call it normal cone. See
        [GKZ1994]_ Section 7.1 for details.

        OUTPUT:

        The closure of the normal cone. The `i`-th entry equals the
        value of the piecewise-linear function at the `i`-th point of
        the configuration.

        For an irregular triangulation, the normal cone is empty. In
        this case, a single point (the origin) is returned.

        EXAMPLES::

            sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal')
            sage: triangulation
            (<0,1,3>, <0,2,3>)
            sage: N = triangulation.normal_cone();  N
            4-d cone in 4-d lattice
            sage: N.rays()
            (-1,  0,  0,  0),
            ( 1,  0,  1,  0),
            (-1,  0, -1,  0),
            ( 1,  0,  0, -1),
            (-1,  0,  0,  1),
            ( 1,  1,  0,  0),
            (-1, -1,  0,  0)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring
            sage: N.dual().rays()
            (-1, 1, 1, -1)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring

        TESTS::

            sage: polytopes.simplex(2).triangulate().normal_cone()
            3-d cone in 3-d lattice
            sage: _.dual().is_trivial()
            True
        """
        if not self.point_configuration().base_ring().is_subring(QQ):
            raise NotImplementedError('Only base rings ZZ and QQ are supported')
        from ppl import Constraint_System, Linear_Expression, C_Polyhedron
        from sage.matrix.constructor import matrix
        from sage.arith.all import lcm
        pc = self.point_configuration()
        cs = Constraint_System()
        for facet in self.interior_facets():
            s0, s1 = self._boundary_simplex_dictionary()[facet]
            p = set(s0).difference(facet).pop()
            q = set(s1).difference(facet).pop()
            origin = pc.point(p).reduced_affine_vector()
            base_indices = [i for i in s0 if i != p]
            base = matrix([ pc.point(i).reduced_affine_vector()-origin for i in base_indices ])
            sol = base.solve_left( pc.point(q).reduced_affine_vector()-origin )
            relation = [0]*pc.n_points()
            relation[p] = sum(sol)-1
            relation[q] = 1
            for i, base_i in enumerate(base_indices):
                relation[base_i] = -sol[i]
            rel_denom = lcm([QQ(r).denominator() for r in relation])
            relation = [ ZZ(r*rel_denom) for r in relation ]
            ex = Linear_Expression(relation,0)
            cs.insert(ex >= 0)
        from sage.modules.free_module import FreeModule
        ambient = FreeModule(ZZ, self.point_configuration().n_points())
        if cs.empty():
            cone = C_Polyhedron(ambient.dimension(), 'universe')
        else:
            cone = C_Polyhedron(cs)
        from sage.geometry.cone import _Cone_from_PPL
        return _Cone_from_PPL(cone, lattice=ambient)