def basis_of_short_vectors(self, show_lengths=False, safe_flag=True):
    """
    Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`).

    The safe_flag allows us to select whether we want a copy of the
    output, or the original output.  By default safe_flag = True, so
    we return a copy of the cached information.  If this is set to
    False, then the routine is much faster but the return values are
    vulnerable to being corrupted by the user.

    OUTPUT:
        a list of vectors, and optionally a list of values for each vector.

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Q.basis_of_short_vectors()
        [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)]
        sage: Q.basis_of_short_vectors(True)
        ([(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)], [1, 3, 5, 7])

    """
    ## Try to use the cached results
    try:
        ## Return the appropriate result
        if show_lengths:
            if safe_flag:
                return deep_copy(self.__basis_of_short_vectors), deepcopy(self.__basis_of_short_vectors_lengths)
            else:
                return self.__basis_of_short_vectors, self.__basis_of_short_vectors_lengths
        else:
            if safe_flag:
                return deepcopy(self.__basis_of_short_vectors)
            else:
                return deepcopy(self.__basis_of_short_vectors)
    except Exception:
        pass


    ## Set an upper bound for the number of vectors to consider
    Max_number_of_vectors = 10000

    ## Generate a PARI matrix string for the associated Hessian matrix
    M_str = str(gp(self.matrix()))


    ## Run through all possible minimal lengths to find a spanning set of vectors
    n = self.dim()
    #MS = MatrixSpace(QQ, n)
    M1 = Matrix([[0]])
    vec_len = 0
    while M1.rank() < n:

        ## DIAGONSTIC
        #print
        #print "Starting with vec_len = ", vec_len
        #print "M_str = ", M_str

        vec_len += 1
        gp_mat = gp.qfminim(M_str, vec_len, Max_number_of_vectors)[3].mattranspose()
        number_of_vecs = ZZ(gp_mat.matsize()[1])
        vector_list = []
        for i in range(number_of_vecs):
            #print "List at", i, ":", list(gp_mat[i+1,])
            new_vec = vector([ZZ(x)  for x in list(gp_mat[i+1,])])
            vector_list.append(new_vec)


        ## DIAGNOSTIC
        #print "number_of_vecs = ", number_of_vecs
        #print "vector_list = ", vector_list


        ## Make a matrix from the short vectors
        if len(vector_list) > 0:
            M1 = Matrix(vector_list)


        ## DIAGNOSTIC
        #print "matrix of vectors = \n", M1
        #print "rank of the matrix = ", M1.rank()



    #print " vec_len = ", vec_len
    #print M1


    ## Organize these vectors by length (and also introduce their negatives)
    max_len = vec_len // 2
    vector_list_by_length = [[]  for _ in range(max_len + 1)]
    for v in vector_list:
        l = self(v)
        vector_list_by_length[l].append(v)
        vector_list_by_length[l].append(vector([-x  for x in v]))


    ## Make a matrix from the column vectors (in order of ascending length).
    sorted_list = []
    for i in range(len(vector_list_by_length)):
        for v in vector_list_by_length[i]:
            sorted_list.append(v)
    sorted_matrix = Matrix(sorted_list).transpose()


    ## Determine a basis of vectors of minimal length
    pivots = sorted_matrix.pivots()
    basis = [sorted_matrix.column(i) for i in pivots]
    pivot_lengths = [self(v)  for v in basis]


    ## DIAGNOSTIC
    #print "basis = ", basis
    #print "pivot_lengths = ", pivot_lengths


    ## Cache the result
    self.__basis_of_short_vectors = basis
    self.__basis_of_short_vectors_lenghts = pivot_lengths


    ## Return the appropriate result
    if show_lengths:
        return basis, pivot_lengths
    else:
        return basis
