def short_primitive_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False):
    """
    Return a list of lists of short primitive vectors `v`, sorted by length, with
    Q(`v`) < len_bound.  The list in output `[i]` indexes all vectors of
    length `i`.  If the up_to_sign_flag is set to True, then only one of
    the vectors of the pair `[v, -v]` is listed.

    Note:  This processes the PARI/GP output to always give elements of type `ZZ`.

    OUTPUT:
        a list of lists of vectors.

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Q.short_vector_list_up_to_length(5, True)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0)],
         [],
         [(0, 1, 0, 0)],
         [(1, 1, 0, 0), (-1, 1, 0, 0), (2, 0, 0, 0)]]
        sage: Q.short_primitive_vector_list_up_to_length(5, True)
        [[], [(1, 0, 0, 0)], [], [(0, 1, 0, 0)], [(1, 1, 0, 0), (-1, 1, 0, 0)]]


    """
    ## Get a list of short vectors
    full_vec_list = self.short_vector_list_up_to_length(len_bound, up_to_sign_flag)

    ## Make a new list of the primitive vectors
    prim_vec_list = [[v  for v in L  if GCD(list(v)) == 1]   for L in full_vec_list]                 ## TO DO:  Arrange for GCD to take a vector argument!

    ## Return the list of primitive vectors
    return prim_vec_list
Ejemplo n.º 2
0
def conv(N):
    file = "data/%s" % N
    if not os.path.exists(file):
        raise RuntimeError, "Data for level %s does not exist." % N

    F = open(file).read()
    i = F.find(":=")
    if i == -1:
        raise RuntimeError, "Syntax error in file for level %s." % N
    F = F[i + 2:]
    TRANS = [("[*", "["), ("*]", "]"), ("<","["), (">","]"), \
             (";",""), ("\n",""), ("^","**")]
    for z, w in TRANS:
        F = F.replace(z, w)
    X = []
    # Define x so the eval below works.
    R = PolynomialRing(RationalField())
    x = R.gen()
    print "starting eval."
    #print "F = ", F
    for f in eval(F):
        print "creating object from f=", f[:4]
        cp = {}
        disc = 0
        for z in f[5]:
            g = R(z[1])
            disc = GCD(disc, g.discriminant())
            cp[z[0]] = g
        X.append(ModularForm(f[0], f[1], f[2], f[3], f[4], cp, disc))
    return X
Ejemplo n.º 3
0
 def torsion_multiple(self):
     """
     Returns a multiple of the order of the torsion subgroup of the
     corresponding abelian variety.
     """
     T = 0
     for p in self.charpolys.keys():
         if self.N % p != 0:
             f = self.charpolys[p]
             T = GCD(T, long(f(p + 1)))
     return T
Ejemplo n.º 4
0
    def congruence_multiple(self, f, anemic=True):
        """
        Return an integer C such that any prime of congruence between
        this modular form and f is a divisor of C.

        If anemic=True (the default), include only coefficients a_p
        with p coprime to the levels of self and f.

        This function returns the gcd of the resultants of each of the
        charpolys of coefficients of self and f that are both known.
        """
        C = 0
        N = self.N * f.N
        for p in self.charpolys.keys():
            if p in f.charpolys.keys():
                if (not anemic) or (anemic and N % p != 0):
                    ap = self.charpolys[p]
                    bp = f.charpolys[p]
                    C = GCD(C, ap.resultant(bp))
        return C
    def gcd(self):
        """
        Returns the greatest common divisor of the coefficients of the
        quadratic form (as a polynomial).

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 4, range(1, 21, 2))
            sage: Q.gcd()
            1

        ::

            sage: Q = QuadraticForm(ZZ, 4, range(0, 20, 2))
            sage: Q.gcd()
            2
        """
        if self.base_ring() != ZZ:
            raise TypeError("Oops! The given quadratic form must be defined over ZZ.")

        return GCD(self.coefficients())
