Exemplo n.º 1
0
def count_points_J_H(G,p):
    """
    Returns the number of points on J_H(GF(p))
    
    INPUT:
        
    - G - a congruence subgroup of type GammaH
    - p - a prime
    
    OUTPUT:
        
    - the number of F_p points on the jacobian of the modular curve X_H
    
    EXAMPLES::

        sage: from mdsage import *
        sage: count_points_J_H(Gamma0(29),19)
        196
    """
    M=ModularSymbols(G,sign=1)
    S=M.cuspidal_subspace()
    dq=S.diamond_bracket_matrix(p)
    #print "computing tq"
    Tq=S.hecke_matrix(p)
    #assert (Tq-dq-p).det() == (Tq-p*dq-1).det()
    return (dq+p-Tq).det()
Exemplo n.º 2
0
def compute_ambient_space(N, k, i):
    if i == 'all':
        G = DirichletGroup(N).galois_orbits()
        sgn = (-1)**k
        for j, g in enumerate(G):
            if g[0](-1) == sgn:
                compute_ambient_space(N,k,j)
        return

    if i == 'quadratic':
        G = DirichletGroup(N).galois_orbits()
        sgn = (-1)**k
        for j, g in enumerate(G):
            if g[0](-1) == sgn and g[0].order()==2:
                compute_ambient_space(N,k,j)
        return

    filename = filenames.ambient(N, k, i)
    if os.path.exists(filename):
        return

    eps = character(N, i)
    t = cputime()
    M = ModularSymbols(eps, weight=k, sign=1)
    tm = cputime(t)
    save(M, filename)
    meta = {'cputime':tm, 'dim':M.dimension(), 'M':str(M), 'version':version()}
    save(meta, filenames.meta(filename))
Exemplo n.º 3
0
def eigenvalue_fields(N, k, eps=None):
    """
    Return Hecke eigenvalue fields of the newforms in the space with
    given level, weight, and character.

    Note that computing this field involves taking random linear
    combinations of Hecke eigenvalues, so is not deterministic.  Set
    the random seed first (set_random_seed(0)) if you want this to be
    deterministic.

    INPUT:

        - N -- level; positive integer
        - k -- weight; integer at least 2
        - eps -- None or Dirichlet character; if specified N is ignored
        
    EXAMPLES::
    
        sage: import psage
        sage: psage.modform.rational.eigenvalue_fields(11,2)
        [Rational Field]
        sage: psage.modform.rational.eigenvalue_fields(43,2)
        [Rational Field, Number Field in alpha with defining polynomial x^2 - 2]
        sage: psage.modform.rational.eigenvalue_fields(DirichletGroup(13).0^2,2)
        [Cyclotomic Field of order 6 and degree 2]
        sage: psage.modform.rational.eigenvalue_fields(13,2,DirichletGroup(13).0^2)
        [Cyclotomic Field of order 6 and degree 2]
    """
    group = eps if eps else N
    from sage.all import ModularSymbols
    M = ModularSymbols(group=group, weight=k, sign=1).cuspidal_subspace()
    N = M.new_subspace()
    D = N.decomposition()
    X = [f.compact_system_of_eigenvalues([2])[1].base_ring() for f in D]
    return X
Exemplo n.º 4
0
def upper_bound_index_cusps_in_JG_torsion(G,d, bound = 60):
    """
    INPUT:
        
    - G - a congruence subgroup
    - d - integer, the size of the rational cuspidal subgroup
    - bound (optional, default = 60) - the bound for the primes p up to which to use
      the hecke matrix `T_p - <p> - p` for bounding the torsion subgroup
    
    OUTPUT:
        
    - an integer `i` such that `(\#J_G(\QQ)_{tors})/d` is a divisor of `i`.
    
    EXAMPLES::

        sage: from mdsage import *
        sage: d = rational_cuspidal_classgroup(Gamma1(23)).cardinality()
        sage: upper_bound_index_cusps_in_JG_torsion(Gamma1(23),d)
        1

    """
    N = G.level()
    M=ModularSymbols(G);
    Sint=cuspidal_integral_structure(M)
    kill_mat=(M.star_involution().matrix().restrict(Sint)-1)
    kill=kill_mat.transpose().change_ring(ZZ).row_module()
    for p in prime_range(3,bound):
        if not N % p ==0:
            kill+=kill_torsion_coprime_to_q(p,M).restrict(Sint).change_ring(ZZ).transpose().row_module()
        if kill.matrix().is_square() and kill.matrix().determinant()==d:
            #print p
            break
    kill_mat=kill.matrix().transpose()
    #print N,"index of torsion in stuff killed",kill.matrix().determinant()/d
    if kill.matrix().determinant()==d:
        return 1
        
    pm=integral_period_mapping(M)
    period_images1=[sum([M.coordinate_vector(M([c,infinity])) for c in cusps])*pm for cusps in galois_orbits(G)]
    
    m=(Matrix(period_images1)*kill_mat).stack(kill_mat)
    diag=m.change_ring(ZZ).echelon_form().diagonal()
    #print diag,prod(diag)
    assert prod(diag)==kill.matrix().determinant()/d
    
    period_images2=[M.coordinate_vector(M([c,infinity]))*pm for c in G.cusps() if c != Cusp(oo)]
    m=(Matrix(period_images2)*kill_mat).stack(kill_mat)
    m,denom=m._clear_denom()
    diag=(m.change_ring(ZZ).echelon_form()/denom).diagonal()
    #print diag
    #print prod(i.numerator() for i in diag),"if this is 1 then :)"
    return prod(i.numerator() for i in diag)
Exemplo n.º 5
0
def JG_torsion_upperbound(G, bound = 60):
    """
    INPUT:
        
    - G - a congruence subgroup
    - bound (optional, default = 60) - the bound for the primes p up to which to use
      the hecke matrix `T_p - <p> - p` for bounding the torsion subgroup
    
    OUTPUT:
        
    - A subgroup of `(S_2(G) \otimes \QQ) / S_2(G)` that is guaranteed to contain
      the rational torison subgroup, together with a subgroup generated by the
      rational cusps.
      The subgroup is given as a subgroup of `S_2(G)/NS_2(G)` for a suitable integer N

    EXAMPLES::
        
        sage: from mdsage import *
        sage: d = rational_cuspidal_classgroup(Gamma1(23)).cardinality()
        sage: upper_bound_index_cusps_in_JG_torsion(Gamma1(23),d)
        1

    """
    N = G.level()
    M=ModularSymbols(G);
    Sint=cuspidal_integral_structure(M)
    kill_mat=(M.star_involution().matrix().restrict(Sint)-1)
    kill=kill_mat.transpose().change_ring(ZZ).row_module()
    for p in prime_range(3,bound):
        if not N % p ==0:
            kill+=kill_torsion_coprime_to_q(p,M).restrict(Sint).change_ring(ZZ).transpose().row_module()
        #if kill.matrix().is_square() and kill.matrix().determinant()==d:
        #    #print p
        #    break
    kill_mat=kill.matrix().transpose()
    #print N,"index of torsion in stuff killed",kill.matrix().determinant()/d
    #if kill.matrix().determinant()==d:
    #    return 1
    d = prod(kill_mat.smith_form()[0].diagonal())    
    pm=integral_period_mapping(M)
    #period_images1=[sum([M.coordinate_vector(M([c,infinity])) for c in cusps])*pm for cusps in galois_orbits(G)]
    period_images2=[M.coordinate_vector(M([c,infinity]))*pm for c in G.cusps() if c != Cusp(oo)]
    
    m=(Matrix(period_images2)*kill_mat).stack(kill_mat)
    m,d2=m._clear_denom()
    d=gcd(d,d2)
