예제 #1
0
def possible_isogeny_degrees(E, verbose=False):
    r""" Return a list of primes `\ell` sufficient to generate the
    isogeny class of `E`.

    INPUT:

    - ``E`` -- An elliptic curve defined over a number field.

    OUTPUT:

    A finite list of primes `\ell` such that every curve isogenous to
    this curve can be obtained by a finite sequence of isogenies of
    degree one of the primes in the list.

    ALGORITHM:

    For curves without CM, the set may be taken to be the finite set
    of primes at which the Galois representation is not surjective,
    since the existence of an `\ell`-isogeny is equivalent to the
    image of the mod-`\ell` Galois representation being contained in a
    Borel subgroup.

    For curves with CM by the order `O` of discriminant `d`, the
    Galois representation is always non-surjective and the curve will
    admit `\ell`-isogenies for infinitely many primes `\ell`, but
    there are (of course) only finitely many codomains `E'`.  The
    primes can be divided according to the discriminant `d'` of the CM
    order `O'` associated to `E`: either `O=O'`, or one contains the
    other with index `\ell`, since `\ell O\subset O'` and vice versa.

    Case (1): `O=O'`.  The degrees of all isogenies between `E` and
    `E'` are precisely the integers represented by one of the classes
    of binary quadratic forms `Q` of discriminant `d`.  Hence to
    obtain all possible isomorphism classes of codomain `E'`, we need
    only use one prime `\ell` represented by each such class `Q`.  It
    would in fact suffice to use primes represented by forms which
    generate the class group.  Here we simply omit the principal class
    and one from each pair of inverse classes, and include a prime
    represented by each of the remaining forms.

    Case (2): `[O':O]=\ell`: so `d=\ell^2d;`.  We include all prime
    divisors of `d`.

    Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide
    `d` as we have already included these, so `\ell` either splits or
    is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)`
    accordingly.  We include all primes `\ell` such that `\ell\pm1`
    divides the degree `[K:\QQ]`.

    For curves with only potential CM we proceed as in the CM case,
    using `2[K:\QQ]` instead of `[K:\QQ]`.

    EXAMPLES:

    For curves without CM we determine the primes at which the mod `p`
    Galois representation is reducible, i.e. contained in a Borel
    subgroup::

        sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees
        sage: E = EllipticCurve('11a1')
        sage: possible_isogeny_degrees(E)
        [5]

    We check that in this case `E` really does have rational
    `5`-isogenies::

        sage: [phi.degree() for phi in E.isogenies_prime_degree()]
        [5, 5]

    Over an extension field::

        sage: E3 = E.change_ring(CyclotomicField(3))
        sage: possible_isogeny_degrees(E3)
        [5]
        sage: [phi.degree() for phi in E3.isogenies_prime_degree()]
        [5, 5]

    For curves with CM by a quadratic order of class number greater
    than `1`, we use the structure of the class group to only give one
    prime in each ideal class::

        sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1])
        sage: L.<a> = NumberField(pol)
        sage: j = hilbert_class_polynomial(-23).roots(L,multiplicities=False)[0]
        sage: E = EllipticCurve(j=j)
        sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees
        sage: possible_isogeny_degrees(E, verbose=True)
        CM case, discriminant = -23
        initial primes: {2}
        upward primes: {}
        downward ramified primes: {}
        downward split primes: {2, 3}
        downward inert primes: {5}
        primes generating the class group: [2]
        Complete set of primes: {2, 3, 5}
        [2, 3, 5]
    """
    if E.has_cm():
        d = E.cm_discriminant()

        if verbose:
            print("CM case, discriminant = %s" % d)

        from sage.libs.pari.all import pari
        from sage.sets.all import Set
        from sage.rings.arith import kronecker_symbol

        n = E.base_field().absolute_degree()
        if not E.has_rational_cm():
            n *= 2
        divs = n.divisors()

        data = pari(d).quadclassunit()
        # This has 4 components: the class number, class group
        # structure (ignored), class group generators (as quadratic
        # forms) and regulator (=1 since d<0, ignored).

        h = data[0].sage()

        # We must have 2*h dividing n, and will need the quotient so
        # see if the j-invariants of any proper sub-orders could lie
        # in the same field

        n_over_2h = n//(2*h)

        # Collect possible primes.  First put in 2, and also 3 for
        # discriminant -3 (special case because of units):

        L = Set([ZZ(2), ZZ(3)]) if d==-3 else  Set([ZZ(2)])
        if verbose:
            print ("initial primes: %s" % L)

        # Step 1: "vertical" primes l such that the isogenous curve
        # has CM by an order whose index is l or 1/l times the index
        # of the order O of discriminant d.  The latter case can only
        # happen when l^2 divides d.

        # Compute the ramified primes

        ram_l = d.odd_part().prime_factors()

        # if the CM is not rational we include all ramified primes,
        # which is simpler than using the class group later:

        if not E.has_rational_cm():
            L1 = Set(ram_l)
            L += L1
            if verbose:
                print ("ramified primes: %s" % L1)

        else:

            # Find the "upward" primes (index divided by l):

            L1 = Set([l for l in ram_l if d.valuation(l)>1])
            L += L1
            if verbose:
                print ("upward primes: %s" % L1)

            # Find the "downward" primes (index multiplied by l, class
            # number multiplied by l-kronecker_symbol(d,l)):

            # (a) ramified primes; the suborder has class number l*h, so l
            # must divide n/2h:

            L1 = Set([l for l in ram_l if l.divides(n_over_2h)])
            L += L1
            if verbose:
                print ("downward ramified primes: %s" % L1)

        # (b) split primes; the suborder has class number (l-1)*h, so
        # l-1 must divide n/2h:

        L1 = Set([lm1+1 for lm1 in divs
                  if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1)==+1])
        L += L1
        if verbose:
            print ("downward split primes: %s" % L1)

        # (c) inert primes; the suborder has class number (l+1)*h, so
        # l+1 must divide n/2h:

        L1 = Set([lp1-1 for lp1 in divs
                  if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1)==-1])
        L += L1
        if verbose:
            print ("downward inert primes: %s" % L1)

        # Now find primes represented by each form of discriminant d.
        # In the rational CM case, we use all forms associated to
        # generators of the class group, otherwise only forms of order
        # 2:

        if E.has_rational_cm():
            from sage.quadratic_forms.binary_qf import BinaryQF
            Qs = [BinaryQF(list(q)) for q in data[2]]

            L1 = [Q.small_prime_value() for Q in Qs]
            if verbose:
                print("primes generating the class group: %s" % L1)
            L += Set(L1)

        # Return sorted list

        if verbose:
            print("Complete set of primes: %s" % L)

        return sorted(list(L))

    #  Non-CM case

    if verbose:
        print("Non-CM case, using Galois representation")

    return E.galois_representation().reducible_primes()
예제 #2
0
def is_a_splitting(S1, S2, n):
    """
    INPUT: S1, S2 are disjoint sublists partitioning [1, 2, ..., n-1]
    n1 is an integer
    
    OUTPUT: a, b where a is True or False, depending on whether S1, S2
    form a "splitting" of n (ie, if there is a b1 such that b\*S1=S2
    (point-wise multiplication mod n), and b is a splitting (if a =
    True) or 0 (if a = False)
    
    Splittings are useful for computing idempotents in the quotient
    ring `Q = GF(q)[x]/(x^n-1)`. For
    
    EXAMPLES::
    
        sage: from sage.coding.code_constructions import is_a_splitting
        sage: n = 11; q = 3
        sage: C = cyclotomic_cosets(q,n); C
        [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]]
        sage: S1 = C[1]
        sage: S2 = C[2]
        sage: is_a_splitting(S1,S2,11)
        (True, 2)
        sage: F = GF(q)
        sage: P.<x> = PolynomialRing(F,"x")
        sage: I = Ideal(P,[x^n-1])
        sage: Q.<x> = QuotientRing(P,I)
        sage: i1 = -sum([x^i for i in S1]); i1
        2*x^9 + 2*x^5 + 2*x^4 + 2*x^3 + 2*x
        sage: i2 = -sum([x^i for i in S2]); i2
        2*x^10 + 2*x^8 + 2*x^7 + 2*x^6 + 2*x^2
        sage: i1^2 == i1
        True
        sage: i2^2 == i2
        True
        sage: (1-i1)^2 == 1-i1
        True
        sage: (1-i2)^2 == 1-i2
        True
    
    We return to dealing with polynomials (rather than elements of
    quotient rings), so we can construct cyclic codes::
    
        sage: P.<x> = PolynomialRing(F,"x")
        sage: i1 = -sum([x^i for i in S1])
        sage: i2 = -sum([x^i for i in S2])
        sage: i1_sqrd = (i1^2).quo_rem(x^n-1)[1]
        sage: i1_sqrd  == i1
        True
        sage: i2_sqrd = (i2^2).quo_rem(x^n-1)[1]
        sage: i2_sqrd  == i2
        True
        sage: C1 = CyclicCodeFromGeneratingPolynomial(n,i1)
        sage: C2 = CyclicCodeFromGeneratingPolynomial(n,1-i2)
        sage: C1.dual_code() == C2
        True
    
    This is a special case of Theorem 6.4.3 in [HP]_.
    """
    if Set(S1).union(Set(S2)) <> Set(range(1, n)):
        raise TypeError, "Lists must partition [1,2,...,n-1]."
    if n < 3:
        raise TypeError, "Input %s must be > 2." % n
    for b in range(2, n):
        SS1 = Set([b * x % n for x in S1])
        SS2 = Set([b * x % n for x in S2])
        if SS1 == Set(S2) and SS2 == Set(S1):
            return True, b
    return False, 0
예제 #3
0
def local_good_density_congruence_even(self, m, Zvec, NZvec):
    """
    Finds the Good-type local density of Q representing `m` at `p=2`.
    (Assuming Q is given in local diagonal form.)


    The additional congruence condition arguments Zvec and NZvec can
    be either a list of indices or None.  Zvec = [] is equivalent to
    Zvec = None which both impose no additional conditions, but NZvec
    = [] returns no solutions always while NZvec = None imposes no
    additional condition.

    WARNING: Here the indices passed in Zvec and NZvec represent
    indices of the solution vector `x` of Q(`x`) = `m (mod p^k)`, and *not*
    the Jordan components of Q.  They therefore are required (and
    assumed) to include either all or none of the indices of a given
    Jordan component of Q.  This is only important when `p=2` since
    otherwise all Jordan blocks are 1x1, and so there the indices and
    Jordan blocks coincide.

    TO DO: Add type checking for Zvec, NZvec, and that Q is in local
    normal form.


    INPUT:

        Q -- quadratic form assumed to be block diagonal and 2-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:

        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_good_density_congruence_even(1, None, None)
        1

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_good_density_congruence_even(1, None, None)
        1
        sage: Q.local_good_density_congruence_even(2, None, None)
        3/2
        sage: Q.local_good_density_congruence_even(3, None, None)
        1
        sage: Q.local_good_density_congruence_even(4, None, None)
        1/2

    ::

        sage: Q = QuadraticForm(ZZ, 4, range(10))
        sage: Q[0,0] = 5
        sage: Q[1,1] = 10
        sage: Q[2,2] = 15
        sage: Q[3,3] = 20
        sage: Q
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 5 1 2 3 ]
        [ * 10 5 6 ]
        [ * * 15 8 ]
        [ * * * 20 ]
        sage: Q.theta_series(20)
        1 + 2*q^5 + 2*q^10 + 2*q^14 + 2*q^15 + 2*q^16 + 2*q^18 + O(q^20)
        sage: Q.local_normal_form(2)
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 0 1 0 0 ]
        [ * 0 0 0 ]
        [ * * 0 1 ]
        [ * * * 0 ]
        sage: Q.local_good_density_congruence_even(1, None, None)
        3/4
        sage: Q.local_good_density_congruence_even(2, None, None)
        1
        sage: Q.local_good_density_congruence_even(5, None, None)
        3/4

    """
    n = self.dim()

    ## Put the Zvec congruence condition in a standard form
    if Zvec is None:
        Zvec = []


    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec is not None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec is not None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")



    ## Find the indices of x for which the associated Jordan blocks are non-zero mod 8    TO DO: Move this to special Jordan block code separately!
    ## -------------------------------------------------------------------------------
    Not8vec = []
    for i in range(n):

        ## DIAGNOSTIC
        verbose(" i = " + str(i))
        verbose(" n = " + str(n))
        verbose(" Not8vec = " + str(Not8vec))

        nz_flag = False

        ## Check if the diagonal entry isn't divisible 8
        if  ((self[i,i] % 8) != 0):
            nz_flag = True

        ## Check appropriate off-diagonal entries aren't divisible by 8
        else:

            ## Special check for first off-diagonal entry
            if ((i == 0) and ((self[i,i+1] % 8) != 0)):
                nz_flag = True

            ## Special check for last off-diagonal entry
            elif ((i == n-1) and ((self[i-1,i] % 8) != 0)):
                nz_flag = True

            ## Check for the middle off-diagonal entries
            else:
                if ( (i > 0)  and  (i < n-1)  and  (((self[i,i+1] % 8) != 0) or ((self[i-1,i] % 8) != 0)) ):
                    nz_flag = True

        ## Remember the (vector) index if it's not part of a Jordan block of norm divisible by 8
        if nz_flag:
            Not8vec += [i]



    ## Compute the number of Good-type solutions mod 8:
    ## ------------------------------------------------

    ## Setup the indexing sets for additional zero congruence solutions
    Q_Not8 = self.extract_variables(Not8vec)
    Not8 = Set(Not8vec)
    Is8 = Set(range(n)) - Not8

    Z = Set(Zvec)
    Z_Not8 = Not8.intersection(Z)
    Z_Is8 = Is8.intersection(Z)
    Is8_minus_Z = Is8 - Z_Is8


    ## DIAGNOSTIC
    verbose("Z = " + str(Z))
    verbose("Z_Not8 = " + str(Z_Not8))
    verbose("Z_Is8 = " + str(Z_Is8))
    verbose("Is8_minus_Z = " + str(Is8_minus_Z))


    ## Take cases on the existence of additional non-zero congruence conditions (mod 2)
    if NZvec is None:
        total = (4 ** len(Z_Is8)) * (8 ** len(Is8_minus_Z)) \
            * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(Z_Not8), None)
    else:
        ZNZ = Z + Set(NZvec)
        ZNZ_Not8 = Not8.intersection(ZNZ)
        ZNZ_Is8 = Is8.intersection(ZNZ)
        Is8_minus_ZNZ = Is8 - ZNZ_Is8

        ## DIAGNOSTIC
        verbose("ZNZ = " + str(ZNZ))
        verbose("ZNZ_Not8 = " + str(ZNZ_Not8))
        verbose("ZNZ_Is8 = " + str(ZNZ_Is8))
        verbose("Is8_minus_ZNZ = " + str(Is8_minus_ZNZ))

        total = (4 ** len(Z_Is8)) * (8 ** len(Is8_minus_Z)) \
            * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(Z_Not8), None) \
            - (4 ** len(ZNZ_Is8)) * (8 ** len(Is8_minus_ZNZ)) \
            * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(ZNZ_Not8), None)


    ## DIAGNOSTIC
    verbose("total = " + str(total))


    ## Return the associated Good-type representation density
    good_density = QQ(total) / 8**(n-1)
    return good_density