Ejemplo n.º 6
0
    def order(self):
        """
        Return the order of this element.

        OUTPUT:

        An integer or ``infinity``.

        EXAMPLES::

            sage: F = AbelianGroup(3,[7,8,9])
            sage: Fd = F.dual_group()
            sage: A,B,C = Fd.gens()
            sage: (B*C).order()
            72

            sage: F = AbelianGroup(3,[7,8,9]); F
            Multiplicative Abelian group isomorphic to C7 x C8 x C9
            sage: F.gens()[2].order()
            9
            sage: a,b,c = F.gens()
            sage: (b*c).order()
            72
            sage: G = AbelianGroup(3,[7,8,9])
            sage: type((G.0 * G.1).order())==Integer
            True
        """
        M = self.parent()
        order = M.gens_orders()
        L = self.exponents()
        N = LCM([
            order[i] / GCD(order[i], L[i]) for i in range(len(order))
            if L[i] != 0
        ])
        if N == 0:
            return infinity
        else:
            return ZZ(N)
Ejemplo n.º 7
0
 def order(self):
     """
     Returns the (finite) order of this element or Infinity if this
     element does not have finite order.
     
     EXAMPLES::
     
         sage: F = AbelianGroup(3,[7,8,9]); F
         Multiplicative Abelian Group isomorphic to C7 x C8 x C9
         sage: F.gens()[2].order()
         9
         sage: a,b,c = F.gens()
         sage: (b*c).order()
         72
         sage: G = AbelianGroup(3,[7,8,9])
         sage: type((G.0 * G.1).order())==Integer
         True
     """
     M = self.parent()
     if self == M(1):
         return Integer(1)
     invs = M.invariants()
     if self in M.gens():
         o = invs[list(M.gens()).index(self)]
         if o == 0:
             return infinity
         return o
     L = list(self.list())
     N = LCM([
         invs[i] // GCD(invs[i], L[i]) for i in range(len(invs))
         if L[i] != 0
     ])
     if N == 0:
         return infinity
     else:
         return Integer(N)
Ejemplo n.º 8
0
def CyclicCodeFromGeneratingPolynomial(n,g,ignore=True):
    r"""
    If g is a polynomial over GF(q) which divides `x^n-1` then
    this constructs the code "generated by g" (ie, the code associated
    with the principle ideal `gR` in the ring
    `R = GF(q)[x]/(x^n-1)` in the usual way).

    The option "ignore" says to ignore the condition that (a) the
    characteristic of the base field does not divide the length (the
    usual assumption in the theory of cyclic codes), and (b) `g`
    must divide `x^n-1`. If ignore=True, instead of returning
    an error, a code generated by `gcd(x^n-1,g)` is created.

    EXAMPLES::

        sage: P.<x> = PolynomialRing(GF(3),"x")
        sage: g = x-1
        sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C
        Linear code of length 4, dimension 3 over Finite Field of size 3
        sage: P.<x> = PolynomialRing(GF(4,"a"),"x")
        sage: g = x^3+1
        sage: C = codes.CyclicCodeFromGeneratingPolynomial(9,g); C
        Linear code of length 9, dimension 6 over Finite Field in a of size 2^2
        sage: P.<x> = PolynomialRing(GF(2),"x")
        sage: g = x^3+x+1
        sage: C = codes.CyclicCodeFromGeneratingPolynomial(7,g); C
        Linear code of length 7, dimension 4 over Finite Field of size 2
        sage: C.gen_mat()
        [1 1 0 1 0 0 0]
        [0 1 1 0 1 0 0]
        [0 0 1 1 0 1 0]
        [0 0 0 1 1 0 1]
        sage: g = x+1
        sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C
        Linear code of length 4, dimension 3 over Finite Field of size 2
        sage: C.gen_mat()
        [1 1 0 0]
        [0 1 1 0]
        [0 0 1 1]

    On the other hand, CyclicCodeFromPolynomial(4,x) will produce a
    ValueError including a traceback error message: "`x` must
    divide `x^4 - 1`". You will also get a ValueError if you
    type

    ::

        sage: P.<x> = PolynomialRing(GF(4,"a"),"x")
        sage: g = x^2+1

    followed by CyclicCodeFromGeneratingPolynomial(6,g). You will also
    get a ValueError if you type

    ::

        sage: P.<x> = PolynomialRing(GF(3),"x")
        sage: g = x^2-1
        sage: C = codes.CyclicCodeFromGeneratingPolynomial(5,g); C
        Linear code of length 5, dimension 4 over Finite Field of size 3

    followed by C = CyclicCodeFromGeneratingPolynomial(5,g,False), with
    a traceback message including "`x^2 + 2` must divide
    `x^5 - 1`".
    """
    P = g.parent()
    x = P.gen()
    F = g.base_ring()
    p = F.characteristic()
    if not(ignore) and p.divides(n):
        raise ValueError, 'The characteristic %s must not divide %s'%(p,n)
    if not(ignore) and not(g.divides(x**n-1)):
        raise ValueError, '%s must divide x^%s - 1'%(g,n)
    gn = GCD([g,x**n-1])
    d = gn.degree()
    coeffs = Sequence(gn.list())
    r1 = Sequence(coeffs+[0]*(n - d - 1))
    Sn = SymmetricGroup(n)
    s = Sn.gens()[0] # assumes 1st gen of S_n is (1,2,...,n)
    rows = [permutation_action(s**(-i),r1) for i in range(n-d)]
    MS = MatrixSpace(F,n-d,n)
    return LinearCode(MS(rows))