Exemplo n.º 6
0
def is_formal_immersion(d, p):
    """This funtion returns an integer n such that we have a formall immersion in
    characteristic q != 2,p if and only if q does not divide n.
    """
    G = Gamma0(p)
    M = ModularSymbols(G, 2)
    S = M.cuspidal_subspace()
    I2 = M.hecke_operator(2) - 3
    if I2.matrix().rank() != S.dimension():
        raise RuntimeError(f"Formall immersion for d={d} p={p} failed"
                           "because I2 is not of the expected rank.")
    Te = R_dp(G.sturm_bound(), p).row_module()
    R = (R_dp(d, p).restrict_codomain(Te)).change_ring(ZZ)
    if R.rank() < d:
        return 0
    D = R.elementary_divisors()
    if D and D[-1]:
        return D[-1].prime_to_m_part(2)
    return 0
Exemplo n.º 7
0
def is_rank_of_twist_zero(p, chi):
    """Returns true if the rank of the twist of the minus part of X_0(p)
    by the character chi is zero"""
    ML = ModularSymbols(p, base_ring=chi.base_ring())
    SL = ML.cuspidal_subspace()
    TL = SL.atkin_lehner_operator()
    S_min_L = (TL + parent(TL)(1)).kernel()

    for S in S_min_L.decomposition():
        my_map = S.rational_period_mapping()
        w = ML([0, oo])
        wmap = my_map(w)
        if wmap == 0:
            return False
        tw = ML.twisted_winding_element(0, chi)
        twmap = my_map(tw)
        if twmap == 0:
            return False

    return True
Exemplo n.º 8
0
def R_dp(d, p):
    """Return the formal immersion matrix
    Args:
        d ([int]): degree of number field
        p ([prime]): prime whose formal immersion properties we'd like to check
    Returns:
        [Matrix]: The Matrix whose rows are (T_2-3)*T_i e  for i <= d.
                  This is for verifying the linear independance in
                  Corollary 6.4 of Derickx-Kamienny-Stein-Stoll.
    """
    M = ModularSymbols(Gamma0(p), 2)
    S = M.cuspidal_subspace()
    S_int = S.integral_structure()
    e = M([0, oo])
    I2 = M.hecke_operator(2) - 3

    def get_row(i):
        return S_int.coordinate_vector(
            S_int(M.coordinate_vector(I2(M.hecke_operator(i)(e)))))

    return Matrix([get_row(i) for i in range(1, d + 1)]).change_ring(ZZ)
Exemplo n.º 9
0
def degrees(N, k, eps=None):
    """
    Return the degrees of the newforms of level N, weight k, with character eps.

    INPUT:

        - N -- level; positive integer or Dirichlet character
        - k -- weight; integer at least 2
        - eps -- None or Dirichlet character; if specified N is ignored
        
    EXAMPLES::

        sage: import psage
        sage: psage.modform.rational.degrees(11,2)
        [1]
        sage: psage.modform.rational.degrees(37,2)
        [1, 1]
        sage: psage.modform.rational.degrees(43,2)
        [1, 2]
        sage: psage.modform.rational.degrees(DirichletGroup(13).0^2,2)
        [1]
        sage: psage.modform.rational.degrees(13,2,DirichletGroup(13).0^2)
        [1]
        sage: psage.modform.rational.degrees(13,2)
        []
    """
    group = eps if eps else N
    from sage.all import ModularSymbols, dimension_new_cusp_forms
    d = dimension_new_cusp_forms(group, k)
    if d == 0:
        # A useful optimization!
        return []
    M = ModularSymbols(group=group, weight=k, sign=1).cuspidal_subspace()
    N = M.new_subspace()
    D = N.decomposition()
    # TODO: put in a consistency check.
    degs = [f.dimension() for f in D]
    assert sum(degs) == d, "major consistency check failed in degrees"
    return degs
Exemplo n.º 10
0
def degrees(N, k, eps=None):
    """
    Return the degrees of the newforms of level N, weight k, with character eps.

    INPUT:

        - N -- level; positive integer or Dirichlet character
        - k -- weight; integer at least 2
        - eps -- None or Dirichlet character; if specified N is ignored
        
    EXAMPLES::

        sage: import psage
        sage: psage.modform.rational.degrees(11,2)
        [1]
        sage: psage.modform.rational.degrees(37,2)
        [1, 1]
        sage: psage.modform.rational.degrees(43,2)
        [1, 2]
        sage: psage.modform.rational.degrees(DirichletGroup(13).0^2,2)
        [1]
        sage: psage.modform.rational.degrees(13,2,DirichletGroup(13).0^2)
        [1]
        sage: psage.modform.rational.degrees(13,2)
        []
    """
    group = eps if eps else N
    from sage.all import ModularSymbols, dimension_new_cusp_forms
    d = dimension_new_cusp_forms(group, k)
    if d == 0:
        # A useful optimization!
        return []
    M = ModularSymbols(group=group, weight=k, sign=1).cuspidal_subspace()
    N = M.new_subspace()
    D = N.decomposition()
    # TODO: put in a consistency check.
    degs = [f.dimension() for f in D]
    assert sum(degs) == d, "major consistency check failed in degrees"
    return degs
Exemplo n.º 11
0
def modular_symbols_from_curve(C, N, num_factors=3):
    """
    Find the modular symbols spaces that shoudl correspond to the
    Jacobian of the given hyperelliptic curve, up to the number of
    factors we consider.
    
    INPUT:
        - C -- a hyperelliptic curve over QQ
        - N -- a positive integer
        - num_factors -- number of Euler factors to verify match up; this is
          important, because if, e.g., there is only one factor of degree g(C), we
          don't want to just immediately conclude that Jac(C) = A_f. 
        
    OUTPUT:
        - list of all sign 1 simple modular symbols factor of level N
          that correspond to a simple modular abelian A_f
          that is isogenous to Jac(C).  

    EXAMPLES::

        sage: from psage.modform.rational.unfiled import modular_symbols_from_curve

        sage: R.<x> = ZZ[]
        sage: f = x^7+4*x^6+5*x^5+x^4-3*x^3-2*x^2+1
        sage: C1 = HyperellipticCurve(f)
        sage: modular_symbols_from_curve(C1, 284)
        [Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 39 for Gamma_0(284) of weight 2 with sign 1 over Rational Field]
        
        sage: f = x^7-7*x^5-11*x^4+5*x^3+18*x^2+4*x-11
        sage: C2 = HyperellipticCurve(f)
        sage: modular_symbols_from_curve(C2, 284)
        [Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 39 for Gamma_0(284) of weight 2 with sign 1 over Rational Field]
    """
    # We will use the Eichler-Shimura relation and David Harvey's
    # p-adic point counting hypellfrob.  Harvey's code has the
    # constraint:   p > (2*g + 1)*(2*prec - 1).
    # So, find the smallest p not dividing N that satisfies the
    # above constraint, for our given choice of prec.

    f, f2 = C.hyperelliptic_polynomials()
    if f2 != 0:
        raise NotImplementedError, "curve must be of the form y^2 = f(x)"
    if f.degree() % 2 == 0:
        raise NotImplementedError, "curve must be of the form y^2 = f(x) with f(x) odd"

    prec = 1

    g = C.genus()
    B = (2 * g + 1) * (2 * prec - 1)

    from sage.rings.all import next_prime
    p = B

    # We use that if F(X) is the characteristic polynomial of the
    # Hecke operator T_p, then X^g*F(X+p/X) is the characteristic
    # polynomial of Frob_p, because T_p = Frob_p + p/Frob_p, according
    # to Eichler-Shimura.  Use this to narrow down the factors.

    from sage.all import ModularSymbols, Integers, get_verbose
    D = ModularSymbols(
        N, sign=1).cuspidal_subspace().new_subspace().decomposition()
    D = [A for A in D if A.dimension() == g]

    from sage.schemes.hyperelliptic_curves.hypellfrob import hypellfrob

    while num_factors > 0:
        p = next_prime(p)
        while N % p == 0:
            p = next_prime(p)

        R = Integers(p**prec)['X']
        X = R.gen()
        D2 = []
        # Compute the charpoly of Frobenius using hypellfrob
        M = hypellfrob(p, 1, f)
        H = R(M.charpoly())

        for A in D:
            F = R(A.hecke_polynomial(p))
            # Compute charpoly of Frobenius from F(X)
            G = R(F.parent()(X**g * F(X + p / X)))
            if get_verbose(): print(p, G, H)
            if G == H:
                D2.append(A)
        D = D2
        num_factors -= 1

    return D