예제 #2
0
def basis_of_short_vectors(self, show_lengths=False, safe_flag=True):
    """
    Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`).

    The safe_flag allows us to select whether we want a copy of the
    output, or the original output.  By default safe_flag = True, so
    we return a copy of the cached information.  If this is set to
    False, then the routine is much faster but the return values are
    vulnerable to being corrupted by the user.

    OUTPUT:
        a list of vectors, and optionally a list of values for each vector.

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Q.basis_of_short_vectors()
        [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)]
        sage: Q.basis_of_short_vectors(True)
        ([(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)], [1, 3, 5, 7])

    """
    ## Try to use the cached results
    try:
        ## Return the appropriate result
        if show_lengths:
            if safe_flag:
                return deep_copy(self.__basis_of_short_vectors), deepcopy(self.__basis_of_short_vectors_lengths)
            else:
                return self.__basis_of_short_vectors, self.__basis_of_short_vectors_lengths
        else:
            if safe_flag:
                return deepcopy(self.__basis_of_short_vectors)
            else:
                return deepcopy(self.__basis_of_short_vectors)
    except Exception:
        pass

    ## Set an upper bound for the number of vectors to consider
    Max_number_of_vectors = 10000

    ## Generate a PARI matrix string for the associated Hessian matrix
    M_str = str(gp(self.matrix()))

    ## Run through all possible minimal lengths to find a spanning set of vectors
    n = self.dim()
    # MS = MatrixSpace(QQ, n)
    M1 = Matrix([[0]])
    vec_len = 0
    while M1.rank() < n:

        ## DIAGONSTIC
        # print
        # print "Starting with vec_len = ", vec_len
        # print "M_str = ", M_str

        vec_len += 1
        gp_mat = gp.qfminim(M_str, vec_len, Max_number_of_vectors)[3].mattranspose()
        number_of_vecs = ZZ(gp_mat.matsize()[1])
        vector_list = []
        for i in range(number_of_vecs):
            # print "List at", i, ":", list(gp_mat[i+1,])
            new_vec = vector([ZZ(x) for x in list(gp_mat[i + 1,])])
            vector_list.append(new_vec)

        ## DIAGNOSTIC
        # print "number_of_vecs = ", number_of_vecs
        # print "vector_list = ", vector_list

        ## Make a matrix from the short vectors
        if len(vector_list) > 0:
            M1 = Matrix(vector_list)

        ## DIAGNOSTIC
        # print "matrix of vectors = \n", M1
        # print "rank of the matrix = ", M1.rank()

    # print " vec_len = ", vec_len
    # print M1

    ## Organize these vectors by length (and also introduce their negatives)
    max_len = vec_len / 2
    vector_list_by_length = [[] for _ in range(max_len + 1)]
    for v in vector_list:
        l = self(v)
        vector_list_by_length[l].append(v)
        vector_list_by_length[l].append(vector([-x for x in v]))

    ## Make a matrix from the column vectors (in order of ascending length).
    sorted_list = []
    for i in range(len(vector_list_by_length)):
        for v in vector_list_by_length[i]:
            sorted_list.append(v)
    sorted_matrix = Matrix(sorted_list).transpose()

    ## Determine a basis of vectors of minimal length
    pivots = sorted_matrix.pivots()
    basis = [sorted_matrix.column(i) for i in pivots]
    pivot_lengths = [self(v) for v in basis]

    ## DIAGNOSTIC
    # print "basis = ", basis
    # print "pivot_lengths = ", pivot_lengths

    ## Cache the result
    self.__basis_of_short_vectors = basis
    self.__basis_of_short_vectors_lenghts = pivot_lengths

    ## Return the appropriate result
    if show_lengths:
        return basis, pivot_lengths
    else:
        return basis