Ejemplo n.º 9
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
Ejemplo n.º 10
0
def has_equivalent_Jordan_decomposition_at_prime(self, other, p):
    """
    Determines if the given quadratic form has a Jordan decomposition
    equivalent to that of self.

    INPUT:
        a QuadraticForm

    OUTPUT:
        boolean

    EXAMPLES::

        sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 1, 0, 3])
        sage: Q2 = QuadraticForm(ZZ, 3, [1, 0, 0, 2, -2, 6])
        sage: Q3 = QuadraticForm(ZZ, 3, [1, 0, 0, 1, 0, 11])
        sage: [Q1.level(), Q2.level(), Q3.level()]
        [44, 44, 44]
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,2)
        False
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,11)
        False
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,2)
        False
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,11)
        True
        sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,2)
        True
        sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,11)
        False

    """
    ## Sanity Checks
    #if not isinstance(other, QuadraticForm):
    if not isinstance(other, type(self)):
        raise TypeError(
            "Oops!  The first argument must be of type QuadraticForm.")
    if not is_prime(p):
        raise TypeError("Oops!  The second argument must be a prime number.")

    ## Get the relevant local normal forms quickly
    self_jordan = self.jordan_blocks_by_scale_and_unimodular(p,
                                                             safe_flag=False)
    other_jordan = other.jordan_blocks_by_scale_and_unimodular(p,
                                                               safe_flag=False)

    ## DIAGNOSTIC
    #print "self_jordan = ", self_jordan
    #print "other_jordan = ", other_jordan

    ## Check for the same number of Jordan components
    if len(self_jordan) != len(other_jordan):
        return False

    ## Deal with odd primes:  Check that the Jordan component scales, dimensions, and discriminants are the same
    if p != 2:
        for i in range(len(self_jordan)):
            if (self_jordan[i][0] != other_jordan[i][0]) \
               or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \
               or (legendre_symbol(self_jordan[i][1].det() * other_jordan[i][1].det(), p) != 1):
                return False

        ## All tests passed for an odd prime.
        return True

    ## For p = 2:  Check that all Jordan Invariants are the same.
    elif p == 2:

        ## Useful definition
        t = len(self_jordan)  ## Define t = Number of Jordan components

        ## Check that all Jordan Invariants are the same (scale, dim, and norm)
        for i in range(t):
            if (self_jordan[i][0] != other_jordan[i][0]) \
               or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \
               or (valuation(GCD(self_jordan[i][1].coefficients()), p) != valuation(GCD(other_jordan[i][1].coefficients()), p)):
                return False

        ## DIAGNOSTIC
        #print "Passed the Jordan invariant test."

        ## Use O'Meara's isometry test 93:29 on p277.
        ## ------------------------------------------

        ## List of norms, scales, and dimensions for each i
        scale_list = [ZZ(2)**self_jordan[i][0] for i in range(t)]
        norm_list = [
            ZZ(2)**(self_jordan[i][0] +
                    valuation(GCD(self_jordan[i][1].coefficients()), 2))
            for i in range(t)
        ]
        dim_list = [(self_jordan[i][1].dim()) for i in range(t)]

        ## List of Hessian determinants and Hasse invariants for each Jordan (sub)chain
        ## (Note: This is not the same as O'Meara's Gram determinants, but ratios are the same!)  -- NOT SO GOOD...
        ## But it matters in condition (ii), so we multiply all by 2 (instead of dividing by 2 since only square-factors matter, and it's easier.)
        j = 0
        self_chain_det_list = [
            self_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])
        ]
        other_chain_det_list = [
            other_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])
        ]
        self_hasse_chain_list = [
            self_jordan[j][1].scale_by_factor(
                ZZ(2)**self_jordan[j][0]).hasse_invariant__OMeara(2)
        ]
        other_hasse_chain_list = [
            other_jordan[j][1].scale_by_factor(
                ZZ(2)**other_jordan[j][0]).hasse_invariant__OMeara(2)
        ]

        for j in range(1, t):
            self_chain_det_list.append(self_chain_det_list[j - 1] *
                                       self_jordan[j][1].Gram_det() *
                                       (scale_list[j]**dim_list[j]))
            other_chain_det_list.append(other_chain_det_list[j - 1] *
                                        other_jordan[j][1].Gram_det() *
                                        (scale_list[j]**dim_list[j]))
            self_hasse_chain_list.append(self_hasse_chain_list[j-1] \
                                         * hilbert_symbol(self_chain_det_list[j-1], self_jordan[j][1].Gram_det(), 2) \
                                         * self_jordan[j][1].hasse_invariant__OMeara(2))
            other_hasse_chain_list.append(other_hasse_chain_list[j-1] \
                                          * hilbert_symbol(other_chain_det_list[j-1], other_jordan[j][1].Gram_det(), 2) \
                                          * other_jordan[j][1].hasse_invariant__OMeara(2))

        ## SANITY CHECK -- check that the scale powers are strictly increasing
        for i in range(1, len(scale_list)):
            if scale_list[i - 1] >= scale_list[i]:
                raise RuntimeError(
                    "Oops!  There is something wrong with the Jordan Decomposition -- the given scales are not strictly increasing!"
                )

        ## DIAGNOSTIC
        #print "scale_list = ", scale_list
        #print "norm_list = ", norm_list
        #print "dim_list = ", dim_list
        #print
        #print "self_chain_det_list = ", self_chain_det_list
        #print "other_chain_det_list = ", other_chain_det_list
        #print "self_hasse_chain_list = ", self_hasse_chain_list
        #print "other_hasse_chain_det_list = ", other_hasse_chain_list

        ## Test O'Meara's two conditions
        for i in range(t - 1):

            ## Condition (i): Check that their (unit) ratio is a square (but it suffices to check at most mod 8).
            modulus = norm_list[i] * norm_list[i + 1] / (scale_list[i]**2)
            if modulus > 8:
                modulus = 8
            if (modulus > 1) and ((
                (self_chain_det_list[i] / other_chain_det_list[i]) % modulus)
                                  != 1):
                #print "Failed when i =", i, " in condition 1."
                return False

            ## Check O'Meara's condition (ii) when appropriate
            if norm_list[i + 1] % (4 * norm_list[i]) == 0:
                if self_hasse_chain_list[i] * hilbert_symbol(norm_list[i] * other_chain_det_list[i], -self_chain_det_list[i], 2) \
                       != other_hasse_chain_list[i] * hilbert_symbol(norm_list[i], -other_chain_det_list[i], 2):      ## Nipp conditions
                    #print "Failed when i =", i, " in condition 2."
                    return False

        ## All tests passed for the prime 2.
        return True

    else:
        raise TypeError("Oops!  This should not have happened.")