Exemplo n.º 12
0
def modular_symbols_ambient_from_lmfdb_mf(data):
    chi = dirichlet_character_from_lmfdb_mf(data)
    return ModularSymbols(chi.sage_character(), data[u'weight'])
Exemplo n.º 13
0
    def __init__(self, p, congruence_type=1, sign=1, algorithm="custom", verbose=False, dump_dir=None):
        """
        Create a Kamienny criterion object.

        INPUT:

            - `p` -- prime -- verify that there is no order p torsion
              over a degree `d` field
            - `sign` -- 1 (default),-1 or 0 -- the sign of the modular symbols space to use 
            - ``algorithm`` -- "default" or "custom" whether to use a custom (faster)
              integral structure algorithm or to use the sage builtin algortihm
            - ``verbose`` -- bool; whether to print extra stuff while
              running.

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29, algorithm="custom", verbose=False); C
            Kamienny's Criterion for p=29
            sage: C.use_custom_algorithm
            True
            sage: C.p
            29
            sage: C.verbose
            False        
        """
        self.verbose = verbose
        self.dump_dir = dump_dir
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print "init"
        assert congruence_type == 0 or congruence_type == 1
        self.congruence_type=congruence_type
        try:
            p = ZZ(p)
            if congruence_type==0:
                self.congruence_group = Gamma0(p)
            if congruence_type==1:
                self.congruence_group = GammaH(p,[-1])
        except TypeError:
            self.congruence_group = GammaH(p.level(),[-1]+p._generators_for_H())
            self.congruence_type = ("H",self.congruence_group._list_of_elements_in_H())
            
        self.p = self.congruence_group.level()
  
        self.algorithm=algorithm
        self.sign=sign
        
        self.M = ModularSymbols(self.congruence_group, sign=sign)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "modsym"
        self.S = self.M.cuspidal_submodule()
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "cuspsub"
        self.use_custom_algorithm = False
        if algorithm=="custom":
            self.use_custom_algorithm = True
        if self.use_custom_algorithm:
            int_struct = self.integral_cuspidal_subspace()
            if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "custom int_struct"
        else:    
            int_struct = self.S.integral_structure()
            if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "sage int_struct"
        self.S_integral = int_struct
        v = VectorSpace(GF(2), self.S.dimension()).random_element()
        self.v=v
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "rand_vect"
        if dump_dir:
            v.dump(dump_dir+"/vector%s_%s" % (p,congruence_type))
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "dump"
Exemplo n.º 14
0
class KamiennyCriterion:
    """
    This class if for verification of Kamienny's criterion for arbitrary fields
    using ell=2 for arbitrary level.

    EXAMPLES::

        sage: from mdsage import *
        sage: C = KamiennyCriterion(31); C
        Kamienny's Criterion for p=31
        sage: C.dbd(2)
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.dbd(2)^15==1
        True
        sage: C.T(2)
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.t1(n=3)
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.t2()
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.t()
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
    """
    def __init__(self, p, congruence_type=1, sign=1, algorithm="custom", verbose=False, dump_dir=None):
        """
        Create a Kamienny criterion object.

        INPUT:

            - `p` -- prime -- verify that there is no order p torsion
              over a degree `d` field
            - `sign` -- 1 (default),-1 or 0 -- the sign of the modular symbols space to use 
            - ``algorithm`` -- "default" or "custom" whether to use a custom (faster)
              integral structure algorithm or to use the sage builtin algortihm
            - ``verbose`` -- bool; whether to print extra stuff while
              running.

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29, algorithm="custom", verbose=False); C
            Kamienny's Criterion for p=29
            sage: C.use_custom_algorithm
            True
            sage: C.p
            29
            sage: C.verbose
            False        
        """
        self.verbose = verbose
        self.dump_dir = dump_dir
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print "init"
        assert congruence_type == 0 or congruence_type == 1
        self.congruence_type=congruence_type
        try:
            p = ZZ(p)
            if congruence_type==0:
                self.congruence_group = Gamma0(p)
            if congruence_type==1:
                self.congruence_group = GammaH(p,[-1])
        except TypeError:
            self.congruence_group = GammaH(p.level(),[-1]+p._generators_for_H())
            self.congruence_type = ("H",self.congruence_group._list_of_elements_in_H())
            
        self.p = self.congruence_group.level()
  
        self.algorithm=algorithm
        self.sign=sign
        
        self.M = ModularSymbols(self.congruence_group, sign=sign)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "modsym"
        self.S = self.M.cuspidal_submodule()
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "cuspsub"
        self.use_custom_algorithm = False
        if algorithm=="custom":
            self.use_custom_algorithm = True
        if self.use_custom_algorithm:
            int_struct = self.integral_cuspidal_subspace()
            if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "custom int_struct"
        else:    
            int_struct = self.S.integral_structure()
            if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "sage int_struct"
        self.S_integral = int_struct
        v = VectorSpace(GF(2), self.S.dimension()).random_element()
        self.v=v
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "rand_vect"
        if dump_dir:
            v.dump(dump_dir+"/vector%s_%s" % (p,congruence_type))
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "dump"



    def __repr__(self):
        """
        Return string representation.

        EXAMPLES::

            sage: from mdsage import *
            sage: KamiennyCriterion(29).__repr__()
            "Kamienny's Criterion for p=29"
        """
        return "Kamienny's Criterion for p=%s" % self.p

    @cached_method
    def coset_representatives_H(self):
        """
        Return representatives of Z/NZ^*/H where H is a subgroup of the
        diamond operators and N is the level. H=Z/NZ^* for Gamma0 and H=1 
        for Gamma1

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(GammaH(31,[3^5]))
            sage: C.coset_representatives_H()
            (1, 2, 3, 4, 8)
            sage: C = KamiennyCriterion(13)
        """
        G = self.congruence_group
        coset_reps = []
        done = set([])
        for i in Integers(self.p):
            if not i.is_unit() or i in done:
                continue
            coset_reps.append(i)
            done.update([i*h for h in G._list_of_elements_in_H()])
        return tuple(coset_reps)


    def dbd(self, d):
        """
        Return matrix of <d>.

        INPUT:

            - `d` -- integer

        OUTPUT:

            - a matrix modulo 2

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.dbd(2)
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.dbd(2)^14==1
            True
            sage: C.dbd(2)[0]
            (0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1)
        """
        d=ZZ(d)
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print "dbd start"
        try: return self._dbd[d % self.p]
        except AttributeError: pass
        # Find generators of the integers modulo p:
        gens = Integers(self.p).unit_gens()
        orders = [g.multiplicative_order() for g in gens]
        # Compute corresponding <z> operator on integral cuspidal modular symbols
        
        X = [self.M.diamond_bracket_operator(z).matrix() for z in gens]
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "create d"
        X = [x.restrict(self.S_integral, check=False) for x in X]
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "restrict d"
        
        X = [matrix_modp(x) for x in X]
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "mod d"
        # Take combinations to make list self._dbd of all dbd's such that
        # self._dbd[d] = <d>
        from itertools import product
        v = [None] * self.p
        for ei in product(*[range(i) for i in orders]):
            di = prod(g**e for e,g in zip(ei,gens)).lift()
            m = prod(g**e for e,g in zip(ei,X))
            v[di] = m
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "mul"

        assert v.count(None) == (self.p-euler_phi(self.p))
        self._dbd = v
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "bdb finnished"
        return v[d % self.p]

    @cached_method
    def T(self, n):
        """
        Return matrix mod 2 of the n-th Hecke operator on the +1
        quotient of cuspidal modular symbols.

        INPUT:

            - `n` -- integer

        OUTPUT:

            matrix modulo 2

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.T(2)
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.T(2)[0]
            (1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1)
        """
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print "T(%s) start" % (n)
        
        T = self.M.hecke_matrix(n).restrict(self.S_integral, check=False)
    
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "T created"
        if self.verbose: print "sparsity", len(T.nonzero_positions()) / RR(T.nrows()**2), T.nrows()
        T = matrix_modp(T)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "T reduced"
        #self.M._hecke_matrices={}
        #self.S._hecke_matrices={}
        #if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "T freed"
        return matrix_modp(T)

    def t1(self, n=5):
        """
        Return choice of element t1 of the Hecke algebra mod 2,
        computed using the Hecke operator $T_n$, where n is self.n
        
        INPUT:
        
            - `n` -- integer (optional default=5)
            
        OUTPUT:

            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t1(n=3)
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.t1(n=3) == 1
            True
            sage: C.t1()
            Traceback (most recent call last):
            ...
            ValueError: T_5 needs to be a generator of the hecke algebra
            sage: C = KamiennyCriterion(37)
            sage: C.t1()[0]
            (1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0) 
        """
        T = self.S.hecke_matrix(n)
        f = T.charpoly()
        F = f.factor()
        if prod(i[1] for i in F) != 1:
            raise ValueError("T_%s needs to be a generator of the hecke algebra"%n)

        # Compute the iterators of T acting on the winding element.
        e = self.M([0, oo]).element().dense_vector()
        t = self.M.hecke_matrix(n).dense_matrix()
        g = t.charpoly()
        Z = t.iterates(e, t.nrows(), rows=True)
        # We find all factors F[i][0] for f such that
        # (g/F[i][0])(t) * e = 0.
        # We do this by computing the polynomial
        #       h = g/F[i][0],
        # turning it into a vector v, and computing
        # the matrix product v * Z.  If the product
        # is 0, then e is killed by h(t).
        J = []
        for i in range(len(F)):
            h, r = g.quo_rem(F[i][0] ** F[i][1])
            assert r == 0
            v = vector(QQ, h.padded_list(t.nrows()))
            if v * Z == 0:
                J.append(i)


        if self.verbose: print "J =", J
        if len(J) == 0:
            # The annihilator of e is the 0 ideal.
            return matrix_modp(identity_matrix(T.nrows()))
            
        # Finally compute t1.  I'm concerned about how
        # long this will take, so we reduce T mod 2 first.

        # It is important to call "self.T(2)" to get the mod-2
        # reduction of T2 with respect to the right basis (e.g., the
        # integral basis in case use_integral_structure is true.
        Tmod2 = self.T(n) 
        g = prod(F[i][0].change_ring(GF(2)) ** F[i][1] for i in J)
        t1 = g(Tmod2)
        return t1

    @cached_method
    def hecke_polynomial(self,n):
        return self.S.hecke_matrix(n).charpoly()

    @cached_method
    def t1_prime(self, n=5, p=65521):
        """
        Return a multiple of element t1 of the Hecke algebra mod 2,
        computed using the Hecke operator $T_n$, where n is self.n.
        To make computation faster we only check if ...==0 mod p.
        Hence J will contain more elements, hence we get a multiple.
        
        INPUT:
        
            - `n` -- integer (optional default=5)
            - `p` -- prime (optional default=65521)

        OUTPUT:

            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t1_prime()
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.t1_prime(n=3) == 1
            True
            sage: C = KamiennyCriterion(37)
            sage: C.t1_prime()[0]
            (1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0)
        """
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print "t1 start"
        T = self.S.hecke_matrix(n)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "hecke 1"
        f = self.hecke_polynomial(n) # this is the same as T.charpoly()
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "char 1"
        Fint = f.factor()
        if all(i[1]!=1 for i in Fint):
            return matrix_modp(zero_matrix(T.nrows()))
        #    raise ValueError("T_%s needs to be a generator of the hecke algebra"%n)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "factor 1, Fint = %s"%(Fint)
        R = f.parent().change_ring(GF(p))
        F = Fint.base_change(R)
        # Compute the iterators of T acting on the winding element.
        e = self.M([0, oo]).element().dense_vector().change_ring(GF(p))
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "wind"
        t = matrix_modp(self.M.hecke_matrix(n).dense_matrix(), p)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "hecke 2"
        g = t.charpoly()
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "char 2"
        Z = t.iterates(e, t.nrows(), rows=True)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "iter"
        # We find all factors F[i][0] for f such that
        # (g/F[i][0])(t) * e = 0.
        # We do this by computing the polynomial
        #       h = g/F[i][0],
        # turning it into a vector v, and computing
        # the matrix product v * Z.  If the product
        # is 0, then e is killed by h(t).
        J = []
        for i in range(len(F)):
            if F[i][1]!=1:
                J.append(i)
                continue
            h, r = g.quo_rem(F[i][0] ** F[i][1])
            assert r == 0
            v = vector(GF(p), h.padded_list(t.nrows()))
            if v * Z == 0:
                J.append(i)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "zero check"

        if self.verbose: print "J =", J
        if len(J) == 0:
            # The annihilator of e is the 0 ideal.
            return matrix_modp(identity_matrix(T.nrows()))

        # Finally compute t1.  I'm concerned about how
        # long this will take, so we reduce T mod 2 first.

        # It is important to call "self.T(2)" to get the mod-2
        # reduction of T2 with respect to the right basis (e.g., the
        # integral basis in case use_integral_structure is true.
        Tmod2 = self.T(n)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "hecke mod" 
        g = prod(Fint[i][0].change_ring(GF(2)) ** Fint[i][1] for i in J)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "g has degree %s"%(g.degree())
        t1 = g(Tmod2)
        if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "t1 finnished"
        return t1        



    def t2(self, q=3):
        """
        Return mod 2 matrix t2 computed using the current choice of
        prime q, as returned by self.q.
        
        INPUT:
        
            - `q` -- integer


        OUTPUT:

            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t2()[0]
            (1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0)
        """
        if q==2 or self.p % q == 0 or not is_prime(q):
            raise ValueError("q must be prime, unequal to 2 and not divide self.p")
        if self.congruence_type==0:
            return self.T(q) - (q  + 1)
        else:
            return self.T(q) -  self.dbd(q) - q

    def t(self, n=5, p=65521, q=3):
        """
        Return mod 2 matrix t, using n, p and q as options for t1 and t2.
        This is just the product of self.t1() and self.t2().
        If p=None it uses t1 else it uses t1_prime
        
        INPUT:
        
            - `n` -- integer (optional default=5), used in computing t1
            - `p` -- prime (optional default=46337), used in computing t1
            - `q` -- prime (optional default=3), used in computing t2

        OUTPUT:
                
            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t()[0]
            (0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
        """
        t2 = self.t2(q)
        if p == None:
            t1 = self.t1(n)
        else:
            t1 = self.t1_prime(n, p)
        #print "t1,t2"
        #print t1
        #print t2
        #if t1==0:
        #    print "fail"
        return  t1 * t2

    def tdbdTi(self, t, k, v):
        """
        Return a list of lists which contains precomputed values of t*dbd(d)*T(i)*v with d a set of coset 
        representatives of (Z/NZ)*/(+/-H) and 0<i<=k 
        
        INPUT:
            
            - `k` -- integer
        
        OUTPUT:
            
            - a list of lists containing hecke operators
            
        EXAMPLES::

            sage: from mdsage import *            
            sage: C = KamiennyCriterion(29,verbose=False)
            sage: Id=C.t().parent().identity_matrix()
            sage: C.t()==C.tdbdTi(C.t(),2,Id)[0][0]
            True
            sage: C.t()*C.T(2)==C.tdbdTi(C.t(),2,Id)[1][0]
            True
            sage: C.t()*C.dbd(2)*C.T(2)==C.tdbdTi(C.t(),2,Id)[1][1]
            True
        """
        return [[t * (self.dbd(d) * (self.T(i) * v)) for d in self.coset_representatives_H()] for i in xrange(1, k + 1)]
    
    def dependencies(self, t, k, l, v):
        """
        Return the vectorspace of dependencies between the t*dbd(d)*T(i)*v with d a set of coset 
        representatives of (Z/NZ)*/(+/-H) and 0<i<=kl, and t*T(l+1),...,t*T(k)
        
        INPUT:
            
            - `k` -- integer the size of the largest partion occuring in the partitions you want to check
            - `l` -- integer which is larger then or equal to the second larges partition
        
        """
        T = self.T
        span = sum(self.tdbdTi(t, l, v), [])
        span += [(t * (T(i) * v)) for i in xrange(l + 1, k + 1)]
        return matrix(GF(2), [i.list() for i in span]).kernel()


    def verify_criterion_range(self,degree,n_min,n_max,q_min,q_max,t1mod,v=None,use_rand_vec=True, verbose=False,stop_if_satisfied=True):
        torsion_order=self.p
        congruence_type=self.congruence_type
        algorithm=self.algorithm
        dependency_spaces=[]
        results=[]
        for t1n in range(n_min,n_max):
            if self.t1_prime(t1n, t1mod) == 0:
                results.append({"torsion_order":torsion_order,"congruence_type":congruence_type,
                            "algorithm":algorithm,"degree":degree,"n":t1n,
                            "satisfied":False,"message":"","use_rand_vec":use_rand_vec,
                            "result_type":"t1n_is_zero"})
                continue

            for t2q in prime_range(q_min,q_max):
                if t2q==torsion_order:
                    continue
                satisfied,message,dependencies=self.verify_criterion(degree,n=t1n,p=t1mod,q=t2q,use_rand_vec=use_rand_vec,verbose=verbose)
                dependency_spaces.append(dependencies)
                results.append({"torsion_order":torsion_order,"congruence_type":congruence_type,
                            "algorithm":algorithm,"degree":degree,"n":t1n,"p":t1mod,
                            "q":t2q,"satisfied":satisfied,"message":message,"use_rand_vec":use_rand_vec,
                            "result_type":"single"})
                if stop_if_satisfied and satisfied:
                    break
            if stop_if_satisfied and satisfied:
                break

        print [len(i) for i in dependency_spaces]