예제 #3
0
def find_linear_relation(vs, p, M): #This is the old version. The new one is _find_linear_relation
    r"""
    Finds a linear relation between a given list of vectors over Z/p^MZ.  If they are LI, returns an empty list.

    INPUT:

    - ``vs`` -- a list of vectors over Z/p^MZ
    - ``p`` -- a prime
    - ``M`` -- positive integer

    OUTPUT:

    - A list of p-adic numbers describing the linear relation of the vs
    """
    d = len(vs)
    R = Qp(p, M)
    V = R**d

    if d == 1:
        z = True
        for r in range(len(vs[0])):
            if vs[0][r] != 0:
                z = False
        if z:
            return [R(1, M)]
        else:
            return []
        # Would be better to use the library as follows. Unfortunately, matsolvemod
        # is not included in the gen class!!
        #from sage.libs.pari.gen import pari
        #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)]
        #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift()))
        #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col()
        #v = A.matsolvemod(p ** M, aug_col)

        ## Stupid hack here to deal with the fact that I can't get
        ## matsolvemod to work if B=0
    z = True
    for r in range(len(vs[d-1])):
        if vs[d-1][r] != 0:
            z = False

    if z:
        return [R(0, M) for a in range(len(vs)-1)] + [1]

    s = '['
    for c in range(d-1):
        v = vs[c]
        for r in range(len(v)):
            s += str(ZZ(v[r]))
            if r < len(v) - 1:
                s += ','
        if c < d - 2:
            s += ';'
    s = s + ']'

    Verbose("s = %s"%(s))

    A = gp(s)
    Verbose("A = %s"%(A))
    if len(vs) == 2:
        A = A.Mat()

    s = '['
    v = vs[d-1]
    for r in range(len(v)):
        s += str(ZZ(v[r]))
        if r < len(v) - 1:
            s += ','
    s += ']~'

    Verbose("s = %s"%(s))

    B = gp(s)

    Verbose("B = %s"%(B))

    v = A.mattranspose().matsolvemod(p**M,B)

    Verbose("v = %s"%(v))

    if v == 0:
        return []
    else:
            ## Move back to SAGE from Pari
        v = [R(v[a], M) for a in range(1,len(v)+1)]
        return v + [R(-1, M)]
예제 #4
0
def _find_linear_relation(Alist, B, p, M):    #new!
    r"""
    Finds a linear relation between a given list of vectors over Z/p^MZ.  If they are LI, returns an empty list.

    INPUT:

    - ``vs`` -- a list of vectors over Z/p^MZ
    - ``p`` -- a prime
    - ``M`` -- positive integer

    OUTPUT:

    - A list of p-adic numbers describing the linear relation of the vs

    TESTS::

        sage: from sage.modular.pollack_stevens.modsym_OMS_families_space import _find_linear_relation
        sage: R = ZpCA(3, 4)
        sage: List = [Sequence([O(3^4), 2*3 + 3^2 + 3^3 + O(3^4), 2 + 3 + 3^2 + 2*3^3 + O(3^4), O(3^4), 1 + 3 + 3^2 + O(3^4), 2 + O(3^4), 1 + 3^2 + 3^3 + O(3^4), 2 + O(3^4), 1 + 3 + 3^2 + O(3^4)], universe=R), Sequence([O(3^4), 1 + 3^2 + O(3^4), 2 + 3 + 3^3 + O(3^4), O(3^4), 1 + 3 + 2*3^2 + 3^3 + O(3^4), 1 + 2*3^2 + O(3^4), 1 + 2*3 + 2*3^2 + 3^3 + O(3^4), 1 + 2*3^2 + O(3^4), 1 + 3 + 2*3^2 + 3^3 + O(3^4)], universe=R), Sequence([O(3^4), 2 + O(3^4), 3 + 3^2 + 3^3 + O(3^4), O(3^4), 2*3 + 3^2 + 3^3 + O(3^4), 1 + 2*3 + 2*3^2 + 3^3 + O(3^4), 3^3 + O(3^4), 1 + 2*3 + 2*3^2 + 3^3 + O(3^4), 2*3 + 3^2 + 3^3 + O(3^4)], universe=R)]
        sage: _find_linear_relation(List[:-1], List[-1], 3, 4)
        ([1 + 3^2 + O(3^4), 2 + 3 + 2*3^2 + O(3^4)], 2 + 2*3 + 2*3^2 + 2*3^3 + O(3^4))
        sage: _find_linear_relation(List[:-1], List[-1], 3, 3)
        ([1 + 3^2 + O(3^3), 2 + 3 + 2*3^2 + O(3^3)], 2 + 2*3 + 2*3^2 + O(3^3))
        sage: List[-1][1] -= 1
        sage: _find_linear_relation(List[:-1], List[-1], 3, 4)
        ([], None)
        sage: List[-1][1] += 1 + 3^3
        sage: _find_linear_relation(List[:-1], List[-1], 3, 4)
        ([], None)
        sage: _find_linear_relation(List[:-1], List[-1], 3, 3)
        ([1 + 3^2 + O(3^3), 2 + 3 + 2*3^2 + O(3^3)], 2 + 2*3 + 2*3^2 + O(3^3))
    """
    vs = list(Alist) + [B]
    d = len(vs)
    R = Qp(p,M)
    V = R**d

    if d == 1:
        z = True
        for r in range(len(vs[0])):
            if vs[0][r] != 0:
                z = False
        if z:
            return [R(1, M)]
        else:
            return []
        # Would be better to use the library as follows. Unfortunately, matsolvemod
        # is not included in the gen class!!
        #from sage.libs.pari.gen import pari
        #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)]
        #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift()))
        #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col()
        #v = A.matsolvemod(p ** M, aug_col)

        ## Stupid hack here to deal with the fact that I can't get
        ## matsolvemod to work if B=0
    z = True
    for r in range(len(vs[d-1])):
        if vs[d-1][r] != 0:
            z = False

    if z:
        return [R(0, M) for a in range(len(vs)-1)], 1

    s = '['
    for c in range(d-1):
        v = vs[c]
        for r in range(len(v)):
            s += str(ZZ(v[r]))
            if r < len(v) - 1:
                s += ','
        if c < d - 2:
            s += ';'
    s = s + ']'

    Verbose("s = %s"%(s))

    A = gp(s)
    Verbose("A = %s"%(A))
    if len(vs) == 2:
        A = A.Mat()

    s = '['
    v = vs[d-1]
    for r in range(len(v)):
        s += str(ZZ(v[r]))
        if r < len(v) - 1:
            s += ','
    s += ']~'

    Verbose("s = %s"%(s))

    B = gp(s)

    Verbose("B = %s"%(B))

    v = A.mattranspose().matsolvemod(p**M,B)

    Verbose("v = %s"%(v))

    if v == 0:
        return [], None
    else:
            ## Move back to SAGE from Pari
        v = [R(v[a], M) for a in range(1,len(v)+1)]
        return v, R(-1, M)