예제 #4
0
 def set(self):
     r"""
     The set of values of the probability space taking possibly nonzero
     probability (a subset of the domain).
     """
     return Set(self.function().keys())
예제 #5
0
    def __classcall_private__(cls, p):
        r"""
        This function tries to recognize the input (it can be either a list or
        a tuple of pairs, or a fix-point free involution given as a list or as
        a permutation), constructs the parent (enumerated set of
        PerfectMatchings of the ground set) and calls the __init__ function to
        construct our object.

        EXAMPLES::

            sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m
            PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')]
            sage: isinstance(m,PerfectMatching)
            True
            sage: n=PerfectMatching([3, 8, 1, 7, 6, 5, 4, 2]);n
            PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
            sage: n.parent()
            Set of perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8}
            sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing()
            True

        The function checks that the given list or permutation is a valid perfect
        matching (i.e. a list of pairs with pairwise disjoint elements  or a
        fixpoint-free involution) and raises a ValueError otherwise:

            sage: PerfectMatching([(1, 2, 3), (4, 5)])
            Traceback (most recent call last):
            ...
            ValueError: [(1, 2, 3), (4, 5)] is not a valid perfect matching: all elements of the list must be pairs

        If you know your datas are in a good format, use directly
        ``PerfectMatchings(objects)(data)``.

        TESTS::

             sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')])
             sage: TestSuite(m).run()
             sage: m=PerfectMatching([])
             sage: TestSuite(m).run()
             sage: PerfectMatching(6)
             Traceback (most recent call last):
             ...
             ValueError: cannot convert p (= 6) to a PerfectMatching
             sage: PerfectMatching([(1,2,3)])
             Traceback (most recent call last):
             ...
             ValueError: [(1, 2, 3)] is not a valid perfect matching:
             all elements of the list must be pairs
             sage: PerfectMatching([(1,1)])
             Traceback (most recent call last):
             ...
             ValueError: [(1, 1)] is not a valid perfect matching:
             there are some repetitions
             sage: PerfectMatching(Permutation([4,2,1,3]))
             Traceback (most recent call last):
             ...
             ValueError: The permutation p (= [4, 2, 1, 3]) is not a fixed point free involution
        """
        # we have to extract from the argument p the set of objects of the
        # matching and the list of pairs.
        # First case: p is a list (resp tuple) of lists (resp tuple).
        if (isinstance(p, list) or isinstance(p, tuple)) and (all(
            [isinstance(x, list) or isinstance(x, tuple) for x in p])):
            objects = Set(flatten(p))
            data = (map(tuple, p))
            #check if the data are correct
            if not all([len(t) == 2 for t in data]):
                raise ValueError, ("%s is not a valid perfect matching:\n"
                                   "all elements of the list must be pairs" %
                                   p)
            if len(objects) < 2 * len(data):
                raise ValueError, ("%s is not a valid perfect matching:\n"
                                   "there are some repetitions" % p)
        # Second case: p is a permutation or a list of integers, we have to
        # check if it is a fix-point-free involution.
        elif ((isinstance(p, list) and all(
                map(lambda x:
                    (isinstance(x, Integer) or isinstance(x, int)), p)))
              or isinstance(p, Permutation_class)):
            p = Permutation(p)
            n = len(p)
            if not (p.cycle_type() == [2 for i in range(n // 2)]):
                raise ValueError, ("The permutation p (= %s) is not a "
                                   "fixed point free involution" % p)
            objects = Set(range(1, n + 1))
            data = p.to_cycles()
        # Third case: p is already a perfect matching, we return p directly
        elif isinstance(p, PerfectMatching):
            return p
        else:
            raise ValueError, "cannot convert p (= %s) to a PerfectMatching" % p
        # Finally, we create the parent and the element using the element
        # class of the parent. Note: as this function is private, when we
        # create an object via parent.element_class(...), __init__ is directly
        # executed and we do not have an infinite loop.
        return PerfectMatchings(objects)(data)
예제 #6
0
    def block_design_checker(self, t, v, k, lmbda, type=None):
        """
        This is *not* a wrapper for GAP Design's IsBlockDesign. The GAP
        Design function IsBlockDesign
        http://www.gap-system.org/Manuals/pkg/design/htm/CHAP004.htm
        apparently simply checks the record structure and no mathematical
        properties. Instead, the function below checks some necessary (but
        not sufficient) "easy" identities arising from the identity.

        INPUT:

        - ``t`` - the t as in "t-design"

        - ``v`` - the number of points

        - ``k`` - the number of blocks incident to a point

        - ``lmbda`` - each t-tuple of points should be incident with
          lmbda blocks

        - ``type`` - can be 'simple' or 'binary' or 'connected'
          Depending on the option, this wraps IsBinaryBlockDesign,
          IsSimpleBlockDesign, or IsConnectedBlockDesign.

          - Binary: no block has a repeated element.

          - Simple: no block is repeated.

          - Connected: its incidence graph is a connected graph.

        WARNING: This is very fast but can return false positives.

        EXAMPLES::

            sage: from sage.combinat.designs.block_design import BlockDesign
            sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]])
            sage: BD.parameters()
            (2, 7, 3, 1)
            sage: BD.block_design_checker(2, 7, 3, 1)
            True
            sage: BD.block_design_checker(2, 7, 3, 1,"binary")
            True
            sage: BD.block_design_checker(2, 7, 3, 1,"connected")
            True
            sage: BD.block_design_checker(2, 7, 3, 1,"simple")
            True
        """
        from sage.sets.set import Set
        if not(v == len(self.points())):
            return False
        b = lmbda*binomial(v,t)/binomial(k,t)
        r = int(b*k/v)
        if not(b == len(self.blocks())):
            return False
        if not(ZZ(v).divides(b*k)):
            return False
        A = self.incidence_matrix()
        #k = sum(A.columns()[0])
        #r = sum(A.rows()[0])
        for i in range(b):
            if not(sum(A.columns()[i]) == k):
                return False
        for i in range(v):
            if not(sum(A.rows()[i]) == r):
                return False
        gD = self._gap_()
        if type==None:
            return True
        if type=="binary":
            for b in self.blocks():
                if len(b)!=len(Set(b)):
                     return False
            return True
        if type=="simple":
            B = self.blocks()
            for b in B:
                 if B.count(b)>1:
                     return False
            return True
        if type=="connected":
            Gamma = self.incidence_graph()
            if Gamma.is_connected():
                return True
            else:
                return False
예제 #7
0
파일: braid.py 프로젝트: biasse/sage
    def _LKB_matrix_(self, braid, variab):
        """
        Compute the Lawrence-Krammer-Bigelow representation matrix.

        The variables of the matrix must be given. This actual
        computation is done in this helper method for caching
        purposes.

        INPUT:

        - ``braid`` -- tuple of integers. The Tietze list of the
          braid.

        - ``variab`` -- string. the names of the variables that will
          appear in the matrix. They must be given as a string,
          separated by a comma

        OUTPUT:

        The LKB matrix of the braid, with respect to the variables.

        TESTS::

            sage: B=BraidGroup(3)
            sage: B._LKB_matrix_((2, 1, 2), 'x, y')
            [             0 -x^4*y + x^3*y         -x^4*y]
            [             0         -x^3*y              0]
            [        -x^2*y  x^3*y - x^2*y              0]
            sage: B._LKB_matrix_((1, 2, 1), 'x, y')
            [             0 -x^4*y + x^3*y         -x^4*y]
            [             0         -x^3*y              0]
            [        -x^2*y  x^3*y - x^2*y              0]
            sage: B._LKB_matrix_((-1, -2, -1, 2, 1, 2), 'x, y')
            [1 0 0]
            [0 1 0]
            [0 0 1]
        """
        n = self.strands()
        if len(braid) > 1:
            A = self._LKB_matrix_(braid[:1], variab)
            for i in braid[1:]:
                A = A * self._LKB_matrix_((i, ), variab)
            return A
        l = list(Set(range(n)).subsets(2))
        R = LaurentPolynomialRing(IntegerRing(), variab)
        q = R.gens()[0]
        t = R.gens()[1]
        if len(braid) == 0:
            return identity_matrix(R, len(l), sparse=True)
        A = matrix(R, len(l), sparse=True)
        if braid[0] > 0:
            i = braid[0] - 1
            for m in range(len(l)):
                j = min(l[m])
                k = max(l[m])
                if i == j - 1:
                    A[l.index(Set([i, k])), m] = q
                    A[l.index(Set([i, j])), m] = q * q - q
                    A[l.index(Set([j, k])), m] = 1 - q
                elif i == j and not j == k - 1:
                    A[l.index(Set([j, k])), m] = 0
                    A[l.index(Set([j + 1, k])), m] = 1
                elif k - 1 == i and not k - 1 == j:
                    A[l.index(Set([j, i])), m] = q
                    A[l.index(Set([j, k])), m] = 1 - q
                    A[l.index(Set([i, k])), m] = (1 - q) * q * t
                elif i == k:
                    A[l.index(Set([j, k])), m] = 0
                    A[l.index(Set([j, k + 1])), m] = 1
                elif i == j and j == k - 1:
                    A[l.index(Set([j, k])), m] = -t * q * q
                else:
                    A[l.index(Set([j, k])), m] = 1
            return A
        else:
            i = -braid[0] - 1
            for m in range(len(l)):
                j = min(l[m])
                k = max(l[m])
                if i == j - 1:
                    A[l.index(Set([j - 1, k])), m] = 1
                elif i == j and not j == k - 1:
                    A[l.index(Set([j + 1, k])), m] = q**(-1)
                    A[l.index(Set([j, k])), m] = 1 - q**(-1)
                    A[l.index(Set([j, j + 1])),
                      m] = t**(-1) * q**(-1) - t**(-1) * q**(-2)
                elif k - 1 == i and not k - 1 == j:
                    A[l.index(Set([j, k - 1])), m] = 1
                elif i == k:
                    A[l.index(Set([j, k + 1])), m] = q**(-1)
                    A[l.index(Set([j, k])), m] = 1 - q**(-1)
                    A[l.index(Set([k, k + 1])), m] = -q**(-1) + q**(-2)
                elif i == j and j == k - 1:
                    A[l.index(Set([j, k])), m] = -t**(-1) * q**(-2)
                else:
                    A[l.index(Set([j, k])), m] = 1
            return A
예제 #8
0
    def line_covectors(self):
        r"""
        Return a dictionary with pairs of normal vectors as keys and a list
        containing the line covectors as value (up to multiplication by -1).

        The line covectors encode the sign pattern of all the vectors in
        ``self`` on the intersection of two hyperplanes for vector
        configurations in dimension 3.

        OUTPUT

        a dictionary.

        EXAMPLES::

            sage: from cn_hyperarr.vector_classes import *
            sage: vc = VectorConfiguration([[1,0,0],[0,1,0],[0,0,1],[1,1,0],[0,1,1],[1,1,1]])
            sage: vc.line_covectors()
            {{4, 5}: ((0, 1, -1, 1, 0, 0), (0, -1, 1, -1, 0, 0)),
             {3, 5}: ((1, -1, 0, 0, -1, 0), (-1, 1, 0, 0, 1, 0)),
             {3, 4}: ((1, -1, 1, 0, 0, 1), (-1, 1, -1, 0, 0, -1)),
             {2, 5}: ((-1, 1, 0, 0, 1, 0), (1, -1, 0, 0, -1, 0)),
             {2, 4}: ((-1, 0, 0, -1, 0, -1), (1, 0, 0, 1, 0, 1)),
             {2, 3}: ((-1, 1, 0, 0, 1, 0), (1, -1, 0, 0, -1, 0)),
             {1, 5}: ((1, 0, -1, 1, -1, 0), (-1, 0, 1, -1, 1, 0)),
             {1, 4}: ((1, 0, 0, 1, 0, 1), (-1, 0, 0, -1, 0, -1)),
             {1, 3}: ((0, 0, -1, 0, -1, -1), (0, 0, 1, 0, 1, 1)),
             {1, 2}: ((1, 0, 0, 1, 0, 1), (-1, 0, 0, -1, 0, -1)),
             {0, 5}: ((0, -1, 1, -1, 0, 0), (0, 1, -1, 1, 0, 0)),
             {0, 4}: ((0, -1, 1, -1, 0, 0), (0, 1, -1, 1, 0, 0)),
             {0, 3}: ((0, 0, 1, 0, 1, 1), (0, 0, -1, 0, -1, -1)),
             {0, 2}: ((0, -1, 0, -1, -1, -1), (0, 1, 0, 1, 1, 1)),
             {0, 1}: ((0, 0, 1, 0, 1, 1), (0, 0, -1, 0, -1, -1))}

        TESTS:

        The ambient dimension should be 3::

            sage: vc = VectorConfiguration([[1], [2], [3]])
            sage: vc.line_covectors()
            Traceback (most recent call last):
            ...
            AssertionError: The ambient dimension should be 3

        TODO:

            Make it work in higher dimension.
        """
        assert self.ambient_dimension(
        ) == 3, "The ambient dimension should be 3"
        nb_hyperpl = self.n_vectors()
        hic_dict = {}
        for index_1, index_2 in combinations(range(nb_hyperpl), 2):
            new_key = Set([index_1, index_2])
            vector_1 = self[index_1]
            vector_2 = self[index_2]
            intersection = vector_1.cross_product(vector_2)
            new_value = list()
            for index in range(nb_hyperpl):
                dot_prod = intersection.dot_product(self[index])
                if dot_prod == 0:
                    new_value.append(0)
                elif dot_prod < 0:
                    new_value.append(-1)
                else:
                    new_value.append(1)
            hic1 = Covector(new_value)
            hic2 = Covector([-v for v in new_value])
            hic_dict[new_key] = tuple([hic1, hic2])
        return hic_dict
예제 #9
0
    def forcing_oriented_graph(self):
        r"""
        Return the forcing oriented graph for a 3-dimensional vector
        configuration.

        This is the directed graph on shards. The hyperplane arrangement with
        base region corresponding to the acyclic vector configuration is
        congruence normal if and only if the directed graph on shards
        is acyclic.

        OUTPUT:

        An oriented graph

        EXAMPLES::

            sage: from cn_hyperarr.vector_classes import *
            sage: vc = VectorConfiguration([[1,0,0],[0,1,0],[0,0,1],[1,1,0],[0,1,1],[1,1,1]])
            sage: fog = vc.forcing_oriented_graph(); fog
            Digraph on 11 vertices
            sage: fog.num_edges()
            24
            sage: tau = AA((1+sqrt(5))/2)
            sage: ncn = [[2*tau+1,2*tau,tau],[2*tau+2,2*tau+1,tau+1],[1,1,1],[tau+1,tau+1,tau],[2*tau,2*tau,tau],[tau+1,tau+1,1],[1,1,0],[0,1,0],[1,0,0],[tau+1,tau,tau]]
            sage: ncn_conf = VectorConfiguration(ncn);
            sage: ncn_fog = ncn_conf.forcing_oriented_graph(); ncn_fog
            Digraph on 29 vertices
            sage: ncn_fog.num_edges()
            96
            sage: ncn_fog.is_directed_acyclic()
            False

        TESTS:

        The ambient dimension should be 3::

            sage: vc = VectorConfiguration([[1],[2],[3]])
            sage: vc.forcing_oriented_graph()
            Traceback (most recent call last):
            ...
            AssertionError: The ambient dimension is not 3
        """
        nb_pts = self.n_vectors()
        shard_covectors = self.shard_covectors()
        hic = self.line_covectors()

        G = DiGraph()
        for shard_cov in shard_covectors:
            G.add_vertex(shard_cov)
            shard_index = shard_cov.index(0)
            potential_shards = (v for v in G.vertices()
                                if v.index(0) != shard_index)
            for other_shard_cov in potential_shards:
                other_shard_index = other_shard_cov.index(0)
                # Get the potential ordering of forcing:
                forcing = True
                if other_shard_cov[shard_index] in [1, -1]:
                    # shard_cov -> other_shard_cov
                    first_cov, second_cov = shard_cov, other_shard_cov
                elif shard_cov[other_shard_index] in [1, -1]:
                    # other_shard_cov -> shard_cov
                    first_cov, second_cov = other_shard_cov, shard_cov
                else:
                    forcing = False
                if forcing:
                    # A forcing takes place
                    hic1, hic2 = hic[Set([shard_index, other_shard_index])]
                    sic1 = hic1 & shard_cov & other_shard_cov
                    sic2 = hic2 & shard_cov & other_shard_cov
                    if sic1 == hic1 or sic2 == hic2:
                        G.add_edge(first_cov, second_cov)
        return G.copy(immutable=True)
예제 #10
0
def _non_surjective(E, patience=100):
    r"""
    Returns a list of primes `p` including all primes for which the mod-`p`
    representation might not be surjective.

    INPUT:

    - ``E`` - EllipticCurve (over a number field).

    - ``A`` - int (a bound on the number of traces of Frobenius to use
                 while trying to prove surjectivity).

    OUTPUT:

    - ``list`` - A list of primes where mod-`p` representation is very likely
      not surjective. At any prime not in this list, the representation is
      definitely surjective. If E has CM, a ValueError is raised.

    EXAMPLES::

        sage: K = NumberField(x**2 - 29, 'a'); a = K.gen()
        sage: E = EllipticCurve([1, 0, ((5 + a)/2)**2, 0, 0])
        sage: sage.schemes.elliptic_curves.gal_reps_number_field._non_surjective(E) # See Section 5.10 of [Serre72].
        [3, 5, 29]
        sage: E = EllipticCurve_from_j(1728).change_ring(K) # CM
        sage: sage.schemes.elliptic_curves.gal_reps_number_field._non_surjective(E)
        Traceback (most recent call last):
        ...
        ValueError: The curve E should not have CM.
        """

    E = _over_numberfield(E)
    K = E.base_field()

    exceptional_primes = [2, 3, 5, 7, 11, 13, 17, 19]
    # The possible primes l unramified in K/QQ for which the image of the mod l
    # Galois representation could be contained in an exceptional subgroup.

    # Find the places of additive reduction.
    SA = []
    for P, n in E.conductor().factor():
        if n > 1:
            SA.append(P)
    # TODO: All we really require is a list of primes that include all
    # primes at which E has additive reduction. Perhaps we can speed
    # up things by doing something less time-consuming here that produces
    # a list with some extra terms? (Of course, the longer this list is,
    # the slower the rest of the computation is, so it is not clear that
    # this would help...)

    char = lambda P: P.smallest_integer(
    )  # cheaper than constructing the residue field

    bad_primes = exceptional_primes
    bad_primes += [char(P) for P in SA]
    bad_primes += K.discriminant().prime_factors()
    bad_primes += _semistable_reducible_primes(E)
    bad_primes += _possible_normalizers(E, SA)

    bad_primes = list(Set(bad_primes))

    return _exceptionals(E, bad_primes, patience)
예제 #11
0
def coefficients_from_j(j, minimal_twist=True):
    """
    Return Weierstrass coefficients `(a_1, a_2, a_3, a_4, a_6)` for an
    elliptic curve with given `j`-invariant.

    INPUT:

    See :func:`EllipticCurve_from_j`.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.constructor import coefficients_from_j
        sage: coefficients_from_j(0)
        [0, 0, 1, 0, 0]
        sage: coefficients_from_j(1728)
        [0, 0, 0, -1, 0]
        sage: coefficients_from_j(1)
        [1, 0, 0, 36, 3455]

    The ``minimal_twist`` parameter (ignored except over `\\QQ` and
    True by default) controls whether or not a minimal twist is
    computed::

        sage: coefficients_from_j(100)
        [0, 1, 0, 3392, 307888]
        sage: coefficients_from_j(100, minimal_twist=False)
        [0, 0, 0, 488400, -530076800]
    """
    try:
        K = j.parent()
    except AttributeError:
        K = rings.RationalField()
    if K not in _Fields:
        K = K.fraction_field()

    char = K.characteristic()
    if char == 2:
        if j == 0:
            return Sequence([0, 0, 1, 0, 0], universe=K)
        else:
            return Sequence([1, 0, 0, 0, 1 / j], universe=K)
    if char == 3:
        if j == 0:
            return Sequence([0, 0, 0, 1, 0], universe=K)
        else:
            return Sequence([0, j, 0, 0, -j**2], universe=K)

    if K is rings.RationalField():
        # we construct the minimal twist, i.e. the curve with minimal
        # conductor with this j_invariant:
        if j == 0:
            return Sequence([0, 0, 1, 0, 0], universe=K)  # 27a3
        if j == 1728:
            return Sequence([0, 0, 0, -1, 0], universe=K)  # 32a2

        if not minimal_twist:
            k = j - 1728
            return Sequence([0, 0, 0, -3 * j * k, -2 * j * k**2], universe=K)

        n = j.numerator()
        m = n - 1728 * j.denominator()
        a4 = -3 * n * m
        a6 = -2 * n * m**2

        # Now E=[0,0,0,a4,a6] has j-invariant j=n/d
        from sage.sets.set import Set
        for p in Set(n.prime_divisors() + m.prime_divisors()):
            e = min(a4.valuation(p) // 2, a6.valuation(p) // 3)
            if e > 0:
                p = p**e
                a4 /= p**2
                a6 /= p**3

        # Now E=[0,0,0,a4,a6] is minimal at all p != 2,3
        tw = [-1, 2, -2, 3, -3, 6, -6]
        E1 = EllipticCurve([0, 0, 0, a4, a6])
        Elist = [E1] + [E1.quadratic_twist(t) for t in tw]
        Elist.sort(key=lambda E: E.conductor())
        return Sequence(Elist[0].ainvs())

    # defaults for all other fields:
    if j == 0:
        return Sequence([0, 0, 0, 0, 1], universe=K)
    if j == 1728:
        return Sequence([0, 0, 0, 1, 0], universe=K)
    k = j - 1728
    return Sequence([0, 0, 0, -3 * j * k, -2 * j * k**2], universe=K)
예제 #12
0
    def isogeny_bound(self, A=100):
        r"""
        Returns a list of primes `p` including all primes for which
        the image of the mod-`p` representation is contained in a
        Borel.

        .. NOTE::

           For the actual list of primes `p` at which the
           representation is reducible see :meth:`reducible_primes()`.

        INPUT:

        - ``A`` - int (a bound on the number of traces of Frobenius to
                     use while trying to prove the mod-`p`
                     representation is not contained in a Borel).

        OUTPUT:

        - ``list`` - A list of primes which contains (but may not be
          equal to) all `p` for which the image of the mod-`p`
          representation is contained in a Borel subgroup.  At any
          prime not in this list, the image is definitely not
          contained in a Borel. If E has `CM` defined over `K`, the list
          [0] is returned.

        EXAMPLES::

            sage: K = NumberField(x**2 - 29, 'a'); a = K.gen()
            sage: E = EllipticCurve([1, 0, ((5 + a)/2)**2, 0, 0])
            sage: rho = E.galois_representation()
            sage: rho.isogeny_bound() # See Section 5.10 of [Serre72].
            [3, 5]
            sage: K = NumberField(x**2 + 1, 'a')
            sage: EllipticCurve_from_j(K(1728)).galois_representation().isogeny_bound() # CM over K
            [0]
            sage: EllipticCurve_from_j(K(0)).galois_representation().isogeny_bound() # CM NOT over K
            [2, 3]
            sage: E = EllipticCurve_from_j(K(2268945/128)) # c.f. [Sutherland12]
            sage: E.galois_representation().isogeny_bound() # No 7-isogeny, but...
            [7]
        """
        E = _over_numberfield(self.E)
        K = E.base_field()

        char = lambda P: P.smallest_integer(
        )  # cheaper than constructing the residue field

        # semistable reducible primes (function raises an error for CM curves)
        try:
            bad_primes = _semistable_reducible_primes(E)
        except ValueError:
            return [0]
        # primes of additive reduction
        bad_primesK = (K.ideal(E.c4()) +
                       K.ideal(E.discriminant())).prime_factors()
        bad_primes += [char(P) for P in bad_primesK]
        # ramified primes
        bad_primes += K.absolute_discriminant().prime_factors()

        # remove repeats:
        bad_primes = list(Set(bad_primes))

        return _maybe_borels(E, bad_primes, A)
예제 #13
0
    def isogeny_bound(self, A=100):
        r"""
        Returns a list of primes `p` including all primes for which
        the image of the mod-`p` representation is contained in a
        Borel.

        .. NOTE::

           For the actual list of primes `p` at which the
           representation is reducible see :meth:`reducible_primes()`.

        INPUT:

        - ``A`` - int (a bound on the number of traces of Frobenius to
                     use while trying to prove the mod-`p`
                     representation is not contained in a Borel).

        OUTPUT:

        - ``list`` - A list of primes which contains (but may not be
          equal to) all `p` for which the image of the mod-`p`
          representation is contained in a Borel subgroup.  At any
          prime not in this list, the image is definitely not
          contained in a Borel. If E has `CM` defined over `K`, the list
          [0] is returned.

        EXAMPLES::

            sage: K = NumberField(x**2 - 29, 'a'); a = K.gen()
            sage: E = EllipticCurve([1, 0, ((5 + a)/2)**2, 0, 0])
            sage: rho = E.galois_representation()
            sage: rho.isogeny_bound() # See Section 5.10 of [Serre72].
            [3, 5]
            sage: K = NumberField(x**2 + 1, 'a')
            sage: EllipticCurve_from_j(K(1728)).galois_representation().isogeny_bound() # CM over K
            [0]
            sage: EllipticCurve_from_j(K(0)).galois_representation().isogeny_bound() # CM NOT over K
            [2, 3]
            sage: E = EllipticCurve_from_j(K(2268945/128)) # c.f. [Sutherland12]
            sage: E.galois_representation().isogeny_bound() # No 7-isogeny, but...
            [7]

        For curves with rational CM, there are infinitely many primes
        `p` for which the mod-`p` representation is reducible, and [0]
        is returned::

            sage: K.<a> = NumberField(x^2-x+1)
            sage: E = EllipticCurve([0,0,0,0,a])
            sage: E.has_rational_cm()
            True
            sage: rho = E.galois_representation()
            sage: rho.isogeny_bound()
            [0]

        An example (an elliptic curve with everywhere good reduction
        over an imaginary quadratic field with quite large
        discriminant), which failed until fixed at :trac:`21776`::

            sage: K.<a> = NumberField(x^2 - x + 112941801)
            sage: E = EllipticCurve([a+1,a-1,a,-23163076*a + 266044005933275,57560769602038*a - 836483958630700313803])
            sage: E.conductor().norm()
            1
            sage: GR = E.galois_representation()
            sage: GR.isogeny_bound()
            []

        """
        if self.E.has_rational_cm():
            return [0]

        E = _over_numberfield(self.E)
        K = E.base_field()

        char = lambda P: P.smallest_integer(
        )  # cheaper than constructing the residue field

        # semistable reducible primes (we are now not in the CM case)
        bad_primes = _semistable_reducible_primes(E)

        # primes of additive reduction
        bad_primesK = (K.ideal(E.c4()) +
                       K.ideal(E.discriminant())).prime_factors()
        bad_primes += [char(P) for P in bad_primesK]

        # ramified primes
        bad_primes += K.absolute_discriminant().prime_factors()

        # remove repeats:
        bad_primes = list(Set(bad_primes))

        return _maybe_borels(E, bad_primes, A)
예제 #14
0
    def _compute(self, verbose=False):
        """
        Computes the list of curves, the matrix and prime-degree
        isogenies.

        EXAMPLES::

            sage: K.<i> = QuadraticField(-1)
            sage: E = EllipticCurve(K, [0,0,0,0,1])
            sage: C = E.isogeny_class()
            sage: C2 = C.copy()
            sage: C2._mat
            sage: C2._compute()
            sage: C2._mat
            [1 3 6 2]
            [3 1 2 6]
            [6 2 1 3]
            [2 6 3 1]

            sage: C2._compute(verbose=True)
            possible isogeny degrees: [2, 3] -actual isogeny degrees: {2, 3} -added curve #1 (degree 2)... -added tuple [0, 1, 2]... -added tuple [1, 0, 2]... -added curve #2 (degree 3)... -added tuple [0, 2, 3]... -added tuple [2, 0, 3]...... relevant degrees: [2, 3]... -now completing the isogeny class... -processing curve #1... -added tuple [1, 0, 2]... -added tuple [0, 1, 2]... -added curve #3... -added tuple [1, 3, 3]... -added tuple [3, 1, 3]... -processing curve #2... -added tuple [2, 3, 2]... -added tuple [3, 2, 2]... -added tuple [2, 0, 3]... -added tuple [0, 2, 3]... -processing curve #3... -added tuple [3, 2, 2]... -added tuple [2, 3, 2]... -added tuple [3, 1, 3]... -added tuple [1, 3, 3]...... isogeny class has size 4
            Sorting permutation = {0: 1, 1: 2, 2: 0, 3: 3}
            Matrix = [1 3 6 2]
            [3 1 2 6]
            [6 2 1 3]
            [2 6 3 1]

        TESTS:

        Check that :trac:`19030` is fixed (codomains of reverse isogenies were wrong)::

            sage: K.<i> = NumberField(x^2+1)
            sage: E = EllipticCurve([1, i + 1, 1, -72*i + 8, 95*i + 146])
            sage: C = E.isogeny_class()
            sage: curves = C.curves
            sage: isos = C.isogenies()
            sage: isos[0][3].codomain() == curves[3]
            True

        """
        from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
        from sage.matrix.all import MatrixSpace
        from sage.sets.set import Set
        self._maps = None

        E = self.E.global_minimal_model(semi_global=True)

        degs = possible_isogeny_degrees(E)
        if verbose:
            import sys
            sys.stdout.write(" possible isogeny degrees: %s" % degs)
            sys.stdout.flush()
        isogenies = E.isogenies_prime_degree(degs)
        if verbose:
            sys.stdout.write(" -actual isogeny degrees: %s" % Set([phi.degree() for phi in isogenies]))
            sys.stdout.flush()
        # Add all new codomains to the list and collect degrees:
        curves = [E]
        ncurves = 1
        degs = []
        # tuples (i,j,l,phi) where curve i is l-isogenous to curve j via phi
        tuples = []

        def add_tup(t):
            for T in [t, [t[1],t[0],t[2],0]]:
                if not T in tuples:
                    tuples.append(T)
                    if verbose:
                        sys.stdout.write(" -added tuple %s..." % T[:3])
                        sys.stdout.flush()

        for phi in isogenies:
            E2 = phi.codomain()
            d = ZZ(phi.degree())
            if not any([E2.is_isomorphic(E3) for E3 in curves]):
                curves.append(E2)
                if verbose:
                    sys.stdout.write(" -added curve #%s (degree %s)..." % (ncurves,d))
                    sys.stdout.flush()
                add_tup([0,ncurves,d,phi])
                ncurves += 1
                if not d in degs:
                    degs.append(d)
        if verbose:
            sys.stdout.write("... relevant degrees: %s..." % degs)
            sys.stdout.write(" -now completing the isogeny class...")
            sys.stdout.flush()

        i = 1
        while i < ncurves:
            E1 = curves[i]
            if verbose:
                sys.stdout.write(" -processing curve #%s..." % i)
                sys.stdout.flush()

            isogenies = E1.isogenies_prime_degree(degs)

            for phi in isogenies:
                E2 = phi.codomain()
                d = phi.degree()
                js = [j for j,E3 in enumerate(curves) if E2.is_isomorphic(E3)]
                if js: # seen codomain already -- up to isomorphism
                    j = js[0]
                    if phi.codomain()!=curves[j]:
                        iso = E2.isomorphism_to(curves[j])
                        phi.set_post_isomorphism(iso)
                    assert phi.domain()==curves[i] and phi.codomain()==curves[j]
                    add_tup([i,j,d,phi])
                else:
                    curves.append(E2)
                    if verbose:
                        sys.stdout.write(" -added curve #%s..." % ncurves)
                        sys.stdout.flush()
                    add_tup([i,ncurves,d,phi])
                    ncurves += 1
            i += 1

        if verbose:
            print("... isogeny class has size %s" % ncurves)

        # key function for sorting
        if E.has_rational_cm():
            key_function = lambda E: (-E.cm_discriminant(),flatten([list(ai) for ai in E.ainvs()]))
        else:
            key_function = lambda E: flatten([list(ai) for ai in E.ainvs()])

        self.curves = sorted(curves,key=key_function)
        perm = dict([(i,self.curves.index(E)) for i,E in enumerate(curves)])
        if verbose:
            print "Sorting permutation = %s" % perm

        mat = MatrixSpace(ZZ,ncurves)(0)
        self._maps = [[0]*ncurves for i in range(ncurves)]
        for i,j,l,phi in tuples:
            if phi!=0:
                mat[perm[i],perm[j]] = l
                self._maps[perm[i]][perm[j]] = phi
        self._mat = fill_isogeny_matrix(mat)
        if verbose:
            print "Matrix = %s" % self._mat

        if not E.has_rational_cm():
            self._qfmat = None
            return

        # In the CM case, we will have found some "horizontal"
        # isogenies of composite degree and would like to replace them
        # by isogenies of prime degree, mainly to make the isogeny
        # graph look better.  We also construct a matrix whose entries
        # are not degrees of cyclic isogenies, but rather quadratic
        # forms (in 1 or 2 variables) representing the isogeny
        # degrees.  For this we take a short cut: properly speaking,
        # when `\text{End}(E_1)=\text{End}(E_2)=O`, the set
        # `\text{Hom}(E_1,E_2)` is a rank `1` projective `O`-module,
        # hence has a well-defined ideal class associated to it, and
        # hence (using an identification between the ideal class group
        # and the group of classes of primitive quadratic forms of the
        # same discriminant) an equivalence class of quadratic forms.
        # But we currently only care about the numbers represented by
        # the form, i.e. which genus it is in rather than the exact
        # class.  So it suffices to find one form of the correct
        # discriminant which represents one isogeny degree from `E_1`
        # to `E_2` in order to obtain a form which represents all such
        # degrees.

        if verbose:
            print("Creating degree matrix (CM case)")

        allQs = {} # keys: discriminants d
                   # values: lists of equivalence classes of
                   # primitive forms of discriminant d
        def find_quadratic_form(d,n):
            if not d in allQs:
                from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives

                allQs[d] = BinaryQF_reduced_representatives(d, primitive_only=True)
            # now test which of the Qs represents n
            for Q in allQs[d]:
                if Q.solve_integer(n):
                    return Q
            raise ValueError("No form of discriminant %d represents %s" %(d,n))

        mat = self._mat
        qfmat = [[0 for i in range(ncurves)] for j in range(ncurves)]
        for i, E1 in enumerate(self.curves):
            for j, E2 in enumerate(self.curves):
                if j<i:
                    qfmat[i][j] = qfmat[j][i]
                    mat[i,j] = mat[j,i]
                elif i==j:
                    qfmat[i][j] = [1]
                    # mat[i,j] already 1
                else:
                    d = E1.cm_discriminant()
                    if d != E2.cm_discriminant():
                        qfmat[i][j] = [mat[i,j]]
                        # mat[i,j] already unique
                    else: # horizontal isogeny
                        q = find_quadratic_form(d,mat[i,j])
                        qfmat[i][j] = list(q)
                        mat[i,j] = q.small_prime_value()

        self._mat = mat
        self._qfmat = qfmat
        if verbose:
            print("new matrix = %s" % mat)
            print("matrix of forms = %s" % qfmat)
예제 #15
0
def test_finite_lattice(L):
    """
    Test several functions on a given finite lattice.

    The function contains tests of different kinds:

    - Implications of Boolean properties. Examples: a distributive lattice is modular,
      a dismantlable and distributive lattice is planar, a simple lattice can not be
      constructible by Day's doublings.
    - Dual and self-dual properties. Examples: Dual of a modular lattice is modular,
      dual of an atomic lattice is co-atomic.
    - Certificate tests. Example: certificate for a non-complemented lattice must be
      an element without a complement.
    - Verification of some property by known property or by a random test.
      Examples: A lattice is distributive iff join-primes are exactly
      join-irreducibles and an interval of a relatively complemented
      lattice is complemented.
    - Set inclusions. Example: Every co-atom must be meet-irreducible.
    - And several other tests. Example: The skeleton of a pseudocomplemented
      lattice must be Boolean.

    EXAMPLES::

        sage: from sage.tests.finite_poset import test_finite_lattice
        sage: L = posets.RandomLattice(10, 0.98)
        sage: test_finite_lattice(L) is None  # Long time
        True
    """
    from sage.combinat.posets.lattices import LatticePoset

    from sage.sets.set import Set
    from sage.combinat.subset import Subsets

    from sage.misc.prandom import randint
    from sage.misc.flatten import flatten
    from sage.misc.misc import attrcall

    if L.cardinality() < 4:
        # Special cases should be tested in specific TESTS-sections.
        return None

    all_props = set(list(implications) + flatten(implications.values()))
    P = {x: test_attrcall('is_' + x, L) for x in all_props}

    ### Relations between boolean-valued properties ###

    # Direct one-property implications
    for prop1 in implications:
        if P[prop1]:
            for prop2 in implications[prop1]:
                if not P[prop2]:
                    raise ValueError("error: %s should implicate %s" %
                                     (prop1, prop2))

    # Impossible combinations
    for p1, p2 in mutually_exclusive:
        if P[p1] and P[p2]:
            raise ValueError(
                "error: %s and %s should be impossible combination" % (p1, p2))

    # Two-property implications
    for p1, p2, p3 in two_to_one:
        if P[p1] and P[p2] and not P[p3]:
            raise ValueError("error: %s and %s, so should be %s" %
                             (p1, p2, p3))

    Ldual = L.dual()
    # Selfdual properties
    for p in selfdual_properties:
        if P[p] != test_attrcall('is_' + p, Ldual):
            raise ValueError("selfdual property %s error" % p)
    # Dual properties and elements
    for p1, p2 in dual_properties:
        if P[p1] != test_attrcall('is_' + p2, Ldual):
            raise ValueError("dual properties error %s" % p1)
    for e1, e2 in dual_elements:
        if set(attrcall(e1)(L)) != set(attrcall(e2)(Ldual)):
            raise ValueError("dual elements error %s" % e1)

    ### Certificates ###

    # Test for "yes"-certificates
    if P['supersolvable']:
        a = L.is_supersolvable(certificate=True)[1]
        S = Subsets(L).random_element()
        if L.is_chain_of_poset(S):
            if not L.sublattice(a + list(S)).is_distributive():
                raise ValueError("certificate error in is_supersolvable")
    if P['dismantlable']:
        elms = L.is_dismantlable(certificate=True)[1]
        if len(elms) != L.cardinality():
            raise ValueError("certificate error 1 in is_dismantlable")
        elms = elms[:randint(0, len(elms) - 1)]
        L_ = L.sublattice([x for x in L if x not in elms])
        if L_.cardinality() != L.cardinality() - len(elms):
            raise ValueError("certificate error 2 in is_dismantlable")
    if P['vertically_decomposable']:
        c = L.is_vertically_decomposable(certificate=True)[1]
        if c == L.bottom() or c == L.top():
            raise ValueError(
                "certificate error 1 in is_vertically_decomposable")
        e = L.random_element()
        if L.compare_elements(c, e) is None:
            raise ValueError(
                "certificate error 2 in is_vertically_decomposable")

    # Test for "no"-certificates
    if not P['atomic']:
        a = L.is_atomic(certificate=True)[1]
        if a in L.atoms() or a not in L.join_irreducibles():
            raise ValueError("certificate error in is_atomic")
    if not P['coatomic']:
        a = L.is_coatomic(certificate=True)[1]
        if a in L.coatoms() or a not in L.meet_irreducibles():
            raise ValueError("certificate error in is_coatomic")

    if not P['complemented']:
        a = L.is_complemented(certificate=True)[1]
        if L.complements(a) != []:
            raise ValueError("compl. error 1")
    if not P['sectionally_complemented']:
        a, b = L.is_sectionally_complemented(certificate=True)[1]
        L_ = L.sublattice(L.interval(L.bottom(), a))
        if L_.is_complemented():
            raise ValueError("sec. compl. error 1")
        if len(L_.complements(b)) > 0:
            raise ValueError("sec. compl. error 2")
    if not P['cosectionally_complemented']:
        a, b = L.is_cosectionally_complemented(certificate=True)[1]
        L_ = L.sublattice(L.interval(a, L.top()))
        if L_.is_complemented():
            raise ValueError("cosec. compl. error 1")
        if len(L_.complements(b)) > 0:
            raise ValueError("cosec. compl. error 2")
    if not P['relatively_complemented']:
        a, b, c = L.is_relatively_complemented(certificate=True)[1]
        I = L.interval(a, c)
        if len(I) != 3 or b not in I:
            raise ValueError("rel. compl. error 1")

    if not P['upper_semimodular']:
        a, b = L.is_upper_semimodular(certificate=True)[1]
        if not set(L.lower_covers(a)).intersection(set(
                L.lower_covers(b))) or set(L.upper_covers(a)).intersection(
                    set(L.upper_covers(b))):
            raise ValueError("certificate error in is_upper_semimodular")
    if not P['lower_semimodular']:
        a, b = L.is_lower_semimodular(certificate=True)[1]
        if set(L.lower_covers(a)).intersection(set(
                L.lower_covers(b))) or not set(L.upper_covers(a)).intersection(
                    set(L.upper_covers(b))):
            raise ValueError("certificate error in is_lower_semimodular")

    if not P['distributive']:
        x, y, z = L.is_distributive(certificate=True)[1]
        if L.meet(x, L.join(y, z)) == L.join(L.meet(x, y), L.meet(x, z)):
            raise ValueError("certificate error in is_distributive")
    if not P['modular']:
        x, a, b = L.is_modular(certificate=True)[1]
        if not L.is_less_than(x, b) or L.join(x, L.meet(a, b)) == L.meet(
                L.join(x, a), b):
            raise ValueError("certificate error in is_modular")

    if not P['pseudocomplemented']:
        a = L.is_pseudocomplemented(certificate=True)[1]
        L_ = L.subposet([e for e in L if L.meet(e, a) == L.bottom()])
        if L_.has_top():
            raise ValueError("certificate error in is_pseudocomplemented")
    if not P['join_pseudocomplemented']:
        a = L.is_join_pseudocomplemented(certificate=True)[1]
        L_ = L.subposet([e for e in L if L.join(e, a) == L.top()])
        if L_.has_bottom():
            raise ValueError("certificate error in is_join_pseudocomplemented")

    if not P['join_semidistributive']:
        e, x, y = L.is_join_semidistributive(certificate=True)[1]
        if L.join(e, x) != L.join(e, y) or L.join(e, x) == L.join(
                e, L.meet(x, y)):
            raise ValueError("certificate error in is_join_semidistributive")
    if not P['meet_semidistributive']:
        e, x, y = L.is_meet_semidistributive(certificate=True)[1]
        if L.meet(e, x) != L.meet(e, y) or L.meet(e, x) == L.meet(
                e, L.join(x, y)):
            raise ValueError("certificate error in is_meet_semidistributive")

    if not P['simple']:
        c = L.is_simple(certificate=True)[1]
        if len(L.congruence([c[randint(0, len(c) - 1)]])) == 1:
            raise ValueError("certificate error in is_simple")
    if not P['isoform']:
        c = L.is_isoform(certificate=True)[1]
        if len(c) == 1:
            raise ValueError("certificate error in is_isoform")
        if all(
                L.subposet(c[i]).is_isomorphic(L.subposet(c[i + 1]))
                for i in range(len(c) - 1)):
            raise ValueError("certificate error in is_isoform")
    if not P['uniform']:
        c = L.is_uniform(certificate=True)[1]
        if len(c) == 1:
            raise ValueError("certificate error in is_uniform")
        if all(len(c[i]) == len(c[i + 1]) for i in range(len(c) - 1)):
            raise ValueError("certificate error in is_uniform")
    if not P['regular']:
        c = L.is_regular(certificate=True)[1]
        if len(c[0]) == 1:
            raise ValueError("certificate error 1 in is_regular")
        if Set(c[1]) not in c[0]:
            raise ValueError("certificate error 2 in is_regular")
        if L.congruence([c[1]]) == c[0]:
            raise ValueError("certificate error 3 in is_regular")

    if not P['subdirectly_reducible']:
        x, y = L.is_subdirectly_reducible(certificate=True)[1]
        a = L.random_element()
        b = L.random_element()
        c = L.congruence([[a, b]])
        if len(c) != L.cardinality():
            for c_ in c:
                if x in c_:
                    if y not in c_:
                        raise ValueError(
                            "certificate error 1 in is_subdirectly_reducible")
                    break
            else:
                raise ValueError(
                    "certificate error 2 in is_subdirectly_reducible")

    if not P['join_distributive']:
        a = L.is_join_distributive(certificate=True)[1]
        L_ = L.sublattice(L.interval(a, L.join(L.upper_covers(a))))
        if L_.is_distributive():
            raise ValueError("certificate error in is_join_distributive")
    if not P['meet_distributive']:
        a = L.is_meet_distributive(certificate=True)[1]
        L_ = L.sublattice(L.interval(L.meet(L.lower_covers(a)), a))
        if L_.is_distributive():
            raise ValueError("certificate error in is_meet_distributive")

    ### Other ###

    # Other ways to recognize some boolean property
    if P['distributive'] != (set(L.join_primes()) == set(
            L.join_irreducibles())):
        raise ValueError(
            "every join-irreducible of a distributive lattice should be join-prime"
        )
    if P['distributive'] != (set(L.meet_primes()) == set(
            L.meet_irreducibles())):
        raise ValueError(
            "every meet-irreducible of a distributive lattice should be meet-prime"
        )
    if P['join_semidistributive'] != all(
            L.canonical_joinands(e) is not None for e in L):
        raise ValueError(
            "every element of join-semidistributive lattice should have canonical joinands"
        )
    if P['meet_semidistributive'] != all(
            L.canonical_meetands(e) is not None for e in L):
        raise ValueError(
            "every element of meet-semidistributive lattice should have canonical meetands"
        )

    # Random verification of a Boolean property
    if P['relatively_complemented']:
        a = L.random_element()
        b = L.random_element()
        if not L.sublattice(L.interval(a, b)).is_complemented():
            raise ValueError("rel. compl. error 3")
    if P['sectionally_complemented']:
        a = L.random_element()
        if not L.sublattice(L.interval(L.bottom(), a)).is_complemented():
            raise ValueError("sec. compl. error 3")
    if P['cosectionally_complemented']:
        a = L.random_element()
        if not L.sublattice(L.interval(a, L.top())).is_complemented():
            raise ValueError("cosec. compl. error 2")

    # Element set inclusions
    for s1, s2 in set_inclusions:
        if not set(attrcall(s1)(L)).issubset(set(attrcall(s2)(L))):
            raise ValueError("%s should be a subset of %s" % (s1, s2))

    # Sublattice-closed properties
    L_ = L.sublattice(Subsets(L).random_element())
    for p in sublattice_closed:
        if P[p] and not test_attrcall('is_' + p, L_):
            raise ValueError("property %s should apply to sublattices" % p)

    # Some sublattices
    L_ = L.center()  # Center is a Boolean lattice
    if not L_.is_atomic() or not L_.is_distributive():
        raise ValueError("error in center")
    if P['pseudocomplemented']:
        L_ = L.skeleton()  # Skeleton is a Boolean lattice
        if not L_.is_atomic() or not L_.is_distributive():
            raise ValueError("error in skeleton")
    L_ = L.frattini_sublattice()
    S = Subsets(L).random_element()
    if L.sublattice(S) == L and L.sublattice([e
                                              for e in S if e not in L_]) != L:
        raise ValueError("error in Frattini sublattice")
    L_ = L.maximal_sublattices()
    L_ = L_[randint(0, len(L_) - 1)]
    e = L.random_element()
    if e not in L_ and L.sublattice(list(L_) + [e]) != L:
        raise ValueError("error in maximal_sublattices")

    # Reverse functions: vertical composition and decomposition
    L_ = reduce(lambda a, b: a.vertical_composition(b),
                L.vertical_decomposition(), LatticePoset())
    if not L.is_isomorphic(L_):
        raise ValueError("error in vertical [de]composition")

    # Meet and join
    a = L.random_element()
    b = L.random_element()
    m = L.meet(a, b)
    j = L.join(a, b)
    m_ = L.subposet([
        e for e in L.principal_lower_set(a) if e in L.principal_lower_set(b)
    ]).top()
    j_ = L.subposet([
        e for e in L.principal_upper_set(a) if e in L.principal_upper_set(b)
    ]).bottom()
    if m != m_ or m != Ldual.join(a, b):
        raise ValueError("error in meet")
    if j != j_ or j != Ldual.meet(a, b):
        raise ValueError("error in join")

    # Misc misc
    e = L.neutral_elements()
    e = e[randint(0, len(e) - 1)]
    a = L.random_element()
    b = L.random_element()
    if not L.sublattice([e, a, b]).is_distributive():
        raise ValueError("error in neutral_elements")
예제 #16
0
def EllipticCurve_from_j(j):
    """
    Return an elliptic curve with given `j`-invariant.
    
    EXAMPLES::

        sage: E = EllipticCurve_from_j(0); E; E.j_invariant(); E.label()
        Elliptic Curve defined by y^2 + y = x^3 over Rational Field
        0
        '27a3'

        sage: E = EllipticCurve_from_j(1728); E; E.j_invariant(); E.label()
        Elliptic Curve defined by y^2 = x^3 - x over Rational Field
        1728
        '32a2'

        sage: E = EllipticCurve_from_j(1); E; E.j_invariant()
        Elliptic Curve defined by y^2 + x*y = x^3 + 36*x + 3455 over Rational Field
        1

    """
    try:
        K = j.parent()
    except AttributeError:
        K = rings.RationalField()
    if not rings.is_Field(K):
        K = K.fraction_field()

    char=K.characteristic()
    if char==2:
        if j == 0:
            return EllipticCurve(K, [ 0, 0, 1, 0, 0 ])
        else:
            return EllipticCurve(K, [ 1, 0, 0, 0, 1/j ])
    if char == 3:
        if j==0:
            return EllipticCurve(K, [ 0, 0, 0, 1, 0 ])
        else:
            return EllipticCurve(K, [ 0, j, 0, 0, -j**2 ])

    if K is rings.RationalField():
        # we construct the minimal twist, i.e. the curve with minimal
        # conductor with this j_invariant:

        if j == 0:
            return EllipticCurve(K, [ 0, 0, 1, 0, 0 ]) # 27a3
        if j == 1728:
            return EllipticCurve(K, [ 0, 0, 0, -1, 0 ]) # 32a2

        n = j.numerator()
        m = n-1728*j.denominator()
        a4 = -3*n*m
        a6 = -2*n*m**2

        # Now E=[0,0,0,a4,a6] has j-invariant j=n/d

        from sage.sets.set import Set
        for p in Set(n.prime_divisors()+m.prime_divisors()):
            e = min(a4.valuation(p)//2,a6.valuation(p)//3)
            if e>0:
                p  = p**e
                a4 /= p**2
                a6 /= p**3

        # Now E=[0,0,0,a4,a6] is minimal at all p != 2,3

        tw = [-1,2,-2,3,-3,6,-6]
        E1 = EllipticCurve([0,0,0,a4,a6])
        Elist = [E1] + [E1.quadratic_twist(t) for t in tw]
        crv_cmp = lambda E,F: cmp(E.conductor(),F.conductor())
        Elist.sort(cmp=crv_cmp)
        return Elist[0]

    # defaults for all other fields:    
    if j == 0:
        return EllipticCurve(K, [ 0, 0, 0, 0, 1 ])
    if j == 1728:
        return EllipticCurve(K, [ 0, 0, 0, 1, 0 ])
    k=j-1728
    return EllipticCurve(K, [0,0,0,-3*j*k, -2*j*k**2])
예제 #17
0
 def support(self):
     return [self._ptdict[P] for P in Set([d for d in self._data])]
def FinitelyGeneratedAbelianPresentation(int_list):
    r"""
    Return canonical presentation of finitely generated abelian group.

    INPUT:

    - ``int_list`` -- List of integers defining the group to be returned, the defining list
      is reduced to the invariants of the input list before generating the corresponding
      group.

    OUTPUT:

    Finitely generated abelian group, `\ZZ_{n_1} \times \ZZ_{n_2} \times \cdots \times \ZZ_{n_k}`
    as a finite presentation, where `n_i` forms the invariants of the input list.

    EXAMPLES::

        sage: groups.presentation.FGAbelian([2,2])
        Finitely presented group < a, b | a^2, b^2, a^-1*b^-1*a*b >
        sage: groups.presentation.FGAbelian([2,3])
        Finitely presented group < a | a^6 >
        sage: groups.presentation.FGAbelian([2,4])
        Finitely presented group < a, b | a^2, b^4, a^-1*b^-1*a*b >

    You can create free abelian groups::

        sage: groups.presentation.FGAbelian([0])
        Finitely presented group < a |  >
        sage: groups.presentation.FGAbelian([0,0])
        Finitely presented group < a, b | a^-1*b^-1*a*b >
        sage: groups.presentation.FGAbelian([0,0,0])
        Finitely presented group < a, b, c | a^-1*c^-1*a*c, a^-1*b^-1*a*b, c^-1*b^-1*c*b >

    And various infinite abelian groups::

        sage: groups.presentation.FGAbelian([0,2])
        Finitely presented group < a, b | a^2, a^-1*b^-1*a*b >
        sage: groups.presentation.FGAbelian([0,2,2])
        Finitely presented group < a, b, c | a^2, b^2, a^-1*c^-1*a*c, a^-1*b^-1*a*b, c^-1*b^-1*c*b >

    Outputs are reduced to minimal generators and relations::

        sage: groups.presentation.FGAbelian([3,5,2,7,3])
        Finitely presented group < a, b | a^3, b^210, a^-1*b^-1*a*b >
        sage: groups.presentation.FGAbelian([3,210])
        Finitely presented group < a, b | a^3, b^210, a^-1*b^-1*a*b >

    The trivial group is an acceptable output::

        sage: groups.presentation.FGAbelian([])
        Finitely presented group <  |  >
        sage: groups.presentation.FGAbelian([1])
        Finitely presented group <  |  >
        sage: groups.presentation.FGAbelian([1,1,1,1,1,1,1,1,1,1])
        Finitely presented group <  |  >

    Input list must consist of positive integers::

        sage: groups.presentation.FGAbelian([2,6,3,9,-4])
        Traceback (most recent call last):
        ...
        ValueError: input list must contain nonnegative entries
        sage: groups.presentation.FGAbelian([2,'a',4])
        Traceback (most recent call last):
        ...
        TypeError: unable to convert x (=a) to an integer

    TESTS::

        sage: ag = groups.presentation.FGAbelian([2,2])
        sage: ag.as_permutation_group().is_isomorphic(groups.permutation.KleinFour())
        True
        sage: G = groups.presentation.FGAbelian([2,4,8])
        sage: C2 = CyclicPermutationGroup(2)
        sage: C4 = CyclicPermutationGroup(4)
        sage: C8 = CyclicPermutationGroup(8)
        sage: gg = (C2.direct_product(C4)[0]).direct_product(C8)[0]
        sage: gg.is_isomorphic(G.as_permutation_group())
        True
        sage: all([groups.presentation.FGAbelian([i]).as_permutation_group().is_isomorphic(groups.presentation.Cyclic(i).as_permutation_group()) for i in [2..35]])
        True
    """
    from sage.groups.free_group import _lexi_gen
    check_ls = [Integer(x) for x in int_list if Integer(x) >= 0]
    if len(check_ls) != len(int_list):
        raise ValueError('input list must contain nonnegative entries')

    col_sp = diagonal_matrix(int_list).column_space()
    invariants = FGP_Module(ZZ**(len(int_list)), col_sp).invariants()
    name_gen = _lexi_gen()
    F = FreeGroup([name_gen.next() for i in invariants])
    ret_rls = [F([i+1])**invariants[i] for i in range(len(invariants)) if invariants[i]!=0]

    # Build commutator relations
    gen_pairs = list(Set(F.gens()).subsets(2))
    ret_rls = ret_rls + [x[0]**(-1)*x[1]**(-1)*x[0]*x[1] for x in gen_pairs]
    return FinitelyPresentedGroup(F, tuple(ret_rls))
예제 #19
0
def EllipticCurve_from_j(j, minimal_twist=True):
    """
    Return an elliptic curve with given `j`-invariant.

    INPUT:

    - ``j`` -- an element of some field.

    - ``minimal_twist`` (boolean, default True) -- If True and ``j`` is in `\QQ`, the curve returned is a
      minimal twist, i.e. has minimal conductor.  If `j` is not in `\QQ` this parameter is ignored.

    OUTPUT:

    An elliptic curve with `j`-invariant `j`.

    EXAMPLES::

        sage: E = EllipticCurve_from_j(0); E; E.j_invariant(); E.label()
        Elliptic Curve defined by y^2 + y = x^3 over Rational Field
        0
        '27a3'

        sage: E = EllipticCurve_from_j(1728); E; E.j_invariant(); E.label()
        Elliptic Curve defined by y^2 = x^3 - x over Rational Field
        1728
        '32a2'

        sage: E = EllipticCurve_from_j(1); E; E.j_invariant()
        Elliptic Curve defined by y^2 + x*y = x^3 + 36*x + 3455 over Rational Field
        1

    The ``minimal_twist`` parameter (ignored except over `\QQ` and
    True by default) controls whether or not a minimal twist is
    computed::

        sage: EllipticCurve_from_j(100)
        Elliptic Curve defined by y^2 = x^3 + x^2 + 3392*x + 307888 over Rational Field
        sage: _.conductor()
        33129800
        sage: EllipticCurve_from_j(100, minimal_twist=False)
        Elliptic Curve defined by y^2 = x^3 + 488400*x - 530076800 over Rational Field
        sage: _.conductor()
        298168200

    Since computing the minimal twist requires factoring both `j` and
    `j-1728` the following example would take a long time without
    setting ``minimal_twist`` to False::

       sage: E = EllipticCurve_from_j(2^256+1,minimal_twist=False)
       sage: E.j_invariant() == 2^256+1
       True

    """
    try:
        K = j.parent()
    except AttributeError:
        K = rings.RationalField()
    if K not in _Fields:
        K = K.fraction_field()

    char = K.characteristic()
    if char == 2:
        if j == 0:
            return EllipticCurve(K, [0, 0, 1, 0, 0])
        else:
            return EllipticCurve(K, [1, 0, 0, 0, 1 / j])
    if char == 3:
        if j == 0:
            return EllipticCurve(K, [0, 0, 0, 1, 0])
        else:
            return EllipticCurve(K, [0, j, 0, 0, -j**2])

    if K is rings.RationalField():
        # we construct the minimal twist, i.e. the curve with minimal
        # conductor with this j_invariant:
        if j == 0:
            return EllipticCurve(K, [0, 0, 1, 0, 0])  # 27a3
        if j == 1728:
            return EllipticCurve(K, [0, 0, 0, -1, 0])  # 32a2

        if not minimal_twist:
            k = j - 1728
            return EllipticCurve(K, [0, 0, 0, -3 * j * k, -2 * j * k**2])

        n = j.numerator()
        m = n - 1728 * j.denominator()
        a4 = -3 * n * m
        a6 = -2 * n * m**2

        # Now E=[0,0,0,a4,a6] has j-invariant j=n/d
        from sage.sets.set import Set
        for p in Set(n.prime_divisors() + m.prime_divisors()):
            e = min(a4.valuation(p) // 2, a6.valuation(p) // 3)
            if e > 0:
                p = p**e
                a4 /= p**2
                a6 /= p**3

        # Now E=[0,0,0,a4,a6] is minimal at all p != 2,3
        tw = [-1, 2, -2, 3, -3, 6, -6]
        E1 = EllipticCurve([0, 0, 0, a4, a6])
        Elist = [E1] + [E1.quadratic_twist(t) for t in tw]
        crv_cmp = lambda E, F: cmp(E.conductor(), F.conductor())
        Elist.sort(cmp=crv_cmp)
        return Elist[0]

    # defaults for all other fields:
    if j == 0:
        return EllipticCurve(K, [0, 0, 0, 0, 1])
    if j == 1728:
        return EllipticCurve(K, [0, 0, 0, 1, 0])
    k = j - 1728
    return EllipticCurve(K, [0, 0, 0, -3 * j * k, -2 * j * k**2])
예제 #20
0
class PositiveIntegersOrderedByDivisibilityFacade(UniqueRepresentation,
                                                  Parent):
    r"""
    An example of a facade poset: the positive integers ordered by divisibility

    This class provides a minimal implementation of a facade poset

    EXAMPLES::

        sage: P = Posets().example("facade"); P
        An example of a facade poset: the positive integers ordered by divisibility

        sage: P(5)
        5
        sage: P(0)
        Traceback (most recent call last):
        ...
        ValueError: Can't coerce `0` in any parent `An example of a facade poset: the positive integers ordered by divisibility` is a facade for

        sage: 3 in P
        True
        sage: 0 in P
        False
    """

    element_class = type(Set([]))

    def __init__(self):
        r"""
        EXAMPLES::

            sage: P = Posets().example("facade"); P
            An example of a facade poset: the positive integers ordered by divisibility
            sage: P.category()
            Category of facade posets
            sage: type(P)
            <class 'sage.categories.examples.posets.PositiveIntegersOrderedByDivisibilityFacade_with_category'>
            sage: TestSuite(P).run()
        """
        Parent.__init__(self, facade=(PositiveIntegers(), ), category=Posets())

    def _repr_(self):
        r"""
        TESTS::

            sage: S = Posets().example("facade")
            sage: S._repr_()
            'An example of a facade poset: the positive integers ordered by divisibility'
        """
        return "An example of a facade poset: the positive integers ordered by divisibility"

    def le(self, x, y):
        r"""
        Returns whether `x` is divisible by `y`

        EXAMPLES::

            sage: P = Posets().example("facade")
            sage: P.le(3, 6)
            True
            sage: P.le(3, 3)
            True
            sage: P.le(3, 7)
            False
        """
        return x.divides(y)
예제 #21
0
def steiner_triple_system(n):
    r"""
    Returns a Steiner Triple System

    A Steiner Triple System (STS) of a set `\{0,...,n-1\}`
    is a family `S` of 3-sets such that for any `i \not = j`
    there exists exactly one set of `S` in which they are
    both contained.

    It can alternatively be thought of as a factorization of
    the complete graph `K_n` with triangles.

    A Steiner Triple System of a `n`-set exists if and only if
    `n \equiv 1 \pmod 6` or `n \equiv 3 \pmod 6`, in which case
    one can be found through Bose's and Skolem's constructions,
    respectively [AndHonk97]_.

    INPUT:

    - ``n`` returns a Steiner Triple System of `\{0,...,n-1\}`

    EXAMPLE:

    A Steiner Triple System on `9` elements ::

        sage: sts = designs.steiner_triple_system(9)
        sage: sts
        Incidence structure with 9 points and 12 blocks
        sage: list(sts)
        [[0, 1, 5], [0, 2, 4], [0, 3, 6], [0, 7, 8], [1, 2, 3], [1, 4, 7], [1, 6, 8], [2, 5, 8], [2, 6, 7], [3, 4, 8], [3, 5, 7], [4, 5, 6]]

    As any pair of vertices is covered once, its parameters are ::

        sage: sts.parameters(t=2)
        (2, 9, 3, 1)

    An exception is raised for invalid values of ``n`` ::

        sage: designs.steiner_triple_system(10)
        Traceback (most recent call last):
        ...
        EmptySetError: Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6

    REFERENCE:

    .. [AndHonk97] A short course in Combinatorial Designs,
      Ian Anderson, Iiro Honkala,
      Internet Editions, Spring 1997,
      http://www.utu.fi/~honkala/designs.ps
    """

    name = "Steiner Triple System on " + str(n) + " elements"

    if n % 6 == 3:
        t = (n - 3) // 6
        Z = range(2 * t + 1)

        T = lambda (x, y): x + (2 * t + 1) * y

        sts = [[(i,0),(i,1),(i,2)] for i in Z] + \
            [[(i,k),(j,k),(((t+1)*(i+j)) % (2*t+1),(k+1)%3)] for k in range(3) for i in Z for j in Z if i != j]

    elif n % 6 == 1:

        t = (n - 1) // 6
        N = range(2 * t)
        T = lambda (x, y): x + y * t * 2 if (x, y) != (-1, -1) else n - 1

        L1 = lambda i, j: (i + j) % ((n - 1) // 3)
        L = lambda i, j: L1(i, j) // 2 if L1(i, j) % 2 == 0 else t + (L1(i, j)
                                                                      - 1) // 2

        sts = [[(i,0),(i,1),(i,2)] for i in range(t)] + \
            [[(-1,-1),(i,k),(i-t,(k+1) % 3)] for i in range(t,2*t) for k in [0,1,2]] + \
            [[(i,k),(j,k),(L(i,j),(k+1) % 3)] for k in [0,1,2] for i in N for j in N if i < j]

    else:
        raise EmptySetError(
            "Steiner triple systems only exist for n = 1 mod 6 or n = 3 mod 6")

    from sage.sets.set import Set
    sts = Set(map(lambda x: Set(map(T, x)), sts))

    return BlockDesign(n, sts, name=name)
def local_zero_density_congruence(self, p, m, Zvec=None, NZvec=None):
    """
    Finds the Zero-type local density of Q representing `m` at `p`,
    allowing certain congruence conditions mod p.


    INPUT:

        Q -- quadratic form assumed to be block diagonal and `p`-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:

        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_zero_density_congruence(2, 2, None, None)
        0
        sage: Q.local_zero_density_congruence(2, 4, None, None)
        1/2
        sage: Q.local_zero_density_congruence(3, 6, None, None)
        0
        sage: Q.local_zero_density_congruence(3, 9, None, None)
        2/9

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_zero_density_congruence(2, 2, None, None)
        0
        sage: Q.local_zero_density_congruence(2, 4, None, None)
        1/4
        sage: Q.local_zero_density_congruence(3, 6, None, None)
        0
        sage: Q.local_zero_density_congruence(3, 9, None, None)
        8/81

    """
    ## DIAGNOSTIC
    verbose(" In local_zero_density_congruence with ")
    verbose(" Q is: \n" + str(self))
    verbose(" p = " + str(p))
    verbose(" m = " + str(m))
    verbose(" Zvec = " + str(Zvec))
    verbose(" NZvec = " + str(NZvec))

    ## Put the Zvec congruence condition in a standard form
    if Zvec is None:
        Zvec = []

    n = self.dim()

    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec is not None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec is not None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")

    p2 = p * p

    ## Check some conditions for no zero-type solutions to exist
    if ((m % (p2) != 0) or (NZvec is not None)):
        return 0

    ## Use the reduction procedure to return the result
    return self.local_density_congruence(p, m / p2, None,
                                         None) / p**(self.dim() - 2)
예제 #23
0
def _normal_label(g, comb_emb, external_face):
    r"""
    Helper function to schnyder method for computing coordinates in
    the plane to plot a planar graph with no edge crossings.

    Constructs a normal labelling of a triangular graph g, given the
    planar combinatorial embedding of g and a designated external
    face.  Returns labels dictionary.  The normal label is constructed
    by first contracting the graph down to its external face, then
    expanding the graph back to the original while simultaneously
    adding angle labels.

    INPUT:

    - g -- the graph to find the normal labeling of (g must be triangulated)
    - ``comb_emb`` -- a planar combinatorial embedding of g
    - ``external_face`` -- the list of three edges in the external face of g

    OUTPUT:

    x -- tuple with entries

        x[0] = dict of dicts of normal labeling for each vertex of g and each
        adjacent neighbors u,v (u < v) of vertex:

        { vertex : { (u,v): angel_label } }

        x[1] = (v1,v2,v3) tuple of the three vertices of the external face.

    EXAMPLES::

        sage: from sage.graphs.schnyder import _triangulate, _normal_label, _realizer
        sage: g = Graph(graphs.CycleGraph(7))
        sage: g.is_planar(set_embedding=True)
        True
        sage: faces = g.faces(g._embedding)
        sage: _triangulate(g, g._embedding)
        [(2, 0), (4, 2), (6, 4), (1, 3), (6, 1), (3, 5), (4, 0), (6, 3)]
        sage: tn = _normal_label(g, g._embedding, faces[0])
        sage: _realizer(g, tn)
        ({0: [<sage.graphs.schnyder.TreeNode object at ...>]},
         (0, 1, 2))
    """
    contracted = []
    contractible = []

    labels = {}

    external_vertices = sorted(
        [external_face[0][0], external_face[1][0], external_face[2][0]])
    v1, v2, v3 = external_vertices
    v1_neighbors = Set(g.neighbors(v1))

    neighbor_count = {}
    for v in g.vertices():
        neighbor_count[v] = 0
    for v in g.neighbors(v1):
        neighbor_count[v] = len(v1_neighbors.intersection(Set(g.neighbors(v))))

    for v in v1_neighbors:
        if v in [v1, v2, v3]:
            continue
        if neighbor_count[v] == 2:
            contractible.append(v)

    # contraction phase:

    while g.order() > 3:
        try:
            v = contractible.pop()
        except Exception:
            raise RuntimeError(
                'Contractible list is empty but graph still has %d vertices.  (Expected 3.)'
                % g.order())

            break
        # going to contract v
        v_neighbors = Set(g.neighbors(v))
        contracted.append(
            (v, v_neighbors, v_neighbors - v1_neighbors - Set([v1])))
        g.delete_vertex(v)
        v1_neighbors -= Set([v])
        for w in v_neighbors - v1_neighbors - Set([v1]):
            # adding edge (v1, w)
            g.add_edge((v1, w))
        if g.order() == 3:
            break
        v1_neighbors += v_neighbors - Set([v1])
        contractible = []
        for w in g.neighbors(v1):
            if (len(v1_neighbors.intersection(Set(
                    g.neighbors(w))))) == 2 and w not in [v1, v2, v3]:
                contractible.append(w)

    # expansion phase:

    v1, v2, v3 = g.vertices()  # always in sorted order

    labels[v1] = {(v2, v3): 1}
    labels[v2] = {(v1, v3): 2}
    labels[v3] = {(v1, v2): 3}

    while len(contracted):
        v, new_neighbors, neighbors_to_delete = contracted.pop()
        # going to add back vertex v
        labels[v] = {}

        for w in neighbors_to_delete:
            g.delete_edge((v1, w))

        if len(neighbors_to_delete) == 0:
            # we are adding v into the face new_neighbors
            w1, w2, w3 = sorted(new_neighbors)

            labels[v] = {
                (w1, w2): labels[w3].pop((w1, w2)),
                (w2, w3): labels[w1].pop((w2, w3)),
                (w1, w3): labels[w2].pop((w1, w3))
            }
            labels[w1][tuple(sorted((w2, v)))] = labels[v][(w2, w3)]
            labels[w1][tuple(sorted((w3, v)))] = labels[v][(w2, w3)]

            labels[w2][tuple(sorted((w1, v)))] = labels[v][(w1, w3)]
            labels[w2][tuple(sorted((w3, v)))] = labels[v][(w1, w3)]

            labels[w3][tuple(sorted((w1, v)))] = labels[v][(w1, w2)]
            labels[w3][tuple(sorted((w2, v)))] = labels[v][(w1, w2)]
        else:
            new_neighbors_set = Set(new_neighbors)
            angles_out_of_v1 = set()
            vertices_in_order = []
            l = []
            for angle in labels[v1].keys():
                if len(Set(angle).intersection(new_neighbors_set)) == 2:
                    angles_out_of_v1.add(angle)
                    l = l + list(angle)
            # find a unique element in l
            l.sort()
            i = 0
            while i < len(l):
                if l[i] == l[i + 1]:
                    i += 2
                else:
                    break

            angle_set = Set(angles_out_of_v1)

            vertices_in_order.append(l[i])
            while len(angles_out_of_v1) > 0:
                for angle in angles_out_of_v1:
                    if vertices_in_order[-1] in angle:
                        break
                if angle[0] == vertices_in_order[-1]:
                    vertices_in_order.append(angle[1])
                else:
                    vertices_in_order.append(angle[0])
                angles_out_of_v1.remove(angle)

            w = vertices_in_order

            # is w[0] a 2 or a 3?
            top_label = labels[w[0]][tuple(sorted((v1, w[1])))]
            if top_label == 3:
                bottom_label = 2
            else:
                bottom_label = 3
            i = 0
            while i < len(w) - 1:
                labels[v][tuple(sorted((w[i], w[i + 1])))] = 1
                labels[w[i]][tuple(sorted((w[i + 1], v)))] = top_label
                labels[w[i + 1]][tuple(sorted((w[i], v)))] = bottom_label
                i += 1

            labels[v][tuple(sorted((v1, w[0])))] = bottom_label
            labels[v][tuple(sorted((v1, w[-1])))] = top_label

            labels[w[0]][tuple(sorted((v1, v)))] = top_label
            labels[w[-1]][tuple(sorted((v1, v)))] = bottom_label
            labels[v1][tuple(sorted((w[0], v)))] = 1
            labels[v1][tuple(sorted((w[-1], v)))] = 1

            # delete all the extra labels

            for angle in angle_set:
                labels[v1].pop(angle)

            labels[w[0]].pop(tuple(sorted((v1, w[1]))))
            labels[w[-1]].pop(tuple(sorted((v1, w[-2]))))

            i = 1
            while i < len(w) - 1:
                labels[w[i]].pop(tuple(sorted((v1, w[i + 1]))))
                labels[w[i]].pop(tuple(sorted((v1, w[i - 1]))))
                i += 1

        for w in new_neighbors:
            g.add_edge((v, w))

    return labels, (v1, v2, v3)
def local_badI_density_congruence(self, p, m, Zvec=None, NZvec=None):
    """
    Finds the Bad-type I local density of Q representing `m` at `p`.
    (Assuming that p > 2 and Q is given in local diagonal form.)


    INPUT:

        Q -- quadratic form assumed to be block diagonal and `p`-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:

        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_badI_density_congruence(2, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(2, 2, None, None)
        1
        sage: Q.local_badI_density_congruence(2, 4, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 9, None, None)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_badI_density_congruence(2, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(2, 2, None, None)
        0
        sage: Q.local_badI_density_congruence(2, 4, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 2, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 9, None, None)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,3,9])
        sage: Q.local_badI_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 3, None, None)
        4/3
        sage: Q.local_badI_density_congruence(3, 6, None, None)
        4/3
        sage: Q.local_badI_density_congruence(3, 9, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 18, None, None)
        0


    """
    ## DIAGNOSTIC
    verbose(" In local_badI_density_congruence with ")
    verbose(" Q is: \n" + str(self))
    verbose(" p = " + str(p))
    verbose(" m = " + str(m))
    verbose(" Zvec = " + str(Zvec))
    verbose(" NZvec = " + str(NZvec))

    ## Put the Zvec congruence condition in a standard form
    if Zvec is None:
        Zvec = []

    n = self.dim()

    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec is not None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec is not None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")

    ## Define the indexing set S_0, and determine if S_1 is empty:
    ## -----------------------------------------------------------
    S0 = []
    S1_empty_flag = True  ## This is used to check if we should be computing BI solutions at all!
    ## (We should really to this earlier, but S1 must be non-zero to proceed.)

    ## Find the valuation of each variable (which will be the same over 2x2 blocks),
    ## remembering those of valuation 0 and if an entry of valuation 1 exists.
    for i in range(n):

        ## Compute the valuation of each index, allowing for off-diagonal terms
        if (self[i, i] == 0):
            if (i == 0):
                val = valuation(self[i, i + 1],
                                p)  ## Look at the term to the right
            else:
                if (i == n - 1):
                    val = valuation(self[i - 1, i],
                                    p)  ## Look at the term above
                else:
                    val = valuation(
                        self[i, i + 1] + self[i - 1, i], p
                    )  ## Finds the valuation of the off-diagonal term since only one isn't zero
        else:
            val = valuation(self[i, i], p)

        if (val == 0):
            S0 += [i]
        elif (val == 1):
            S1_empty_flag = False  ## Need to have a non-empty S1 set to proceed with Bad-type I reduction...

    ## Check that S1 is non-empty and p|m to proceed, otherwise return no solutions.
    if S1_empty_flag or m % p != 0:
        return 0

    ## Check some conditions for no bad-type I solutions to exist
    if (NZvec is not None) and (len(Set(S0).intersection(Set(NZvec))) != 0):
        return 0

    ## Check that the form is primitive...                     WHY DO WE NEED TO DO THIS?!?
    if (S0 == []):
        print(" Using Q = " + str(self))
        print(" and p = " + str(p))
        raise RuntimeError("Oops! The form is not primitive!")

    ## DIAGNOSTIC
    verbose(" m = " + str(m) + "   p = " + str(p))
    verbose(" S0 = " + str(S0))
    verbose(" len(S0) = " + str(len(S0)))

    ## Make the form Qnew for the reduction procedure:
    ## -----------------------------------------------
    Qnew = deepcopy(self)  ## TO DO:  DO THIS WITHOUT A copy(). =)
    for i in range(n):
        if i in S0:
            Qnew[i, i] = p * Qnew[i, i]
            if ((p == 2) and (i < n - 1)):
                Qnew[i, i + 1] = p * Qnew[i, i + 1]
        else:
            Qnew[i, i] = Qnew[i, i] / p
            if ((p == 2) and (i < n - 1)):
                Qnew[i, i + 1] = Qnew[i, i + 1] / p

    ## DIAGNOSTIC
    verbose("\n\n Check of Bad-type I reduction: \n")
    verbose(" Q is " + str(self))
    verbose(" Qnew is " + str(Qnew))
    verbose(" p = " + str(p))
    verbose(" m / p = " + str(m / p))
    verbose(" NZvec " + str(NZvec))

    ## Do the reduction
    Zvec_geq_1 = list(Set([i for i in Zvec if i not in S0]))
    if NZvec is None:
        NZvec_geq_1 = NZvec
    else:
        NZvec_geq_1 = list(Set([i for i in NZvec if i not in S0]))

    return QQ(p**(1 - len(S0))) * Qnew.local_good_density_congruence(
        p, m / p, Zvec_geq_1, NZvec_geq_1)
예제 #25
0
def cyclotomic_cosets(q, n, t=None):
    r"""    
    INPUT: q,n,t positive integers (or t=None) Some type-checking of
    inputs is performed.
    
    OUTPUT: q-cyclotomic cosets mod n (or, if t is not None, the q-cyclotomic
    coset mod n containing t)
    
    Let q, n be relatively print positive integers and let
    `A = q^{ZZ}`. The group A acts on ZZ/nZZ by multiplication.
    The orbits of this action are "cyclotomic cosets", or more
    precisely "q-cyclotomic cosets mod n". Sometimes the smallest
    element of the coset is called the "coset leader". The algorithm
    will always return the cosets as sorted lists of lists, so the
    coset leader will always be the first element in the list.
    
    These cosets arise in the theory of duadic codes and minimal
    polynomials of finite fields. Fix a primitive element `z`
    of `GF(q^k)`. The minimal polynomial of `z^s` over
    `GF(q)` is given by
    
    .. math::
    
             M_s(x) = \prod_{i \in C_s} (x-z^i),     
    
    
    where `C_s` is the q-cyclotomic coset mod n containing s,
    `n = q^k - 1`.
    
    EXAMPLES::
    
        sage: cyclotomic_cosets(2,11)
        [[0], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
        sage: cyclotomic_cosets(2,15)
        [[0], [1, 2, 4, 8], [3, 6, 9, 12], [5, 10], [7, 11, 13, 14]]
        sage: cyclotomic_cosets(2,15,5)
        [5, 10]
        sage: cyclotomic_cosets(3,16)
        [[0], [1, 3, 9, 11], [2, 6], [4, 12], [5, 7, 13, 15], [8], [10, 14]]
        sage: F.<z> = GF(2^4, "z")
        sage: P.<x> = PolynomialRing(F,"x")
        sage: a = z^5
        sage: a.minimal_polynomial()
        x^2 + x + 1
        sage: prod([x-z^i for i in [5, 10]])
        x^2 + x + 1
        sage: cyclotomic_cosets(3,2,0)
        [0]
        sage: cyclotomic_cosets(3,2,1)
        [1]
        sage: cyclotomic_cosets(3,2,2)
        [0]
    
    This last output looks strange but is correct, since the elements of
    the cosets are in ZZ/nZZ and 2 = 0 in ZZ/2ZZ.
    """
    from sage.misc.misc import srange
    if not (t == None) and type(t) <> Integer:
        raise TypeError, "Optional input %s must None or an integer." % t
    if q < 2 or n < 2:
        raise TypeError, "Inputs %s and %s must be > 1." % (q, n)
    if GCD(q, n) <> 1:
        raise TypeError, "Inputs %s and %s must be relative prime." % (q, n)
    if t <> None and type(t) == Integer:
        S = Set([t * q**i % n for i in srange(n)])
        L = list(S)
        L.sort()
        return L
    ccs = Set([])
    ccs_list = [[0]]
    for s in range(1, n):
        if not (s in ccs):
            S = Set([s * q**i % n for i in srange(n)])
            L = list(S)
            L.sort()
            ccs = ccs.union(S)
            ccs_list.append(L)
    return ccs_list
def local_good_density_congruence_odd(self, p, m, Zvec, NZvec):
    """
    Finds the Good-type local density of Q representing `m` at `p`.
    (Assuming that `p` > 2 and Q is given in local diagonal form.)

    The additional congruence condition arguments Zvec and NZvec can
    be either a list of indices or None.  Zvec = [] is equivalent to
    Zvec = None which both impose no additional conditions, but NZvec
    = [] returns no solutions always while NZvec = None imposes no
    additional condition.

    TO DO: Add type checking for Zvec, NZvec, and that Q is in local
    normal form.

    INPUT:

        Q -- quadratic form assumed to be diagonal and p-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:

        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_good_density_congruence_odd(3, 1, None, None)
        2/3

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_good_density_congruence_odd(3, 1, None, None)
        8/9

    """
    n = self.dim()

    ## Put the Zvec congruence condition in a standard form
    if Zvec is None:
        Zvec = []

    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec is not None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec is not None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")

    ## Assuming Q is diagonal, find the indices of the p-unit (diagonal) entries
    UnitVec = [i for i in range(n) if (self[i, i] % p) != 0]
    NonUnitVec = list(Set(range(n)) - Set(UnitVec))

    ## Take cases on the existence of additional non-zero congruence conditions (mod p)
    UnitVec_minus_Zvec = list(Set(UnitVec) - Set(Zvec))
    NonUnitVec_minus_Zvec = list(Set(NonUnitVec) - Set(Zvec))
    Q_Unit_minus_Zvec = self.extract_variables(UnitVec_minus_Zvec)

    if (NZvec is None):
        if m % p != 0:
            total = Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(
                p, m) * p**len(NonUnitVec_minus_Zvec)  ## m != 0 (mod p)
        else:
            total = (Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(
                p, m) - 1) * p**len(NonUnitVec_minus_Zvec)  ## m == 0 (mod p)

    else:
        UnitVec_minus_ZNZvec = list(Set(UnitVec) - (Set(Zvec) + Set(NZvec)))
        NonUnitVec_minus_ZNZvec = list(
            Set(NonUnitVec) - (Set(Zvec) + Set(NZvec)))
        Q_Unit_minus_ZNZvec = self.extract_variables(UnitVec_minus_ZNZvec)

        if m % p != 0:  ## m != 0 (mod p)
            total = Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(p, m) * p**len(NonUnitVec_minus_Zvec) \
                    - Q_Unit_minus_ZNZvec.count_modp_solutions__by_Gauss_sum(p, m) * p**len(NonUnitVec_minus_ZNZvec)
        else:  ## m == 0 (mod p)
            total = (Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(p, m) - 1) * p**len(NonUnitVec_minus_Zvec) \
                    - (Q_Unit_minus_ZNZvec.count_modp_solutions__by_Gauss_sum(p, m) - 1) * p**len(NonUnitVec_minus_ZNZvec)

    ## Return the Good-type representation density
    good_density = QQ(total) / p**(n - 1)
    return good_density
예제 #27
0
def BCHCode(n, delta, F, b=0):
    r"""
    A 'Bose-Chaudhuri-Hockenghem code' (or BCH code for short) is the
    largest possible cyclic code of length n over field F=GF(q), whose
    generator polynomial has zeros (which contain the set)
    `Z = \{a^{b},a^{b+1}, ..., a^{b+delta-2}\}`, where a is a
    primitive `n^{th}` root of unity in the splitting field
    `GF(q^m)`, b is an integer `0\leq b\leq n-delta+1`
    and m is the multiplicative order of q modulo n. (The integers
    `b,...,b+delta-2` typically lie in the range
    `1,...,n-1`.) The integer `delta \geq 1` is called
    the "designed distance". The length n of the code and the size q of
    the base field must be relatively prime. The generator polynomial
    is equal to the least common multiple of the minimal polynomials of
    the elements of the set `Z` above.
    
    Special cases are b=1 (resulting codes are called 'narrow-sense'
    BCH codes), and `n=q^m-1` (known as 'primitive' BCH
    codes).
    
    It may happen that several values of delta give rise to the same
    BCH code. The largest one is called the Bose distance of the code.
    The true minimum distance, d, of the code is greater than or equal
    to the Bose distance, so `d\geq delta`.
    
    EXAMPLES::
    
        sage: FF.<a> = GF(3^2,"a")
        sage: x = PolynomialRing(FF,"x").gen()
        sage: L = [b.minpoly() for b in [a,a^2,a^3]]; g = LCM(L)
        sage: f = x^(8)-1
        sage: g.divides(f)
        True
        sage: C = CyclicCode(8,g); C
        Linear code of length 8, dimension 4 over Finite Field of size 3
        sage: C.minimum_distance()
        4
        sage: C = BCHCode(8,3,GF(3),1); C
        Linear code of length 8, dimension 4 over Finite Field of size 3
        sage: C.minimum_distance()
        4
        sage: C = BCHCode(8,3,GF(3)); C
        Linear code of length 8, dimension 5 over Finite Field of size 3
        sage: C.minimum_distance()
        3
        sage: C = BCHCode(26, 5, GF(5), b=1); C
        Linear code of length 26, dimension 10 over Finite Field of size 5
    
    """
    from sage.misc.misc import srange
    q = F.order()
    R = IntegerModRing(n)
    m = R(q).multiplicative_order()
    FF = GF(q**m, "z")
    z = FF.gen()
    e = z.multiplicative_order() / n
    a = z**e  # order n
    P = PolynomialRing(F, "x")
    x = P.gen()
    cosets = Set([])
    for i in srange(b, b + delta - 1):
        cosets = cosets.union(Set(cyclotomic_cosets(q, n, i)))
    L0 = [a**j for j in cosets]
    L1 = [P(ai.minpoly()) for ai in L0]
    g = P(LCM(L1))
    #print cosets, "\n", g, "\n", (x**n-1).factor(), "\n", L1, "\n", g.divides(x**n-1)
    if not (g.divides(x**n - 1)):
        raise ValueError, "BCH codes does not exist with the given input."
    return CyclicCodeFromGeneratingPolynomial(n, g)
def local_badII_density_congruence(self, p, m, Zvec=None, NZvec=None):
    """
    Finds the Bad-type II local density of Q representing `m` at `p`.
    (Assuming that `p` > 2 and Q is given in local diagonal form.)


     INPUT:

        Q -- quadratic form assumed to be block diagonal and p-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:

        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_badII_density_congruence(2, 1, None, None)
        0
        sage: Q.local_badII_density_congruence(2, 2, None, None)
        0
        sage: Q.local_badII_density_congruence(2, 4, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 9, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 27, None, None)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,3,9,9])
        sage: Q.local_badII_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 3, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 9, None, None)
        4/27
        sage: Q.local_badII_density_congruence(3, 18, None, None)
        4/9

    """
    ## DIAGNOSTIC
    verbose(" In local_badII_density_congruence with ")
    verbose(" Q is: \n" + str(self))
    verbose(" p = " + str(p))
    verbose(" m = " + str(m))
    verbose(" Zvec = " + str(Zvec))
    verbose(" NZvec = " + str(NZvec))

    ## Put the Zvec congruence condition in a standard form
    if Zvec is None:
        Zvec = []

    n = self.dim()

    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec is not None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec is not None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")

    ## Define the indexing sets S_i:
    ## -----------------------------
    S0 = []
    S1 = []
    S2plus = []

    for i in range(n):

        ## Compute the valuation of each index, allowing for off-diagonal terms
        if (self[i, i] == 0):
            if (i == 0):
                val = valuation(self[i, i + 1],
                                p)  ## Look at the term to the right
            elif (i == n - 1):
                val = valuation(self[i - 1, i], p)  ## Look at the term above
            else:
                val = valuation(
                    self[i, i + 1] + self[i - 1, i], p
                )  ## Finds the valuation of the off-diagonal term since only one isn't zero
        else:
            val = valuation(self[i, i], p)

        ## Sort the indices into disjoint sets by their valuation
        if (val == 0):
            S0 += [i]
        elif (val == 1):
            S1 += [i]
        elif (val >= 2):
            S2plus += [i]

    ## Check that S2 is non-empty and p^2 divides m to proceed, otherwise return no solutions.
    p2 = p * p
    if (S2plus == []) or (m % p2 != 0):
        return 0

    ## Check some conditions for no bad-type II solutions to exist
    if (NZvec is not None) and (len(Set(S2plus).intersection(Set(NZvec)))
                                == 0):
        return 0

    ## Check that the form is primitive...                     WHY IS THIS NECESSARY?
    if (S0 == []):
        print(" Using Q = " + str(self))
        print(" and p = " + str(p))
        raise RuntimeError("Oops! The form is not primitive!")

    ## DIAGNOSTIC
    verbose("\n Entering BII routine ")
    verbose(" S0 is " + str(S0))
    verbose(" S1 is " + str(S1))
    verbose(" S2plus is " + str(S2plus))
    verbose(" m = " + str(m) + "   p = " + str(p))

    ## Make the form Qnew for the reduction procedure:
    ## -----------------------------------------------
    Qnew = deepcopy(self)  ## TO DO:  DO THIS WITHOUT A copy(). =)
    for i in range(n):
        if i in S2plus:
            Qnew[i, i] = Qnew[i, i] / p2
            if (p == 2) and (i < n - 1):
                Qnew[i, i + 1] = Qnew[i, i + 1] / p2

    ## DIAGNOSTIC
    verbose("\n\n Check of Bad-type II reduction: \n")
    verbose(" Q is " + str(self))
    verbose(" Qnew is " + str(Qnew))

    ## Perform the reduction formula
    Zvec_geq_2 = list(Set([i for i in Zvec if i in S2plus]))
    if NZvec is None:
        NZvec_geq_2 = NZvec
    else:
        NZvec_geq_2 = list(Set([i for i in NZvec if i in S2plus]))

    return QQ(p**(len(S2plus) + 2 - n)) \
        * (Qnew.local_density_congruence(p, m / p2, Zvec_geq_2, NZvec_geq_2) \
        - Qnew.local_density_congruence(p, m / p2, S2plus , NZvec_geq_2))
예제 #29
0
def local_good_density_congruence(self, p, m, Zvec=None, NZvec=None):
    """
    Finds the Good-type local density of Q representing `m` at `p`.
    (Front end routine for parity specific routines for p.)

    TO DO: Add Documentation about the additional congruence
    conditions Zvec and NZvec.



    INPUT:

        Q -- quadratic form assumed to be block diagonal and p-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:

        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_good_density_congruence(2, 1, None, None)
        1
        sage: Q.local_good_density_congruence(3, 1, None, None)
        2/3

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_good_density_congruence(2, 1, None, None)
        1
        sage: Q.local_good_density_congruence(3, 1, None, None)
        8/9

    """
    ## DIAGNOSTIC
    verbose(" In local_good_density_congruence with ")
    verbose(" Q is: \n" + str(self))
    verbose(" p = " + str(p))
    verbose(" m = " + str(m))
    verbose(" Zvec = " + str(Zvec))
    verbose(" NZvec = " + str(NZvec))

    ## Put the Zvec congruence condition in a standard form
    if Zvec is None:
        Zvec = []


    n = self.dim()

    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec is not None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec is not None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")



    ## Check that Q is in local normal form -- should replace this with a diagonalization check?
    ##   (it often may not be since the reduction procedure
    ##   often mixes up the order of the valuations...)
    #
    #if (self != self.local_normal_form(p))
    #    print "Warning in local_good_density_congruence: Q is not in local normal form! \n";




    ## Decide which routine to use to compute the Good-type density
    if (p > 2):
        return self.local_good_density_congruence_odd(p, m, Zvec, NZvec)

    if (p == 2):
        return self.local_good_density_congruence_even(m, Zvec, NZvec)

    raise RuntimeError("\n Error in Local_Good_Density: The 'prime' p = " + str(p) + " is < 2. \n")
예제 #30
0
def pSelmerGroup(K, S, p, proof=None, debug=False):
    r"""
    Return the ``p``-Selmer group `K(S,p)` of the number field ``K``
    with respect to the prime ideals in ``S``

    INPUT:

    - ``K`` (number field) -- a number field, or `\QQ`.

    - ``S`` (list) -- a list of prime ideals in ``K``, or prime
      numbers when ``K`` is `\QQ`.

    - ``p`` (prime) -- a prime number.

    - ``proof`` - if True then compute the class group provably
      correctly. Default is True. Call :meth:`proof.number_field` to
      change this default globally.

    - ``debug`` (boolean, default ``False``) -- debug flag.

    OUTPUT:

    (tuple) ``KSp``, ``KSp_gens``, ``from_KSp``, ``to_KSp`` where

    - ``KSp`` is an abstract vector space over `GF(p)` isomorphic to `K(S,p)`;

    - ``KSp_gens`` is a list of elements of `K^*` generating `K(S,p)`;

    - ``from_KSp`` is a function from ``KSp`` to `K^*` implementing
      the isomorphism from the abstract `K(S,p)` to `K(S,p)` as a
      subgroup of `K^*/(K^*)^p`;

    - ``to_KSP`` is a partial function from `K^*` to ``KSp``, defined
      on elements `a` whose image in `K^*/(K^*)^p` lies in `K(S,p)`,
      mapping them via the inverse isomorphism to the abstract vector
      space ``KSp``.

    ALGORITHM:

    The list of generators of `K(S,p)` is the concatenation of three
    sublists, called ``alphalist``, ``betalist`` and ``ulist`` in the
    code.  Only ``alphalist`` depends on the primes in `S`.

    - ``ulist`` is a basis for `U/U^p` where `U` is the unit group.
      This is the list of fundamental units, including the generator
      of the group of roots of unity if its order is divisible by `p`.
      These have valuation `0` at all primes.

    - ``betalist`` is a list of the generators of the `p`'th powers of
      ideals which generate the `p`-torsion in the class group (so is
      empty if the class number is prime to `p`).  These have
      valuation divisible by `p` at all primes.

    - ``alphalist`` is a list of generators for each ideal `A` in a
      basis of those ideals supported on `S` (modulo `p`'th powers of
      ideals) which are `p`'th powers in the class group.  We find `B`
      such that `A/B^p` is principal and take a generator of it, for
      each `A` in a generating set.  As a special case, if all the
      ideals in `S` are principal then ``alphalist`` is a list of
      their generators.

    The map from the abstract space to `K^*` is easy: we just take the
    product of the generators to powers given by the coefficient
    vector.  No attempt is made to reduce the resulting product modulo
    `p`'th powers.

    The reverse map is more complicated.  Given `a\in K^*`:

    - write the principal ideal `(a)` in the form `AB^p` with `A`
      supported by `S` and `p`'th power free.  If this fails, then `a`
      does not represent an element of `K(S,p)` and an error is
      raised.

    - set `I_S` to be the group of ideals spanned by `S` mod `p`'th
      powers, and `I_{S,p}` the subgroup of `I_S` which maps to `0` in
      `C/C^p`.

    - Convert `A` to an element of `I_{S,p}`, hence find the
      coordinates of `a` with respect to the generators in
      ``alphalist``.

    - after dividing out by `A`, now `(a)=B^p` (with a different `a`
      and `B`).  Write the ideal class `[B]`, whose `p`'th power is
      trivial, in terms of the generators of `C[p]`; then `B=(b)B_1`,
      where the coefficients of `B_1` with respect to generators of
      `C[p]` give the coordinates of the result with respect to the
      generators in ``betalist``.

    - after dividing out by `B`, and by `b^p`, we now have `(a)=(1)`,
      so `a` is a unit, which can be expressed in terms of the unit
      generators.

    EXAMPLES:

    Over `\QQ` the the unit contribution is trivial unless `p=2` and
    the class group is trivial::

        sage: from sage.rings.number_field.selmer_group import pSelmerGroup
        sage: QS2, gens, fromQS2, toQS2 = pSelmerGroup(QQ, [2,3], 2)
        sage: QS2
        Vector space of dimension 3 over Finite Field of size 2
        sage: gens
        [2, 3, -1]
        sage: a = fromQS2([1,1,1]); a.factor()
        -1 * 2 * 3
        sage: toQS2(-6)
        (1, 1, 1)

        sage: QS3, gens, fromQS3, toQS3 = pSelmerGroup(QQ, [2,13], 3)
        sage: QS3
        Vector space of dimension 2 over Finite Field of size 3
        sage: gens
        [2, 13]
        sage: a = fromQS3([5,4]); a.factor()
        2^5 * 13^4
        sage: toQS3(a)
        (2, 1)
        sage: toQS3(a) == QS3([5,4])
        True

    A real quadratic field with class number 2, where the fundamental
    unit is a generator, and the class group provides another
    generator when `p=2`::

        sage: K.<a> = QuadraticField(-5)
        sage: K.class_number()
        2
        sage: P2 = K.ideal(2, -a+1)
        sage: P3 = K.ideal(3, a+1)
        sage: P5 = K.ideal(a)
        sage: KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, [P2, P3, P5], 2)
        sage: KS2
        Vector space of dimension 4 over Finite Field of size 2
        sage: gens
        [a + 1, a, 2, -1]

    Each generator must have even valuation at primes not in `S`::

        sage: [K.ideal(g).factor() for g in gens]
        [(Fractional ideal (2, a + 1)) * (Fractional ideal (3, a + 1)),
        Fractional ideal (-a),
        (Fractional ideal (2, a + 1))^2,
        1]

        sage: toKS2(10)
        (0, 0, 1, 1)
        sage: fromKS2([0,0,1,1])
        -2
        sage: K(10/(-2)).is_square()
        True

        sage: KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, [P2, P3, P5], 3)
        sage: KS3
        Vector space of dimension 3 over Finite Field of size 3
        sage: gens
        [1/2, 1/4*a + 1/4, a]

    The ``to`` and ``from`` maps are inverses of each other::

        sage: K.<a> = QuadraticField(-5)
        sage: S = K.ideal(30).support()
        sage: KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, S, 2)
        sage: KS2
        Vector space of dimension 5 over Finite Field of size 2
        sage: assert all(toKS2(fromKS2(v))==v for v in KS2)
        sage: KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, S, 3)
        sage: KS3
        Vector space of dimension 4 over Finite Field of size 3
        sage: assert all(toKS3(fromKS3(v))==v for v in KS3)
    """
    from sage.rings.number_field.number_field import proof_flag
    from sage.modules.free_module import VectorSpace
    from sage.sets.set import Set

    proof = proof_flag(proof)

    # Input check: p and all P in S must be prime.  Remove any repeats in S.

    S = list(Set(S))
    if not all(P.is_prime() for P in S):
        raise ValueError("elements of S must all be prime")
    if not p.is_prime():
        raise ValueError("p must be prime")

    F = GF(p)

    # Step 1. The unit contribution: all fundamental units, and also the
    # generating root of unity if its order is a multiple of p; we just
    # take generators of U/U^p.  These have valuation 0 everywhere.

    hK = 1 if K == QQ else K.class_number(proof=proof)
    C = K.class_group() if K == QQ else K.class_group(proof=proof)

    hKp = (hK%p == 0) # flag whether the class number is divisible by p

    if K == QQ:
        if p == 2:
            ulist = [QQ(-1)]
        else:
            ulist = []
    else:
        U = K.unit_group(proof=proof)
        ulist = U.gens_values()
        if U.zeta_order() % p:
            ulist = ulist[1:]

    if debug:
        print("{} generators in ulist = {}".format(len(ulist),ulist))

    # Step 2. The class group contribution: generators of the p'th
    # powers of ideals generating the p-torsion in the class group.
    # These have valuation divisible by p everywhere.

    if hKp:
        betalist = [_ideal_generator(c ** n)
                    for c, n in zip(C.gens_ideals(), C.gens_orders())
                    if n % p == 0]
    else:
        betalist = []

    if debug:
        print("{} generators in betalist = {}".format(len(betalist),betalist))

    # Step 3. The part depending on S: one generator for each ideal A
    # in a basis of those ideals supported on S (modulo p'th powers of
    # ideals) which is a p'th power in the class group.  We find B
    # such that A/B^p is principal and take a generator of that, for
    # each A in a generating set.

    # As a special case, when the class number is 1 we just take
    # generators of the primes in S.

    if hK > 1:
        T, f = basis_for_p_cokernel(S, C, p)
        alphalist = [_ideal_generator(I / _root_ideal(I, C, p) ** p) for I in T]
    else:
        f = lambda x:x
        alphalist = [_ideal_generator(P) for P in S]

    if debug:
        print("{} generators in alphalist = {}".format(len(alphalist), alphalist))

    # Now we have the generators of K(S,p), and define K(S,p) as an
    # abstract vector space:

    KSp_gens = alphalist + betalist + ulist
    KSp = VectorSpace(GF(p), len(KSp_gens))

    if debug:
        print("Generators of K(S,p) = {} (dimension {})".format(KSp_gens, len(KSp_gens)))

    # Now we define maps in each direction between the abstract space and K^*.

    # Define the easy map from KSp into K^*:

    def from_KSp(v):
        return prod([g ** vi for g, vi in zip(KSp_gens, v)], K(1))

    # Define the hard map from (a subgroup of) K^* to KSp:

    def to_KSp(a):
        # Check that a is in K(S,p):

        if not a:
            raise ValueError("argument {} should be nonzero".format(a))
        try:
            a = K(a)
        except ValueError:
            raise ValueError("argument {} should be in {}".format(a, K))

        if not all(P in S or a.valuation(P) % p == 0 for P in a.support()):
            raise ValueError("argument {} should have valuations divisible by {} at all primes in {}".format(a, p, S))

        # 1. (a) is a p'th power mod ideals in S, say (a)=AB^p, where
        # A is supported on S and is a linear combination of the
        # ideals T above.  Find the exponents of the P_i in S in A:

        S_vals = [F(a.valuation(P)) for P in S]
        avec = list(f(S_vals)) # coordinates of A w.r.t ideals in T (mod p'th powers)
        a1 = prod((alpha ** e for alpha, e in zip(alphalist,avec)), K(1))
        a /= a1
        if debug:
            print("alpha component is {} with coords {}".format(a1,avec))
            if K == QQ:
                print("continuing with quotient {} whose ideal should be a {}'th power: {}".format(a,p,a.factor()))
            else:
                print("continuing with quotient {} whose ideal should be a {}'th power: {}".format(a,p,K.ideal(a).factor()))

        # 2. Now (a) is a p'th power, say (a)=B^p.
        # Find B and the exponents of [B] w.r.t. basis of C[p]:

        supp = a.support()
        vals = [a.valuation(P) for P in supp]
        if debug:
            assert all(v % p == 0 for v in vals)
        one = K(1)    if K == QQ else K.ideal(1)
        aa  = a.abs() if K == QQ else K.ideal(a)
        B = prod((P ** (v // p) for P, v in zip(supp,vals)), one)
        if debug:
            assert B ** p == aa
            print("B={}".format(B))
            print("a={}".format(a))

        if hKp:
            bvec = _coords_in_C_p(B, C, p)
            a2 = prod((beta ** e for beta, e in zip(betalist, bvec)), K(1))
            a /= a2
            supp = a.support()
            vals = [a.valuation(P) for P in supp]
            if debug:
                assert all(v % p == 0 for v in vals)
            B = prod((P ** (v // p) for P, v in zip(supp, vals)), one)
            if debug:
                assert B ** p == aa
        else:
            bvec = []
            a2 = 1

        if debug:
            print("beta component is {} with coords {}".format(a2,bvec))
            print("continuing with quotient {} which should be a p'th power times a unit".format(a))

        # 3. Now (a) = (c)^p for some c, so a/c^p is a unit

        if K != QQ:
            assert B.is_principal()

        if debug:
            print("B={}".format(B))
        a3 = B if K==QQ else _ideal_generator(B)
        if debug:
            print("a3={}".format(a3))
        a /= a3 ** p
        if debug:
            print("dividing by {}th power of {}".format(p,a3))
            print("continuing with quotient {} which should be a unit".format(a))

        #4. Now a is a unit

        # NB not a.is_unit which is true for all a in K^*.  One could
        # also test K.ring_of_integers()(a).is_unit().

        if debug:
            if K == QQ:
                assert a.abs()==1
            else:
                assert K.ideal(a).is_one()

        if K == QQ:
            if p == 2:
                cvec = [1] if a == -1 else [0]
            else:
                cvec = []
        else:
            cvec = coords_in_U_mod_p(a,U,p)

        if debug:
            print("gamma component has coords {}".format(cvec))

        return KSp(avec + bvec + cvec)

    return KSp, KSp_gens, from_KSp, to_KSp