Exemplo n.º 15
0
    def __init__(self, p, congruence_type=1, sign=1, algorithm="custom", verbose=False, dump_dir=None):
        """
        Create a Kamienny criterion object.

        INPUT:

            - `p` -- prime -- verify that there is no order p torsion
              over a degree `d` field
            - `sign` -- 1 (default),-1 or 0 -- the sign of the modular symbols space to use 
            - ``algorithm`` -- "default" or "custom" whether to use a custom (faster)
              integral structure algorithm or to use the sage builtin algortihm
            - ``verbose`` -- bool; whether to print extra stuff while
              running.

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29, algorithm="custom", verbose=False); C
            Kamienny's Criterion for p=29
            sage: C.use_custom_algorithm
            True
            sage: C.p
            29
            sage: C.verbose
            False        
        """
        self.verbose = verbose
        self.dump_dir = dump_dir
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("init")
        assert congruence_type == 0 or congruence_type == 1
        self.congruence_type=congruence_type
        try:
            p = ZZ(p)
            if congruence_type==0:
                self.congruence_group = Gamma0(p)
            if congruence_type==1:
                self.congruence_group = GammaH(p,[-1])
        except TypeError:
            self.congruence_group = GammaH(p.level(),[-1]+p._generators_for_H())
            self.congruence_type = ("H",self.congruence_group._list_of_elements_in_H())
            
        self.p = self.congruence_group.level()
  
        self.algorithm=algorithm
        self.sign=sign
        
        self.M = ModularSymbols(self.congruence_group, sign=sign)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "modsym")
        self.S = self.M.cuspidal_submodule()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "cuspsub")
        self.use_custom_algorithm = False
        if algorithm=="custom":
            self.use_custom_algorithm = True
        if self.use_custom_algorithm:
            int_struct = self.integral_cuspidal_subspace()
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "custom int_struct")
        else:    
            int_struct = self.S.integral_structure()
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "sage int_struct")
        self.S_integral = int_struct
        v = VectorSpace(GF(2), self.S.dimension()).random_element()
        self.v=v
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "rand_vect")
        if dump_dir:
            v.dump(dump_dir+"/vector%s_%s" % (p,congruence_type))
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "dump")
Exemplo n.º 16
0
class KamiennyCriterion:
    """
    This class if for verification of Kamienny's criterion for arbitrary fields
    using ell=2 for arbitrary level.

    EXAMPLES::

        sage: from mdsage import *
        sage: C = KamiennyCriterion(31); C
        Kamienny's Criterion for p=31
        sage: C.dbd(2)
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.dbd(2)^15==1
        True
        sage: C.T(2)
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.t1(n=3)
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.t2()
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
        sage: C.t()
        26 x 26 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
    """
    def __init__(self, p, congruence_type=1, sign=1, algorithm="custom", verbose=False, dump_dir=None):
        """
        Create a Kamienny criterion object.

        INPUT:

            - `p` -- prime -- verify that there is no order p torsion
              over a degree `d` field
            - `sign` -- 1 (default),-1 or 0 -- the sign of the modular symbols space to use 
            - ``algorithm`` -- "default" or "custom" whether to use a custom (faster)
              integral structure algorithm or to use the sage builtin algortihm
            - ``verbose`` -- bool; whether to print extra stuff while
              running.

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29, algorithm="custom", verbose=False); C
            Kamienny's Criterion for p=29
            sage: C.use_custom_algorithm
            True
            sage: C.p
            29
            sage: C.verbose
            False        
        """
        self.verbose = verbose
        self.dump_dir = dump_dir
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("init")
        assert congruence_type == 0 or congruence_type == 1
        self.congruence_type=congruence_type
        try:
            p = ZZ(p)
            if congruence_type==0:
                self.congruence_group = Gamma0(p)
            if congruence_type==1:
                self.congruence_group = GammaH(p,[-1])
        except TypeError:
            self.congruence_group = GammaH(p.level(),[-1]+p._generators_for_H())
            self.congruence_type = ("H",self.congruence_group._list_of_elements_in_H())
            
        self.p = self.congruence_group.level()
  
        self.algorithm=algorithm
        self.sign=sign
        
        self.M = ModularSymbols(self.congruence_group, sign=sign)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "modsym")
        self.S = self.M.cuspidal_submodule()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "cuspsub")
        self.use_custom_algorithm = False
        if algorithm=="custom":
            self.use_custom_algorithm = True
        if self.use_custom_algorithm:
            int_struct = self.integral_cuspidal_subspace()
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "custom int_struct")
        else:    
            int_struct = self.S.integral_structure()
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "sage int_struct")
        self.S_integral = int_struct
        v = VectorSpace(GF(2), self.S.dimension()).random_element()
        self.v=v
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "rand_vect")
        if dump_dir:
            v.dump(dump_dir+"/vector%s_%s" % (p,congruence_type))
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "dump")



    def __repr__(self):
        """
        Return string representation.

        EXAMPLES::

            sage: from mdsage import *
            sage: KamiennyCriterion(29).__repr__()
            "Kamienny's Criterion for p=29"
        """
        return "Kamienny's Criterion for p=%s" % self.p

    @cached_method
    def coset_representatives_H(self):
        """
        Return representatives of Z/NZ^*/H where H is a subgroup of the
        diamond operators and N is the level. H=Z/NZ^* for Gamma0 and H=1 
        for Gamma1

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(GammaH(31,[3^5]))
            sage: C.coset_representatives_H()
            (1, 2, 3, 4, 8)
            sage: C = KamiennyCriterion(13)
        """
        G = self.congruence_group
        coset_reps = []
        done = set([])
        for i in Integers(self.p):
            if not i.is_unit() or i in done:
                continue
            coset_reps.append(i)
            done.update([i*h for h in G._list_of_elements_in_H()])
        return tuple(coset_reps)


    def dbd(self, d):
        """
        Return matrix of <d>.

        INPUT:

            - `d` -- integer

        OUTPUT:

            - a matrix modulo 2

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.dbd(2)
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.dbd(2)^14==1
            True
            sage: C.dbd(2)[0]
            (0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1)
        """
        d=ZZ(d)
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("dbd start")
        try: return self._dbd[d % self.p]
        except AttributeError: pass
        # Find generators of the integers modulo p:
        gens = Integers(self.p).unit_gens()
        orders = [g.multiplicative_order() for g in gens]
        # Compute corresponding <z> operator on integral cuspidal modular symbols
        
        X = [self.M.diamond_bracket_operator(z).matrix() for z in gens]
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "create d")
        X = [x.restrict(self.S_integral, check=False) for x in X]
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "restrict d")
        
        X = [matrix_modp(x) for x in X]
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "mod d")
        # Take combinations to make list self._dbd of all dbd's such that
        # self._dbd[d] = <d>
        from itertools import product
        v = [None] * self.p
        for ei in product(*[list(range(i)) for i in orders]):
            di = prod(g**e for e,g in zip(ei,gens)).lift()
            m = prod(g**e for e,g in zip(ei,X))
            v[di] = m
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "mul")

        assert v.count(None) == (self.p-euler_phi(self.p))
        self._dbd = v
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "bdb finnished")
        return v[d % self.p]

    @cached_method
    def T(self, n):
        """
        Return matrix mod 2 of the n-th Hecke operator on the +1
        quotient of cuspidal modular symbols.

        INPUT:

            - `n` -- integer

        OUTPUT:

            matrix modulo 2

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.T(2)
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.T(2)[0]
            (1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1)
        """
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("T(%s) start" % (n))
        
        T = self.M.hecke_matrix(n).restrict(self.S_integral, check=False)
    
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "T created")
        if self.verbose: print("sparsity", len(T.nonzero_positions()) / RR(T.nrows()**2), T.nrows())
        T = matrix_modp(T)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "T reduced")
        #self.M._hecke_matrices={}
        #self.S._hecke_matrices={}
        #if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "T freed"
        return matrix_modp(T)

    def t1(self, n=5):
        """
        Return choice of element t1 of the Hecke algebra mod 2,
        computed using the Hecke operator $T_n$, where n is self.n
        
        INPUT:
        
            - `n` -- integer (optional default=5)
            
        OUTPUT:

            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t1(n=3)
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.t1(n=3) == 1
            True
            sage: C.t1()
            Traceback (most recent call last):
            ...
            ValueError: T_5 needs to be a generator of the hecke algebra
            sage: C = KamiennyCriterion(37)
            sage: C.t1()[0]
            (1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0) 
        """
        T = self.S.hecke_matrix(n)
        f = T.charpoly()
        F = f.factor()
        if prod(i[1] for i in F) != 1:
            raise ValueError("T_%s needs to be a generator of the hecke algebra"%n)

        # Compute the iterators of T acting on the winding element.
        e = self.M([0, oo]).element().dense_vector()
        t = self.M.hecke_matrix(n).dense_matrix()
        g = t.charpoly()
        Z = t.iterates(e, t.nrows(), rows=True)
        # We find all factors F[i][0] for f such that
        # (g/F[i][0])(t) * e = 0.
        # We do this by computing the polynomial
        #       h = g/F[i][0],
        # turning it into a vector v, and computing
        # the matrix product v * Z.  If the product
        # is 0, then e is killed by h(t).
        J = []
        for i in range(len(F)):
            h, r = g.quo_rem(F[i][0] ** F[i][1])
            assert r == 0
            v = vector(QQ, h.padded_list(t.nrows()))
            if v * Z == 0:
                J.append(i)


        if self.verbose: print("J =", J)
        if len(J) == 0:
            # The annihilator of e is the 0 ideal.
            return matrix_modp(identity_matrix(T.nrows()))
            
        # Finally compute t1.  I'm concerned about how
        # long this will take, so we reduce T mod 2 first.

        # It is important to call "self.T(2)" to get the mod-2
        # reduction of T2 with respect to the right basis (e.g., the
        # integral basis in case use_integral_structure is true.
        Tmod2 = self.T(n) 
        g = prod(F[i][0].change_ring(GF(2)) ** F[i][1] for i in J)
        t1 = g(Tmod2)
        return t1

    @cached_method
    def hecke_polynomial(self,n):
        return self.S.hecke_matrix(n).charpoly()

    @cached_method
    def t1_prime(self, n=5, p=65521):
        """
        Return a multiple of element t1 of the Hecke algebra mod 2,
        computed using the Hecke operator $T_n$, where n is self.n.
        To make computation faster we only check if ...==0 mod p.
        Hence J will contain more elements, hence we get a multiple.
        
        INPUT:
        
            - `n` -- integer (optional default=5)
            - `p` -- prime (optional default=65521)

        OUTPUT:

            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t1_prime()
            22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries)
            sage: C.t1_prime(n=3) == 1
            True
            sage: C = KamiennyCriterion(37)
            sage: C.t1_prime()[0]
            (1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0)
        """
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("t1 start")
        T = self.S.hecke_matrix(n)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "hecke 1")
        f = self.hecke_polynomial(n) # this is the same as T.charpoly()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "char 1")
        Fint = f.factor()
        if all(i[1]!=1 for i in Fint):
            return matrix_modp(zero_matrix(T.nrows()))
        #    raise ValueError("T_%s needs to be a generator of the hecke algebra"%n)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "factor 1, Fint = %s"%(Fint))
        R = f.parent().change_ring(GF(p))
        F = Fint.base_change(R)
        # Compute the iterators of T acting on the winding element.
        e = self.M([0, oo]).element().dense_vector().change_ring(GF(p))
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "wind")
        t = matrix_modp(self.M.hecke_matrix(n).dense_matrix(), p)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "hecke 2")
        g = t.charpoly()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "char 2")
        Z = t.iterates(e, t.nrows(), rows=True)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "iter")
        # We find all factors F[i][0] for f such that
        # (g/F[i][0])(t) * e = 0.
        # We do this by computing the polynomial
        #       h = g/F[i][0],
        # turning it into a vector v, and computing
        # the matrix product v * Z.  If the product
        # is 0, then e is killed by h(t).
        J = []
        for i in range(len(F)):
            if F[i][1]!=1:
                J.append(i)
                continue
            h, r = g.quo_rem(F[i][0] ** F[i][1])
            assert r == 0
            v = vector(GF(p), h.padded_list(t.nrows()))
            if v * Z == 0:
                J.append(i)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "zero check")

        if self.verbose: print("J =", J)
        if len(J) == 0:
            # The annihilator of e is the 0 ideal.
            return matrix_modp(identity_matrix(T.nrows()))

        # Finally compute t1.  I'm concerned about how
        # long this will take, so we reduce T mod 2 first.

        # It is important to call "self.T(2)" to get the mod-2
        # reduction of T2 with respect to the right basis (e.g., the
        # integral basis in case use_integral_structure is true.
        Tmod2 = self.T(n)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "hecke mod") 
        g = prod(Fint[i][0].change_ring(GF(2)) ** Fint[i][1] for i in J)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "g has degree %s"%(g.degree()))
        t1 = g(Tmod2)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "t1 finnished")
        return t1        



    def t2(self, q=3):
        """
        Return mod 2 matrix t2 computed using the current choice of
        prime q, as returned by self.q.
        
        INPUT:
        
            - `q` -- integer


        OUTPUT:

            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t2()[0]
            (1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0)
        """
        if q==2 or self.p % q == 0 or not is_prime(q):
            raise ValueError("q must be prime, unequal to 2 and not divide self.p")
        if self.congruence_type==0:
            return self.T(q) - (q  + 1)
        else:
            return self.T(q) -  self.dbd(q) - q

    def t(self, n=5, p=65521, q=3):
        """
        Return mod 2 matrix t, using n, p and q as options for t1 and t2.
        This is just the product of self.t1() and self.t2().
        If p=None it uses t1 else it uses t1_prime
        
        INPUT:
        
            - `n` -- integer (optional default=5), used in computing t1
            - `p` -- prime (optional default=46337), used in computing t1
            - `q` -- prime (optional default=3), used in computing t2

        OUTPUT:
                
            - a mod 2 matrix

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.t()[0]
            (0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
        """
        t2 = self.t2(q)
        if p == None:
            t1 = self.t1(n)
        else:
            t1 = self.t1_prime(n, p)
        #print "t1,t2"
        #print t1
        #print t2
        #if t1==0:
        #    print "fail"
        return  t1 * t2

    def tdbdTi(self, t, k, v):
        """
        Return a list of lists which contains precomputed values of t*dbd(d)*T(i)*v with d a set of coset 
        representatives of (Z/NZ)*/(+/-H) and 0<i<=k 
        
        INPUT:
            
            - `k` -- integer
        
        OUTPUT:
            
            - a list of lists containing hecke operators
            
        EXAMPLES::

            sage: from mdsage import *            
            sage: C = KamiennyCriterion(29,verbose=False)
            sage: Id=C.t().parent().identity_matrix()
            sage: C.t()==C.tdbdTi(C.t(),2,Id)[0][0]
            True
            sage: C.t()*C.T(2)==C.tdbdTi(C.t(),2,Id)[1][0]
            True
            sage: C.t()*C.dbd(2)*C.T(2)==C.tdbdTi(C.t(),2,Id)[1][1]
            True
        """
        return [[t * (self.dbd(d) * (self.T(i) * v)) for d in self.coset_representatives_H()] for i in range(1, k + 1)]
    
    def dependencies(self, t, k, l, v):
        """
        Return the vectorspace of dependencies between the t*dbd(d)*T(i)*v with d a set of coset 
        representatives of (Z/NZ)*/(+/-H) and 0<i<=kl, and t*T(l+1),...,t*T(k)
        
        INPUT:
            
            - `k` -- integer the size of the largest partion occuring in the partitions you want to check
            - `l` -- integer which is larger then or equal to the second larges partition
        
        """
        T = self.T
        span = sum(self.tdbdTi(t, l, v), [])
        span += [(t * (T(i) * v)) for i in range(l + 1, k + 1)]
        return matrix(GF(2), [i.list() for i in span]).kernel()


    def verify_criterion_range(self,degree,n_min,n_max,q_min,q_max,t1mod,v=None,use_rand_vec=True, verbose=False,stop_if_satisfied=True):
        torsion_order=self.p
        congruence_type=self.congruence_type
        algorithm=self.algorithm
        dependency_spaces=[]
        results=[]
        for t1n in range(n_min,n_max):
            if self.t1_prime(t1n, t1mod) == 0:
                results.append({"torsion_order":torsion_order,"congruence_type":congruence_type,
                            "algorithm":algorithm,"degree":degree,"n":t1n,
                            "satisfied":False,"message":"","use_rand_vec":use_rand_vec,
                            "result_type":"t1n_is_zero"})
                continue

            for t2q in prime_range(q_min,q_max):
                if t2q==torsion_order:
                    continue
                satisfied,message,dependencies=self.verify_criterion(degree,n=t1n,p=t1mod,q=t2q,use_rand_vec=use_rand_vec,verbose=verbose)
                dependency_spaces.append(dependencies)
                results.append({"torsion_order":torsion_order,"congruence_type":congruence_type,
                            "algorithm":algorithm,"degree":degree,"n":t1n,"p":t1mod,
                            "q":t2q,"satisfied":satisfied,"message":message,"use_rand_vec":use_rand_vec,
                            "result_type":"single"})
                if stop_if_satisfied and satisfied:
                    break
            if stop_if_satisfied and satisfied:
                break

        print([len(i) for i in dependency_spaces])
        intersected_dependency_spaces=[]
        if dependency_spaces:
            for i in range(len(dependency_spaces[0])):
                intersection=reduce(lambda x,y:x.intersection(y), [s[i] for s in dependency_spaces])
                intersected_dependency_spaces.append(intersection)
            satisfied=all([d.dimension()<13 and (d.dimension()==0 or LinearCode(d).minimum_distance()>degree) 
                                        for d in intersected_dependency_spaces])
            results.append({"torsion_order":torsion_order,"congruence_type":congruence_type,
                            "algorithm":algorithm,"degree":degree,"n":(n_min,n_max),"p":t1mod,
                            "q":(q_min,q_max),"satisfied":satisfied,"message":"","use_rand_vec":use_rand_vec,
                            "result_type":"range"})
        return results,dependency_spaces


        

    def verify_criterion(self, d, t=None, n=5, p=65521, q=3, v=None, use_rand_vec=False, verbose=False):
        """
        Attempt to verify the criterion at p using the input t. If t is not
        given compute it using n, p and q

        INPUT:
            
            - `t` -- hecke operator (optional default=None)
            - `n` -- integer (optional default=5), used in computing t1
            - `p` -- prime (optional default=46337), used in computing t1
            - `q` -- prime (optional default=3), used in computing t2
            - `verbose` -- bool (default: True); if true, print to
              stdout as the computation is performed.

        OUTPUT:

            - bool -- True if criterion satisfied; otherwise, False
            - string -- message about what happened or went wrong

        EXAMPLES:

        We can't get p=29 to work no matter what I try, which nicely illustrates
        the various ways the criterion can fail::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.verify_criterion(4,n=5)
            dependency dimension to large to search through
            (False,
             'There is a dependency of weigt 2 in dependencies(3,1)',
             [Vector space of degree 4 and dimension 0 over Finite Field of size 2
              Basis matrix:
              [], Vector space of degree 16 and dimension 8 over Finite Field of size 2
              Basis matrix:
              [1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0]
              [0 1 0 0 0 0 1 0 1 1 0 1 1 1 0 0]
              [0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0]
              [0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0]
              [0 0 0 0 1 0 1 0 1 1 0 1 1 1 0 0]
              [0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0]
              [0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0]
              [0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0], Vector space of degree 28 and dimension 19 over Finite Field of size 2
              Basis matrix:
              [1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1]
              [0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 0]
              [0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0]
              [0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
              [0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 0]
              [0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
              [0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0]
              [0 0 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1 1]
              [0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1 1]
              [0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
              [0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 1]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 1 1]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0]])
        
        With p=43 the criterion is satisfied for d=4, thus proving that 43 is
        not the order of a torsion point on any elliptic curve over
        a quartic field::

            sage: C = KamiennyCriterion(43); C
            Kamienny's Criterion for p=43
            sage: C.verify_criterion(4)
            (True,
             'All conditions are satified for Gamma1 d=4 p=43. Using n=5, modp=65521, q=3 in Kamienny Version 1.5 and SageMath version ...',
             [Vector space of degree 4 and dimension 0 over Finite Field of size 2
              Basis matrix:
              [], Vector space of degree 23 and dimension 2 over Finite Field of size 2
              Basis matrix:
              [1 1 0 1 0 0 1 1 1 0 1 0 1 1 1 1 1 1 0 0 1 0 0]
              [0 0 1 0 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 1 0 0 0], Vector space of degree 42 and dimension 11 over Finite Field of size 2
              Basis matrix:
              [1 0 0 0 0 0 0 1 0 1 1 1 0 1 1 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 1 1 1 1 1 1]
              [0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 1 1 0 0 0 0 0 1 0 1 1 0 0 1 0 0 0 1 1 1 1 0 0 0 1 0 0]
              [0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 0 0 1 0]
              [0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0]
              [0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0]
              [0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 1 1 1 1 1 1 0 1 0 0 1 1 0 0 1 1 1 0 0 1 0 0 0 1 1 0 1]
              [0 0 0 0 0 0 1 0 0 0 1 0 1 1 1 0 1 1 0 1 0 0 1 0 0 0 1 0 1 0 1 0 0 1 1 0 0 0 1 1 1 1]
              [0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0]
              [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 1 0 1 1 0 0 0]])
        """
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("verif start")
        if self.p < (1 + 2 ** (d / 2)) ** 2 and self.verbose:
            print("WARNING: p must be at least (1+2^(d/2))^2 for the criterion to work.")
        if t == None:
            t = self.t(n, p, q)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "t")
        if v==None:
            if use_rand_vec:
                v=self.v
            else:
                v=t.parent()(1)
        if self.congruence_type==0:
            verified,message,dependencies=self.verify_gamma0(d,t,v)
        else:
            verified,message,dependencies=self.verify_gamma1(d,t,v)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "total verif time")
        if not verified:
            return verified,message,dependencies
        return True, "All conditions are satified for Gamma%s d=%s p=%s. Using n=%s, modp=%s, q=%s in Kamienny Version %s and %s" % (self.congruence_type,d, self.p, n, p, q, Kamienny_Version, version()),dependencies

    def verify_gamma0(self,d,t,v):
        ker = matrix(GF(2),[(t * self.T(n) * v).list() for n in range(1,d+1)]).kernel()
        #if ker.dimension()<ker.degree():
        #    print ker
        return ker.dimension()==0,"",[ker]
    
    def verify_gamma1(self,d,t,v):
        dependencies=[]
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("verif gamma1 start")
        satisfied=True
        message=""
        for i in range(d, (d - 1) // 2, -1):
        #We only need to start at d/2 since the dependencies(k,l) contains all neccesary
        #checks for largest partition of zise at most k and second largest at most l
            dependency = self.dependencies(t, i, d - i, v)
            dependencies.append(dependency)
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "dep (%s,%s)" % (i, d - i))
            assert dependency.degree() == len(self.coset_representatives_H()) * (d - i) + 2 * i - d
            assert dependency.degree() - dependency.dimension() <= self.S.dimension()
            if dependency.dimension() == 0:
                if self.verbose: print("...no dependencies found")
            elif dependency.dimension() > 12:
                satisfied=False
                print("dependency dimension to large to search through")
            else:
                if self.verbose: print("dependency dimension is:", dependency.dimension())
                min_dist = LinearCode(dependency).minimum_distance()
                if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "min dist")
                if self.verbose: print("...the smallest dependency has %s nonzero coefficients." % (min_dist))
                if min_dist > d:
                    if self.verbose: print(i, d - i, "passed")
                else:
                    satisfied,message= False, "There is a dependency of weigt %s in dependencies(%s,%s)" % (min_dist, i, d - i)
        return satisfied, message,dependencies

    def is_independent(self, v):
        """
        Return True if the Hecke operators in v are independent.

        INPUT:

            - `v` -- four elements of the Hecke algebra mod 2 (represented as matrices)

        OUTPUT:

            - bool

        EXAMPLES::

            sage: from mdsage import *
            sage: C = KamiennyCriterion(29)
            sage: C.is_independent([C.T(1), C.T(2), C.T(3), C.T(4)])
            True
            sage: C.is_independent([C.T(1), C.T(2), C.T(3), C.T(1)+C.T(3)])
            False        
        """