예제 #5
0
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False):
    """
    Return a list of lists of short 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(3)
        [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], []]
        sage: Q.short_vector_list_up_to_length(4)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), (-1, 0, 0, 0)],
         [],
         [(0, 1, 0, 0), (0, -1, 0, 0)]]
        sage: Q.short_vector_list_up_to_length(5)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), (-1, 0, 0, 0)],
         [],
         [(0, 1, 0, 0), (0, -1, 0, 0)],
         [(1, 1, 0, 0),
         (-1, -1, 0, 0),
         (-1, 1, 0, 0),
         (1, -1, 0, 0),
         (2, 0, 0, 0),
         (-2, 0, 0, 0)]]
        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 = QuadraticForm(matrix(6, [2, 1, 1, 1, -1, -1, 1, 2, 1, 1, -1, -1, 1, 1, 2, 0, -1, -1, 1, 1, 0, 2, 0, -1, -1, -1, -1, 0, 2, 1, -1, -1, -1, -1, 1, 2]))
        sage: vs = Q.short_vector_list_up_to_length(40)     #long time

    The cases of ``len_bound < 2`` led to exception or infinite runtime before.

    ::

        sage: Q.short_vector_list_up_to_length(0)
        []
        sage: Q.short_vector_list_up_to_length(1)
        [[(0, 0, 0, 0, 0, 0)]]

    In the case of quadratic forms that are not positive definite an error is raised.

    ::

        sage: QuadraticForm(matrix(2, [2, 0, 0, -2])).short_vector_list_up_to_length(3)
        Traceback (most recent call last):
        ...
        ValueError: Quadratic form must be positive definite in order to enumerate short vectors

    Sometimes, Pari does not compute short vectors correctly.  It returns too long vectors.

    ::

        sage: mat = matrix(2, [72, 12, 12, 120])                         #long time
        sage: len_bound = 22953421                                       #long time
        sage: gp_mat = gp.qfminim(str(gp(mat)), 2 * len_bound - 2)[3]    #long time
        sage: rows = [ map(ZZ, str(gp_mat[i,])[1:-1].split(',')) for i in range(1, gp_mat.matsize()[1] + 1) ]   #long time
        sage: vec_list = map(vector, zip(*rows))                         #long time
        sage: eval_v_cython = cython_lambda( ", ".join( "int a{0}".format(i) for i in range(2) ), " + ".join( "{coeff} * a{i} * a{j}".format(coeff = mat[i,j], i = i, j = j) for i in range(2) for j in range(2) ) )   #long time
        sage: any( eval_v_cython(*v) == 2 * 22955664 for v in vec_list ) # 22955664 > 22953421 = len_bound   #long time
        True
    """
    if not self.is_positive_definite():
        raise ValueError(
            "Quadratic form must be positive definite in order to enumerate short vectors"
        )

    ## Generate a PARI matrix string for the associated Hessian matrix
    M_str = str(gp(self.matrix()))

    if len_bound <= 0:
        return list()
    elif len_bound == 1:
        return [[(vector([ZZ(0) for _ in range(self.dim())]))]]

    ## Generate the short vectors
    gp_mat = gp.qfminim(M_str, 2 * len_bound - 2)[3]

    ## We read all n-th entries at once so that not too many sage[...] variables are
    ## used.  This is important when to many vectors are returned.
    rows = [
        map(ZZ,
            str(gp_mat[i, ])[1:-1].split(','))
        for i in range(1,
                       gp_mat.matsize()[1] + 1)
    ]
    vec_list = map(vector, zip(*rows))

    if len(vec_list) > 500:
        eval_v_cython = cython_lambda(
            ", ".join("int a{0}".format(i) for i in range(self.dim())),
            " + ".join(
                "{coeff} * a{i} * a{j}".format(coeff=self[i, j], i=i, j=j)
                for i in range(self.dim()) for j in range(i, self.dim())))
        eval_v = lambda v: eval_v_cython(*v)
    else:
        eval_v = self

    ## Sort the vectors into lists by their length
    vec_sorted_list = [list() for i in range(len_bound)]
    for v in vec_list:
        v_evaluated = eval_v(v)
        try:
            vec_sorted_list[v_evaluated].append(v)
            if not up_to_sign_flag:
                vec_sorted_list[v_evaluated].append(-v)
        except IndexError:
            ## We deal with a Pari but, that returns longer vectors that requested.
            ## E.g. : self.matrix() == matrix(2, [72, 12, 12, 120])
            ##        len_bound = 22953421
            ## gives maximal length 22955664
            pass

    ## Add the zero vector by hand
    zero_vec = vector([ZZ(0) for _ in range(self.dim())])
    vec_sorted_list[0].append(zero_vec)

    ## Return the sorted list
    return vec_sorted_list