def local_normal_form(self, p):
    """
    Returns the a locally integrally equivalent quadratic form over
    the p-adic integers Z_p which gives the Jordan decomposition.  The
    Jordan components are written as sums of blocks of size <= 2 and
    are arranged by increasing scale, and then by increasing norm.
    (This is equivalent to saying that we put the 1x1 blocks before
    the 2x2 blocks in each Jordan component.)

    INPUT:

        `p` -- a positive prime number.

    OUTPUT:

        a quadratic form over ZZ

    WARNING:  Currently this only works for quadratic forms defined over ZZ.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ, 2, [10,4,1])
        sage: Q.local_normal_form(5)
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 1 0 ]
        [ * 6 ]

    ::

        sage: Q.local_normal_form(3)
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 10 0 ]
        [ * 15 ]

        sage: Q.local_normal_form(2)
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 1 0 ]
        [ * 6 ]

    """
    ## Sanity Checks
    if (self.base_ring() != IntegerRing()):
        raise NotImplementedError(
            "Oops!  This currently only works for quadratic forms defined over IntegerRing(). =("
        )
    if not ((p >= 2) and is_prime(p)):
        raise TypeError("Oops!  p is not a positive prime number. =(")

    ## Some useful local variables
    Q = copy.deepcopy(self)
    Q.__init__(self.base_ring(), self.dim(), self.coefficients())

    ## Prepare the final form to return
    Q_Jordan = copy.deepcopy(self)
    Q_Jordan.__init__(self.base_ring(), 0)

    while Q.dim() > 0:
        n = Q.dim()

        ## Step 1: Find the minimally p-divisible matrix entry, preferring diagonals
        ## -------------------------------------------------------------------------
        (min_i, min_j) = Q.find_entry_with_minimal_scale_at_prime(p)
        if min_i == min_j:
            min_val = valuation(2 * Q[min_i, min_j], p)
        else:
            min_val = valuation(Q[min_i, min_j], p)

        ## Error if we still haven't seen non-zero coefficients!
        if (min_val == Infinity):
            raise RuntimeError("Oops!  The original matrix is degenerate. =(")

        ## Step 2: Arrange for the upper leftmost entry to have minimal valuation
        ## ----------------------------------------------------------------------
        if (min_i == min_j):
            block_size = 1
            Q.swap_variables(0, min_i, in_place=True)
        else:
            ## Work in the upper-left 2x2 block, and replace it by its 2-adic equivalent form
            Q.swap_variables(0, min_i, in_place=True)
            Q.swap_variables(1, min_j, in_place=True)

            ## 1x1 => make upper left the smallest
            if (p != 2):
                block_size = 1
                Q.add_symmetric(1, 0, 1, in_place=True)
            ## 2x2 => replace it with the appropriate 2x2 matrix
            else:
                block_size = 2

        ## DIAGNOSTIC
        #print "\n Finished Step 2 \n";
        #print "\n Q is: \n" + str(Q)  + "\n";
        #print "  p is: " + str(p)
        #print "  min_val is: " + str( min_val)
        #print "  block_size is: " + str(block_size)
        #print "\n Starting Step 3 \n"

        ## Step 3: Clear out the remaining entries
        ##  ---------------------------------------
        min_scale = p**min_val  ## This is the minimal valuation of the Hessian matrix entries.

        ##DIAGNOSTIC
        #print "Starting Step 3:"
        #print "----------------"
        #print "  min_scale is: " + str(min_scale)

        ## Perform cancellation over Z by ensuring divisibility
        if (block_size == 1):
            a = 2 * Q[0, 0]
            for j in range(block_size, n):
                b = Q[0, j]
                g = GCD(a, b)

                ## DIAGNSOTIC
                #print "Cancelling from a 1x1 block:"
                #print "----------------------------"
                #print "  Cancelling entry with index (" + str(upper_left) + ", " + str(j) + ")"
                #print "  entry = " + str(b)
                #print "  gcd = " + str(g)
                #print "  a = " + str(a)
                #print "  b = " + str(b)
                #print "  a/g = " + str(a/g) + "   (used for stretching)"
                #print "  -b/g = " + str(-b/g) + "   (used for cancelling)"

                ## Sanity Check:  a/g is a p-unit
                if valuation(g, p) != valuation(a, p):
                    raise RuntimeError(
                        "Oops!  We have a problem with our rescaling not preserving p-integrality!"
                    )

                Q.multiply_variable(
                    ZZ(a / g), j, in_place=True
                )  ## Ensures that the new b entry is divisible by a
                Q.add_symmetric(ZZ(-b / g), j, 0,
                                in_place=True)  ## Performs the cancellation

        elif (block_size == 2):
            a1 = 2 * Q[0, 0]
            a2 = Q[0, 1]
            b1 = Q[1, 0]  ## This is the same as a2
            b2 = 2 * Q[1, 1]

            big_det = (a1 * b2 - a2 * b1)
            small_det = big_det / (min_scale * min_scale)

            ## Cancels out the rows/columns of the 2x2 block
            for j in range(block_size, n):
                a = Q[0, j]
                b = Q[1, j]

                ## Ensures an integral result (scale jth row/column by big_det)
                Q.multiply_variable(big_det, j, in_place=True)

                ## Performs the cancellation (by producing -big_det * jth row/column)
                Q.add_symmetric(ZZ(-(a * b2 - b * a2)), j, 0, in_place=True)
                Q.add_symmetric(ZZ(-(-a * b1 + b * a1)), j, 1, in_place=True)

                ## Now remove the extra factor (non p-unit factor) in big_det we introduced above
                Q.divide_variable(ZZ(min_scale * min_scale), j, in_place=True)

            ## DIAGNOSTIC
            #print "Cancelling out a 2x2 block:"
            #print "---------------------------"
            #print "  a1 = " + str(a1)
            #print "  a2 = " + str(a2)
            #print "  b1 = " + str(b1)
            #print "  b2 = " + str(b2)
            #print "  big_det = " + str(big_det)
            #print "  min_scale = " + str(min_scale)
            #print "  small_det = " + str(small_det)
            #print "  Q = \n", Q

            ## Uses Cassels's proof to replace the remaining 2 x 2 block
            if (((1 + small_det) % 8) == 0):
                Q[0, 0] = 0
                Q[1, 1] = 0
                Q[0, 1] = min_scale
            elif (((5 + small_det) % 8) == 0):
                Q[0, 0] = min_scale
                Q[1, 1] = min_scale
                Q[0, 1] = min_scale
            else:
                raise RuntimeError(
                    "Error in LocalNormal: Impossible behavior for a 2x2 block! \n"
                )

        ## Check that the cancellation worked, extract the upper-left block, and trim Q to handle the next block.
        for i in range(block_size):
            for j in range(block_size, n):
                if Q[i, j] != 0:
                    raise RuntimeError(
                        "Oops!  The cancellation didn't work properly at entry ("
                        + str(i) + ", " + str(j) + ").")
        Q_Jordan = Q_Jordan + Q.extract_variables(range(block_size))
        Q = Q.extract_variables(range(block_size, n))

    return Q_Jordan