#        X = matrix(GF(2), 4, sum([a.list() for a in v], []))
#        c = sage.matrix.matrix_modn_dense.Matrix_modn_dense(X.parent(),X.list(),False,True)
#        return c.rank() == 4

        # This crashes!  See http://trac.sagemath.org/sage_trac/ticket/8301
        return matrix(GF(2), len(v), sum([a.list() for a in v], [])).rank() == len(v)
        raise NotImplementedError
    
    def integral_cuspidal_subspace(self):
        """
        In certatain cases this might return the integral structure of the cuspidal subspace.
        This code is mainly a way to compute the integral structe faster than sage does now. 
        It returns None if it cannot find the integral subspace. 
        """
        if self.verbose: tm = cputime(); mem = get_memory_usage(); print("Int struct start")
        #This code is the same as the firs part of self.M.integral_structure
        G = set([i for i, _ in self.M._mod2term])
        G = list(G)
        G.sort()
        #if there is a two term relation between two manin symbols we only need one of the two
        #so that's why we only use elements from G instead of all manin symbols.
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "G")
        B = self.M._manin_gens_to_basis.matrix_from_rows(list(G)).sparse_matrix()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "B")
        #The collums of B now span self.M.integral_structure as ZZ-module
        B, d = B._clear_denom()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "Clear denom")
        if d == 1:
            #for explanation see d == 2
            assert len(set([B.nonzero_positions_in_row(i)[0] for i in range(B.nrows()) if len(B.nonzero_positions_in_row(i)) == 1 and B[i, B.nonzero_positions_in_row(i)[0]] == 1])) == B.ncols(), "B doesn't contain the Identity"
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "Check Id")
            ZZbasis = MatrixSpace(QQ, B.ncols(), sparse=True)(1)
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "ZZbasis")
        elif d == 2:
            #in this case the matrix B will contain 2*Id as a minor this allows us to compute the hermite normal form of B in a very efficient way. This will give us the integral basis ZZbasis.
            #if it turns out to be nessecarry this can be generalized to the case d%4==2 if we don't mind to only get the right structure localized at 2
            assert len(set([B.nonzero_positions_in_row(i)[0] for i in range(B.nrows()) if len(B.nonzero_positions_in_row(i)) == 1 and B[i, B.nonzero_positions_in_row(i)[0]] == 2])) == B.ncols(), "B doesn't contain 2*Identity"    
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "Check 2*Id")
            E = matrix_modp(B,sparse=True)
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "matmodp")
            E = E.echelon_form()
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "echelon")
            ZZbasis = MatrixSpace(QQ, B.ncols(), sparse=True)(1)
            for (pivot_row, pivot_col) in zip(E.pivot_rows(), E.pivots()):
                for j in E.nonzero_positions_in_row(pivot_row):
                    ZZbasis[pivot_col, j] = QQ(1) / 2 
            if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "ZZbasis")
        else:
            return None
        #now we compute the integral kernel of the boundary map with respect to the integral basis. This will give us the integral cuspidal submodule.                 
        boundary_matrix = self.M.boundary_map().matrix()
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "Boundary matrix")
        ZZboundary_matrix=(ZZbasis*boundary_matrix).change_ring(ZZ)
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "ZZBoundary matrix")
        left_kernel_matrix=ZZboundary_matrix.transpose().dense_matrix()._right_kernel_matrix(algorithm='pari')
        if type(left_kernel_matrix)==tuple:
            left_kernel_matrix=left_kernel_matrix[1]
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "kernel matrix")
        ZZcuspidal_basis=left_kernel_matrix*ZZbasis
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "ZZkernel matrix")
        assert ZZcuspidal_basis.change_ring(QQ).echelon_form()==self.S.basis_matrix() , "the calculated integral basis does not span the right QQ vector space" # a little sanity check. This shows that the colums of ZZcuspidal_basis really span the right QQ vectorspace
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "check")
        #finally create the sub-module, we delibarately do this as a QQ vector space with custom basis, because this is faster then dooing the calculations over ZZ since sage will then use a slow hermite normal form algorithm.
        ambient_module=VectorSpace(QQ,ZZcuspidal_basis.ncols())
        int_struct = ambient_module.submodule_with_basis(ZZcuspidal_basis.rows())
        if self.verbose: print("time and mem", cputime(tm), get_memory_usage(mem), "finnished")
        return int_struct