예제 #6
0
    def linear_relation(self, List):
        r"""
        Finds a linear relation between the given list of OMSs.  If they are LI, returns a list of all 0's.
    
        INPUT:

        - ``List`` -- a list of OMSs

        OUTPUT:

        - A list of p-adic numbers describing the linear relation of the list of OMSs
        """
        for Phi in List:
            assert Phi.valuation() >= 0, "Symbols must be integral"

        R = self.base()
        ## NEED A COMMAND FOR RELATIVE PRECISION OF OMS
        M = List[0].precision_relative()
        p = self.prime()
        d = len(List)
        V = R**d

        if d == 1:
            if List[0].is_zero():
                return [R(1)]
            else:
                return [R(0)]
        # Would be better to use the library as follows. Unfortunately, matsolvemod
        # is not included in the gen class!!
        #from sage.libs.pari.gen import pari
        #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)]
        #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift()))
        #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col()
        #v = A.matsolvemod(p ** M, aug_col)

        s = '['
        for c in range(d - 1):
            v = List[c].list_of_total_measures()
            for r in range(len(v)):
                s += str(ZZ(v[r]))
                if r < len(v) - 1:
                    s += ','
            if c < d - 2:
                s += ';'
        s = s + ']'

        verbose("s = %s" % (s))

        A = gp(s)
        verbose("A = %s" % (A))
        if len(List) == 2:
            A = A.Mat()

        s = '['
        v = List[d - 1].list_of_total_measures()
        for r in range(len(v)):
            s += str(ZZ(v[r]))
            if r < len(v) - 1:
                s += ','
        s += ']~'

        verbose("s = %s" % (s))

        B = gp(s)

        verbose("B = %s" % (B))

        v = A.mattranspose().matsolvemod(p**M, B)

        verbose("v = %s" % (v))

        if v == 0:
            return [R(0) for a in range(len(v))]
        else:
            ## Move back to SAGE from Pari
            v = [R(v[a]) for a in range(1, len(v) + 1)]
            return v + [R(-1)]