Ejemplo n.º 12
0
def is_a_splitting(S1, S2, n, return_automorphism=False):
    """
    Check wether ``(S1,S2)`` is a splitting of `\ZZ/n\ZZ`.

    A splitting of `R = \ZZ/n\ZZ` is a pair of subsets of `R` which is a
    partition of `R \\backslash \{0\}` and such that there exists an element `r`
    of `R` such that `r S_1 = S_2` and `r S_2 = S_1` (where `r S` is the
    point-wise multiplication of the elements of `S` by `r`).

    Splittings are useful for computing idempotents in the quotient
    ring `Q = GF(q)[x]/(x^n-1)`.

    INPUT:

    - ``S1, S2`` -- disjoint sublists partitioning ``[1, 2, ..., n-1]``

    - ``n`` (integer)

    - ``return_automorphism`` (boolean) -- whether to return the automorphism
      exchanging `S_1` and `S_2`.

    OUTPUT:

    If ``return_automorphism is False`` (default) the function returns boolean values.

    Otherwise, it returns a pair ``(b, r)`` where ``b`` is a boolean indicating
    whether `S1`, `S2` is a splitting of `n`, and `r` is such that `r S_1 = S_2`
    and `r S_2 = S_1` (if `b` is ``False``, `r` is equal to ``None``).

    EXAMPLES::

        sage: from sage.coding.code_constructions import is_a_splitting
        sage: is_a_splitting([1,2],[3,4],5)
        True
        sage: is_a_splitting([1,2],[3,4],5,return_automorphism=True)
        (True, 4)

        sage: is_a_splitting([1,3],[2,4,5,6],7)
        False
        sage: is_a_splitting([1,3,4],[2,5,6],7)
        False

        sage: for P in SetPartitions(6,[3,3]):
        ....:     res,aut= is_a_splitting(P[0],P[1],7,return_automorphism=True)
        ....:     if res:
        ....:         print aut, P[0], P[1]
        6 {1, 2, 3} {4, 5, 6}
        3 {1, 2, 4} {3, 5, 6}
        6 {1, 3, 5} {2, 4, 6}
        6 {1, 4, 5} {2, 3, 6}

    We illustrate now how to find idempotents in quotient rings::

        sage: n = 11; q = 3
        sage: C = Zmod(n).cyclotomic_cosets(q); 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
        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 = codes.CyclicCodeFromGeneratingPolynomial(n,i1)
        sage: C2 = codes.CyclicCodeFromGeneratingPolynomial(n,1-i2)
        sage: C1.dual_code() == C2
        True

    This is a special case of Theorem 6.4.3 in [HP]_.
    """
    R = IntegerModRing(n)
    S1 = set(R(x) for x in S1)
    S2 = set(R(x) for x in S2)

    # we first check whether (S1,S2) is a partition of R - {0}
    if (len(S1) + len(S2) != n - 1 or len(S1) != len(S2) or R.zero() in S1
            or R.zero() in S2 or not S1.isdisjoint(S2)):
        if return_automorphism:
            return False, None
        else:
            return False

    # now that we know that (S1,S2) is a partition, we look for an invertible
    # element b that maps S1 to S2 by multiplication
    for b in range(2, n):
        if GCD(b, n) == 1 and all(b * x in S2 for x in S1):
            if return_automorphism:
                return True, b
            else:
                return True
    if return_automorphism:
        return False, None
    else:
        return False