예제 #7
0
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False):
    """
    Return a list of lists of short 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(3)
        [[(0, 0, 0, 0)], [(1, 0, 0, 0), (-1, 0, 0, 0)], []]
        sage: Q.short_vector_list_up_to_length(4)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), (-1, 0, 0, 0)],
         [],
         [(0, 1, 0, 0), (0, -1, 0, 0)]]
        sage: Q.short_vector_list_up_to_length(5)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), (-1, 0, 0, 0)],
         [],
         [(0, 1, 0, 0), (0, -1, 0, 0)],
         [(1, 1, 0, 0),
         (-1, -1, 0, 0),
         (-1, 1, 0, 0),
         (1, -1, 0, 0),
         (2, 0, 0, 0),
         (-2, 0, 0, 0)]]
        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 = QuadraticForm(matrix(6, [2, 1, 1, 1, -1, -1, 1, 2, 1, 1, -1, -1, 1, 1, 2, 0, -1, -1, 1, 1, 0, 2, 0, -1, -1, -1, -1, 0, 2, 1, -1, -1, -1, -1, 1, 2]))
        sage: vs = Q.short_vector_list_up_to_length(40)     #long time

    The cases of ``len_bound < 2`` led to exception or infinite runtime before.

    ::

        sage: Q.short_vector_list_up_to_length(0)
        []
        sage: Q.short_vector_list_up_to_length(1)
        [[(0, 0, 0, 0, 0, 0)]]

    In the case of quadratic forms that are not positive definite an error is raised.

    ::

        sage: QuadraticForm(matrix(2, [2, 0, 0, -2])).short_vector_list_up_to_length(3)
        Traceback (most recent call last):
        ...
        ValueError: Quadratic form must be positive definite in order to enumerate short vectors

    Sometimes, Pari does not compute short vectors correctly.  It returns too long vectors.

    ::

        sage: mat = matrix(2, [72, 12, 12, 120])                         #long time
        sage: len_bound = 22953421                                       #long time
        sage: gp_mat = gp.qfminim(str(gp(mat)), 2 * len_bound - 2)[3]    #long time
        sage: rows = [ map(ZZ, str(gp_mat[i,])[1:-1].split(',')) for i in range(1, gp_mat.matsize()[1] + 1) ]   #long time
        sage: vec_list = map(vector, zip(*rows))                         #long time
        sage: eval_v_cython = cython_lambda( ", ".join( "int a{0}".format(i) for i in range(2) ), " + ".join( "{coeff} * a{i} * a{j}".format(coeff = mat[i,j], i = i, j = j) for i in range(2) for j in range(2) ) )   #long time
        sage: any( eval_v_cython(*v) == 2 * 22955664 for v in vec_list ) # 22955664 > 22953421 = len_bound   #long time
        True
    """
    if not self.is_positive_definite() :
        raise ValueError( "Quadratic form must be positive definite in order to enumerate short vectors" )

    ## Generate a PARI matrix string for the associated Hessian matrix
    M_str = str(gp(self.matrix()))

    if len_bound <= 0 :
        return list()
    elif len_bound == 1 :
        return [ [(vector([ZZ(0) for _ in range(self.dim())]))] ]

    ## Generate the short vectors
    gp_mat = gp.qfminim(M_str, 2*len_bound - 2)[3]

    ## We read all n-th entries at once so that not too many sage[...] variables are
    ## used.  This is important when to many vectors are returned.
    rows = [ map(ZZ, str(gp_mat[i,])[1:-1].split(','))
             for i in range(1, gp_mat.matsize()[1] + 1) ]
    vec_list = map(vector, zip(*rows))

    if len(vec_list) > 500 :
        eval_v_cython = cython_lambda( ", ".join( "int a{0}".format(i) for i in range(self.dim()) ),
                                       " + ".join( "{coeff} * a{i} * a{j}".format(coeff = self[i,j], i = i, j = j)
                                                   for i in range(self.dim()) for j in range(i, self.dim()) ) )
        eval_v = lambda v: eval_v_cython(*v)
    else :
        eval_v = self

    ## Sort the vectors into lists by their length
    vec_sorted_list = [list() for i in range(len_bound)]
    for v in vec_list:
        v_evaluated = eval_v(v)
        try :
            vec_sorted_list[v_evaluated].append(v)
            if not up_to_sign_flag :
                vec_sorted_list[v_evaluated].append(-v)
        except IndexError :
            ## We deal with a Pari but, that returns longer vectors that requested.
            ## E.g. : self.matrix() == matrix(2, [72, 12, 12, 120])
            ##        len_bound = 22953421
            ## gives maximal length 22955664
            pass

    ## Add the zero vector by hand
    zero_vec = vector([ZZ(0)  for _ in range(self.dim())])
    vec_sorted_list[0].append(zero_vec)

    ## Return the sorted list
    return vec_sorted_list
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False):
    """
    Return a list of lists of short 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(3)
        [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], []]
        sage: Q.short_vector_list_up_to_length(4)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), [-1, 0, 0, 0]],
         [],
         [(0, 1, 0, 0), [0, -1, 0, 0]]]
        sage: Q.short_vector_list_up_to_length(5)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), [-1, 0, 0, 0]],
         [],
         [(0, 1, 0, 0), [0, -1, 0, 0]],
         [(1, 1, 0, 0),
         [-1, -1, 0, 0],
         (-1, 1, 0, 0),
         [1, -1, 0, 0],
         (2, 0, 0, 0),
         [-2, 0, 0, 0]]]
        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)]]
         
    """
    ## Set an upper bound for the number of vectors to consider
    Max_number_of_vectors = 10000

    ## Generate a PARI matrix string for the associated Hessian matrix
    M_str = str(gp(self.matrix()))

    ## Generate the short vectors
    gp_mat = gp.qfminim(M_str, 2 * len_bound - 2,
                        Max_number_of_vectors)[3].mattranspose()
    number_of_rows = gp_mat.matsize()[1]
    gp_mat_vector_list = [
        vector([ZZ(x) for x in gp_mat[i + 1, ]]) for i in range(number_of_rows)
    ]

    ## Sort the vectors into lists by their length
    vec_list = [[] for i in range(len_bound)]
    for v in gp_mat_vector_list:
        vec_list[self(v)].append(v)
        if up_to_sign_flag == False:
            vec_list[self(v)].append([-x for x in v])

    ## Add the zero vector by hand
    zero_vec = vector([ZZ(0) for _ in range(self.dim())])
    vec_list[0].append(zero_vec)

    ## Return the sorted list
    return vec_list