def complementary_subform_to_vector(self, v):
    """
    Finds the `(n-1)`-dim'l quadratic form orthogonal to the vector `v`.

    Note: This is usually not a direct summand!

    Technical Notes: There is a minor difference in the cancellation
    code here (form the C++ version) since the notation Q `[i,j]` indexes
    coefficients of the quadratic polynomial here, not the symmetric
    matrix.  Also, it produces a better splitting now, for the full
    lattice (as opposed to a sublattice in the C++ code) since we
    now extend `v` to a unimodular matrix.

    INPUT:
        `v` -- a list of self.dim() integers

    OUTPUT:
        a QuadraticForm over `ZZ`


    EXAMPLES::

        sage: Q1 = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Q1.complementary_subform_to_vector([1,0,0,0])
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 3 0 0 ]
        [ * 5 0 ]
        [ * * 7 ]

    ::

        sage: Q1.complementary_subform_to_vector([1,1,0,0])
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 12 0 0 ]
        [ * 5 0 ]
        [ * * 7 ]

    ::

        sage: Q1.complementary_subform_to_vector([1,1,1,1])
        Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 624 -480 -672 ]
        [ * 880 -1120 ]
        [ * * 1008 ]

    """
    n = self.dim()

    ## Copy the quadratic form
    Q = deepcopy(self)

    ## Find the first non-zero component of v, and call it nz  (Note: 0 <= nz < n)
    nz = 0
    while (nz < n) and (v[nz] == 0):
        nz += 1

    ## Abort if v is the zero vector
    if nz == n:
        raise TypeError, "Oops, v cannot be the zero vector! =("

    ## Make the change of basis matrix
    new_basis = extend_to_primitive(matrix(ZZ, n, 1, v))

    ## Change Q (to Q1) to have v as its nz-th basis vector
    Q1 = Q(new_basis)

    ## Pick out the value Q(v) of the vector
    d = Q1[0, 0]

    #print Q1

    ## For each row/column, perform elementary operations to cancel them out.
    for i in range(1, n):

        ## Check if the (i,0)-entry is divisible by d,
        ## and stretch its row/column if not.
        if Q1[i, 0] % d != 0:
            Q1 = Q1.multiply_variable(d / GCD(d, Q1[i, 0] // 2), i)

        #print "After scaling at i =", i
        #print Q1

        ## Now perform the (symmetric) elementary operations to cancel out the (i,0) entries/
        Q1 = Q1.add_symmetric(-(Q1[i, 0] / 2) / (GCD(d, Q1[i, 0] // 2)), i, 0)

        #print "After cancelling at i =", i
        #print Q1

    ## Check that we're done!
    done_flag = True
    for i in range(1, n):
        if Q1[0, i] != 0:
            done_flag = False

    if done_flag == False:
        raise RuntimeError, "There is a problem cancelling out the matrix entries! =O"

    ## Return the complementary matrix
    return Q1.extract_variables(range(1, n))