def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False):
    """
    Return a list of lists of short 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(3)
        [[(0, 0, 0, 0)], [(1, 0, 0, 0), [-1, 0, 0, 0]], []]
        sage: Q.short_vector_list_up_to_length(4)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), [-1, 0, 0, 0]],
         [],
         [(0, 1, 0, 0), [0, -1, 0, 0]]]
        sage: Q.short_vector_list_up_to_length(5)
        [[(0, 0, 0, 0)],
         [(1, 0, 0, 0), [-1, 0, 0, 0]],
         [],
         [(0, 1, 0, 0), [0, -1, 0, 0]],
         [(1, 1, 0, 0),
         [-1, -1, 0, 0],
         (-1, 1, 0, 0),
         [1, -1, 0, 0],
         (2, 0, 0, 0),
         [-2, 0, 0, 0]]]
        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)]]
         
    """
    ## Set an upper bound for the number of vectors to consider
    Max_number_of_vectors = 10000

    ## Generate a PARI matrix string for the associated Hessian matrix
    M_str = str(gp(self.matrix()))

    ## Generate the short vectors
    gp_mat = gp.qfminim(M_str, 2*len_bound-2, Max_number_of_vectors)[3].mattranspose()
    number_of_rows = gp_mat.matsize()[1]
    gp_mat_vector_list = [vector([ZZ(x)  for x in gp_mat[i+1,]])  for i in range(number_of_rows)]

    ## Sort the vectors into lists by their length
    vec_list = [[]  for i in range(len_bound)]
    for v in gp_mat_vector_list:
        vec_list[self(v)].append(v)
        if up_to_sign_flag == False:
            vec_list[self(v)].append([-x  for x in v])

    ## Add the zero vector by hand
    zero_vec = vector([ZZ(0)  for _ in range(self.dim())])
    vec_list[0].append(zero_vec)  

    ## Return the sorted list
    return vec_list