changeset 13:ca02f8d93048 tip

Some additions made in functionality.
author Marc Masdeu <masdeu@math.columbia.edu>
date Thu, 08 Sep 2011 14:01:26 -0400
parents 6cf8fe6afa3b
children
files btquotients.sage
diffstat 1 files changed, 201 insertions(+), 115 deletions(-) [+]
line wrap: on
line diff
--- a/btquotients.sage	Wed Jul 27 09:21:09 2011 +0200
+++ b/btquotients.sage	Thu Sep 08 14:01:26 2011 -0400
@@ -46,7 +46,7 @@
        extrapow: gets added to the power attribute, and
                  it is used for the Hecke action.
 """
-class EdgeDecomposition(SageObject):
+class DoubleCosetReduction(SageObject):
     def __init__(self,Y,x,extrapow=0):
         e1=Y._BT.edge(x)
         try:
@@ -78,14 +78,23 @@
     def igamma(self,iota=None):
         Y=self._parent
         if iota is None:
+            # The default behavior: use the precision of the bruhat tits quotient (usually too low)
             iota=Y.iota
-            if(self._igamma_prec>=Y._prec):
-                return self._cached_igamma
-            self._igamma_prec=Y._prec
+            prec=Y._prec
+        else:
+            try:
+                # The user wants higher precision
+                prec=ZZ(iota)
+                iota=Y.get_iota(prec)
+                print 'prec=',prec
+                print 'iota=',iota
+            except TypeError:
+                # The user knows what she is doing, so let it go
+                return iota(self.gamma)
+        if(self._igamma_prec<prec):
+            self._igamma_prec=prec
             self._cached_igamma=iota(self.gamma)
-            return self._cached_igamma
-        else:
-            return iota(self.gamma)
+        return self._cached_igamma
 
     def t(self):
         Y=self._parent
@@ -106,7 +115,7 @@
 class BruhatTitsTree:
     def __init__(self,p):
         self._p=p
-        self._MQp=None
+        self._MQp=MatrixSpace(Qp(self._p),2,2)
         self._Mat_22=MatrixSpace(ZZ,2,2)
         self._prec=-1
 
@@ -167,12 +176,12 @@
         try: return self._edges_leaving_origin
         except:
             p=self._p
-            self._edges_leaving_origin=[[self._Mat_22([0,-1,p,0]),self.target([0,-1,p,0])]]
-            self._edges_leaving_origin.extend([[self._Mat_22([p,i,0,1]),self.target([p,i,0,1])] for i in range(p)])
+            self._edges_leaving_origin=[self._Mat_22([0,-1,p,0])]
+            self._edges_leaving_origin.extend([self._Mat_22([p,i,0,1]) for i in range(p)])
             return self._edges_leaving_origin
 
     def leaving_edges(self,M):
-        return [[self.edge(M*A[0]),self.vertex(M*A[1])] for A in self.edges_leaving_origin()]
+        return [self.edge(M*A) for A in self.edges_leaving_origin()]
 
     def opposite(self,M):
         x=copy(M)
@@ -181,7 +190,7 @@
         return self.edge(x)
 
     def entering_edges(self,M):
-         return [[self.opposite(e[0]),M] for e in self.leaving_edges(M)]
+         return [self.opposite(e) for e in self.leaving_edges(M)]
 
     r"""
     Given a list of edges (which we think of finite union of open balls)
@@ -201,12 +210,11 @@
                 edge=self._Mat_22(edge)
                 origin=self.origin(edge)
                 newE=self.leaving_edges(self.target(edge))
-                newEgood.extend([e[0] for e in newE if e[1]!=origin])
+                newEgood.extend([e for e in newE if self.target(e)!=origin])
             return self.subdivide(newEgood,level-1)
 
     def get_ball(self,center=1,level=1):
-        newE=self.leaving_edges(center)
-        return self.subdivide([e[0] for e in newE],level)
+        return self.subdivide(self.leaving_edges(center),level)
 
     def whereis(self,z):
         #Assume z belongs to some extension of QQp.
@@ -275,10 +283,10 @@
 
 class BTQuotient(SageObject,UniqueRepresentation):
     @staticmethod
-    def __classcall__(cls,p,Nminus,Nplus=1,backend='magma'):
+    def __classcall__(cls,p,Nminus,Nplus=1,backend='sage'):
         return super(BTQuotient,cls).__classcall__(cls,p,Nminus,Nplus,backend)
 
-    def __init__(self,p,Nminus,Nplus=1,backend='magma'):
+    def __init__(self,p,Nminus,Nplus=1,backend='sage'):
         Nminus=Integer(Nminus)
         Nplus=Integer(Nplus)
         p=Integer(p)
@@ -300,7 +308,7 @@
         if(backend=='magma' or not self._Nminus.is_prime() or self._Nplus!=1 or self._p==2):
             try:
                 self._magma=magma
-                magmap=magma.p
+                magmap=self._magma(p)
             except RuntimeError:
                 raise NotImplementedError,'Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.'
             self._backend='magma'
@@ -314,6 +322,7 @@
         self._cached_paths=dict()
         self._cached_decomps=dict()
         self._cached_equivalent=dict()
+        self._CM_points=dict()
 
         self._V=(QQ**4).ambient_module().change_ring(ZZ)
         self._Mat_44=MatrixSpace(ZZ,4,4)
@@ -336,11 +345,17 @@
         self._S=Graph(ndict['_S'],multiedges=True,weighted=True)
 
     def _repr_(self):
-        return "Quotient of the Bruhat Tits tree of GL_2(QQ_%s) with discriminant %s and level %s at the prime %s"%(self.Nminus().factor(),self.Nplus().factor(),self.prime())
+        return "Quotient of the Bruhat Tits tree of GL_2(QQ_%s) with discriminant %s and level %s"%(self.prime(),self.Nminus().factor(),self.Nplus().factor())
 
     def _latex_(self):
         return "X(%s,%s)\\otimes_{\\mathbb{Z}} \\mathbb{F}_{%s}"%(latex(self.level().factor()),latex(self.Nplus().factor()),latex(self.prime()))
 
+    def get_boundary(self):
+        try: return self._boundary
+        except AttributeError:
+            self._compute_quotient()
+            return self._boundary
+
     def get_vertex_list(self):
         try: return self._vertex_list
         except AttributeError:
@@ -357,7 +372,8 @@
     def genus(self):
         Nplus=self._Nplus
         lev=self._p*self._Nminus
-        #Compute the genus using the genus formulas
+        # Compute the genus using the genus formulas
+        # Source: Rotger, V. "Non-elliptic Shimura curves of genus one", Proposition 5.2
         e4=1
         e3=1
         mu=Nplus
@@ -414,7 +430,7 @@
         disc=D
         if(D%4!=1):
             disc*=4
-        ff=self._level.factor()
+        ff=self.level().factor()
         for f in ff:
             if kronecker_symbol(disc,f[0])!=-1:
                 return False
@@ -527,12 +543,21 @@
     def iota(self,g,prec=None):
         return Matrix(self._R,2,2,self.get_Iota(prec)*g)
 
+    def get_iota(self,prec=None):
+        return lambda g:Matrix(self._R,2,2,self.get_Iota(prec)*g)
+
     def get_edge_stabs(self):
         try: return self._edge_stabs
         except AttributeError:
-            self._edge_stabs=[self._edge_stabilizer(e.rep) for e in self.get_edge_list()]
+            self._edge_stabs=[self._stabilizer(e.rep,as_edge=True) for e in self.get_edge_list()]
             return self._edge_stabs
 
+    def get_vertex_stabs(self):
+        try: return self._vertex_stabs
+        except AttributeError:
+            self._vertex_stabs=[self._stabilizer(e.rep,as_edge=False) for e in self.get_vertex_list()]
+            return self._vertex_stabs
+
     def get_quaternion_algebra(self):
         try: return self._A
         except AttributeError: pass
@@ -581,18 +606,15 @@
             O_units.append(vec)
         return O_units
 
-    @cached_method
-    def get_CM_points(self,O,prec=None,ignore_units=False):
+    def get_CM_points(self,O,ignore_units=False):
         p=self._p
-        if prec is None:
-            prec=self._prec
-        if(not isinstance(O,AbsoluteOrder)):
+        try:
             disc=Integer(O)
             R = QQ['x']
             f = R([-disc, 0, 1])
             K=NumberField(f, 'sq', check=False)
             O=K.maximal_order()
-        else:
+        except TypeError:
             disc=O.discriminant()
             K=O.fraction_field()
         try: all_elts=self._CM_points[disc]
@@ -648,6 +670,7 @@
         Kp=Qq(p**2,prec=prec,names='g')
         W=[]
         for m in all_elts_split:
+            m.set_immutable()
             v=self._fixed_points(m,Kp)
             W.append(v[0])
         return W
@@ -657,13 +680,13 @@
      and each entry consists of the corresponding data for the matrix [p,a,0,1]
      where a varies from 0 to p-1.
      The data is a tuple (acter,edge_images), with edge images being of type
-     EdgeDecomposition
+     DoubleCosetReduction
      """
     @cached_method
     def _get_Up_data(self):
         E=self.get_edge_list()
         vec_a=self._BT.subdivide([1],1)
-        return [[alpha.inverse(),[EdgeDecomposition(self,e.rep*alpha) for e in E]+[EdgeDecomposition(self,e.opposite.rep*alpha) for e in E]] for alpha in vec_a]
+        return [[alpha.inverse(),[DoubleCosetReduction(self,e.rep*alpha) for e in E]+[DoubleCosetReduction(self,e.opposite.rep*alpha) for e in E]] for alpha in vec_a]
 
     @cached_method
     def _get_atkin_lehner_data(self,q):
@@ -682,7 +705,7 @@
             try:
                 x=self.iota(beta1)
                 nn=ceil(x.determinant().valuation())
-                T=[beta1,[EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn) for e in E]]
+                T=[beta1,[DoubleCosetReduction(self,x.adjoint()*e.rep,extrapow=nn) for e in E]]
                 success=True
             except PrecisionError:
                 self.increase_precision(10)
@@ -732,7 +755,7 @@
                     try:
                         x=self.iota(v1)*alphamat
                         nn=ceil(x.determinant().valuation())
-                        T.append([v1,[EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn) for e in E]])
+                        T.append([v1,[DoubleCosetReduction(self,x.adjoint()*e.rep,extrapow=nn) for e in E]])
                         success=True
                     except PrecisionError:
                         self.increase_precision(10)
@@ -790,19 +813,20 @@
         m=self._mat_p001
         new_v,h=vertex_with_hash(v)
         chain=[]
+        boundary=self.get_boundary()
         while h[1]!=0 or h[0]<0:
-            if self._boundary.has_key(h):
-                return chain,self._boundary[h]
+            if boundary.has_key(h):
+                return chain,boundary[h]
             chain.append(new_v)
             new_v,h=vertex_with_hash(new_v*m)
-        if self._boundary.has_key(h):
-            return chain,self._boundary[h]
+        if boundary.has_key(h):
+            return chain,boundary[h]
         chain.append(new_v)
         a=h[0]
         while(a>0):
             a-=1
-            if self._boundary.has_key((a,0)):
-                return chain,self._boundary[(a,0)]
+            if boundary.has_key((a,0)):
+                return chain,boundary[(a,0)]
             new_v=self._Mat_22([new_v[0,0]/self._p,0,0,1])
             new_v.set_immutable()
             chain.append(new_v)
@@ -825,11 +849,11 @@
         OM=self.get_eichler_order_quadmatrix()
         return E,E*OM*E.transpose()
 
-    def _edge_stabilizer(self,e):
+    def _stabilizer(self,e,as_edge=True):
         p=self._p
         m=valuation(e.determinant(),p)
         twom=2*m
-        E,A = self._find_lattice(e,e,True,twom)
+        E,A = self._find_lattice(e,e,as_edge,twom)
         n_units=len(self.get_units_of_order())
         ## Using PARI to get the shortest vector in the lattice (via LLL)
         try:
@@ -840,9 +864,9 @@
 
         mat=v[2].python().transpose()
         n_vecs=mat.nrows()
-        B=self.get_eichler_order_basis()
         if(n_vecs<=1):
-            return 1
+            return [[self._B_one,0,False]]
+            #return 1
         stabs=[]
         for jj in range(n_vecs):
             vec=Matrix(ZZ,1,4,list(mat.row(jj)))
@@ -854,7 +878,6 @@
                 wt=w.transpose()
                 wt.set_immutable()
                 stabs.append([wt,m,x!=p**m])
-                #self._elements.add(wt)
         return stabs
 
     def _are_equivalent(self,v1,v2,as_edges=False,twom=None,comp=False):
@@ -880,8 +903,8 @@
         if(nrd.valuation(p)==twom and nrd.is_power_of(p**2)):
             A=(Matrix(ZZ,1,4,[v[2][ii,0].python() for ii in range(4)])*E).transpose()
             A.set_immutable()
-            if(comp):
-                self._elements.add(A)
+            if comp and not as_edges:
+                    self._elements.add(A)
             self._cached_equivalent[(v1,v2,as_edges)]=(A,m)
             return (A,m)
         else:
@@ -949,20 +972,20 @@
         boundary=dict({(0,0):v0})
         self._num_edges=0
         num_verts+=1
-        B_one=self._are_equivalent(v0.rep,v0.rep,True,0,comp=True)
+        self._B_one=self._are_equivalent(v0.rep,v0.rep,True,0,comp=True)
         while len(V)>0:
             v=V.pop(0)
             E=self._BT.leaving_edges(v.rep)
             #print 'V = %s, E = %s, G = %s (target = %s)'%(num_verts,self._num_edges,1+self._num_edges-num_verts,self.genus())
             for e in E:
                 new_edge=True
-                edge_det=e[0].determinant()
+                edge_det=e.determinant()
                 edge_valuation=edge_det.valuation(p)
-                g,e1=self._find_equivalent_edge(e[0],v.leaving_edges,comp=True,valuation=edge_valuation)
+                g,e1=self._find_equivalent_edge(e,v.leaving_edges,comp=True,valuation=edge_valuation)
                 if(g):
                     e1.links.append(g)
                 else:
-                    target=e[1]
+                    target=self._BT.target(e)
                     target.set_immutable()
                     new_det=target.determinant()
                     new_valuation=new_det.valuation(p)
@@ -980,11 +1003,11 @@
                         boundary[self._BT.vertex_with_hash(v1.rep)[1]]=v1
 
                     # Add the edge to the list
-                    new_e=Edge(self,self._num_edges,e[0],v,v1,[],[],edge_det,edge_valuation)
-                    new_e.links.append(B_one)
+                    new_e=Edge(self,self._num_edges,e,v,v1,[],[],edge_det,edge_valuation)
+                    new_e.links.append(self._B_one)
 
                     # Find the opposite edge
-                    opp=self._BT.opposite(e[0])
+                    opp=self._BT.opposite(e)
 
                     # Add the edge to the graph
                     S.add_edge(v.label,v1.label,self._num_edges)
@@ -1024,7 +1047,10 @@
 
     @cached_method
     def _fixed_points(self,x,K):
-        a=x[0,0],b=x[0,1],c=x[1,0],d=x[1,1]
+        a=x[0,0]
+        b=x[0,1]
+        c=x[1,0]
+        d=x[1,1]
         t=x.trace()
         n=x.determinant()
         A=K(a-d)
@@ -1090,11 +1116,18 @@
 import sage.modular.hecke.hecke_operator
 
 class HarmonicCocycle(HeckeModuleElement):
-    def __init__(self,_parent,vec=None):
-        HeckeModuleElement.__init__(self,_parent,vec)
+    def __init__(self,_parent,vec=None,from_values=False):
+        HeckeModuleElement.__init__(self,_parent,None)
 
         self._parent=_parent
 
+        if(from_values):
+            self._R=_parent._U._R
+            self._wt=_parent._k
+            self._nE=len(_parent._E)
+            self._F=copy(vec)
+            return
+
         if(isinstance(vec,HarmonicCocycle)):
             #The first argument is just a modular form that we copy as is
             self._R=vec._R
@@ -1103,8 +1136,8 @@
             self._F=[_parent._U.element_class(_parent._U,vec._F[ii],quick=True) for ii in range(self._nE)]
             return
 
-        try: vec=Sequence(vec)
-        except TypeError: pass
+        try: vec=vec.list()
+        except AttributeError: pass
         # When vec contains coordinates for the basis
         if(isinstance(vec,(list,tuple)) and vec[0].parent() is self._parent._R):
             try:
@@ -1128,22 +1161,6 @@
             E=_parent._E
             self._F=[_parent._U.element_class(_parent._U,vec._F[ii]).l_act_by(E[ii].rep) for ii in range(self._nE)]
             return
-        elif(isinstance(vec,dict)):
-            assert(vec['p'])==_parent._X._p
-            assert(vec['t'])==_parent._U._depth
-            assert(vec['level'])==_parent._X._level
-            assert(vec['k']==_parent._U.weight()+2)
-            self._R=_parent._U._R
-            self._wt=_parent._k
-            self._nE=len(_parent._E)
-            self._F=[_parent._U(c) for c in mydata['value']]
-            return
-        elif(isinstance(vec,(list,tuple)) and vec[0].parent() is _parent._U):
-            self._R=_parent._U._R
-            self._wt=_parent._k
-            self._nE=len(_parent._E)
-            self._F=copy(vec)
-            return
         else:
             try:
                 veczp=_parent._U._R(vec)
@@ -1153,21 +1170,24 @@
                 self._F=[_parent._U(veczp) for ii in range(self._nE)]
                 return
             except:
+                print vec.parent()
                 print vec
                 raise ValueError
 
     def _add_(self,g):
         #Should ensure that self and g are modular forms of the same weight and on the same curve
-        vec=[self._F[e]+g._F[e] for e in range(self._nE)]
-        return HarmonicCocycle(self._parent,vec)
+        #vec=[self._F[e]+g._F[e] for e in range(self._nE)]
+        return HarmonicCocycle(self._parent,self.element()+g.element())
     def _sub_(self,g):
         #Should ensure that self and g are modular forms of the same weight and on the same curve
-        vec=[self._F[e]-g._F[e] for e in range(self._nE)]
-        return HarmonicCocycle(self._parent,vec)
+        #vec=[self._F[e]-g._F[e] for e in range(self._nE)]
+        return HarmonicCocycle(self._parent,self.element()+g.element())
 
     def _rmul_(self,a):
         #Should ensure that 'a' is a scalar
-        return HarmonicCocycle(self._parent,[a*self._F[e] for e in range(self._nE)])
+        #return HarmonicCocycle(self._parent,[a*self._F[e] for e in range(self._nE)])
+        return HarmonicCocycle(self._parent,a*self.element())
+
 
     def _repr_(self):
         tmp='Harmonic cocycle on '+str(self._parent)+':\n'
@@ -1190,19 +1210,19 @@
         else:
             return min([self._F[e].valuation() for e in range(self._nE)])
 
-    def list(self):
+    def _compute_element(self):
         R=self._R
         A=self._parent.basis_matrix().transpose()
         B=Matrix(R,self._nE*(self._parent._k-1),1,[self._F[e]._val[ii,0]  for e in range(self._nE) for ii in range(self._parent._k-1) ])
         res=(A.solve_right(B)).transpose()
-        return res.list()
+        return res
 
     #In HarmonicCocycle
     def evaluate(self,e1):
         X=self._parent._X
         p=X._p
-        u=EdgeDecomposition(X,e1)
-        return (u.sign()*self._F[u.label]).l_act_by(u.igamma(self.iota)*(p**(-u.power)))
+        u=DoubleCosetReduction(X,e1)
+        return (u.sign()*self._F[u.label]).l_act_by(u.igamma(self._parent.iota)*(p**(-u.power)))
 
     #In HarmonicCocycle
     def riemann_sum(self,f,center=1,level=0,E=None):
@@ -1254,6 +1274,9 @@
     def __init__(self,X,k,prec=None,basis_matrix=None):
         self._k=k
         self._X=X
+        self._E=self._X.get_edge_list()
+        self._V=self._X.get_vertex_list()
+
         if(prec is None):
             self._is_exact=True
             self._prec=None
@@ -1281,8 +1304,6 @@
             self.__matrix.set_immutable()
             self.__rank=self.__matrix.nrows()
         AmbientHeckeModule.__init__(self, self._R, self.__rank, self._X.prime()*self._X.Nplus()*self._X.Nminus(), weight=self._k)
-        self._E=self._X.get_edge_list()
-        self._V=self._X.get_vertex_list()
         self._populate_coercion_lists_()
 
     def rank(self):
@@ -1296,15 +1317,14 @@
         return self.rank()==1
 
     def _repr_(self):
-        s='Space of harmonic cocycles of weight '+str(self._k)+' on '+str(self._X)
-        return s
+        return 'Space of harmonic cocycles of weight %s on %s'%(self._k,self._X)
 
     def _latex_(self):
         s='\\text{Space of harmonic cocycles of weight }'+latex(self._k)+'\\text{ on }'+latex(self._X)
         return s
 
     def _an_element_(self):
-        return HarmonicCocycle(self,1)
+        return self.basis()[0]
 
     def _coerce_map_from_(self, S):
        """
@@ -1335,7 +1355,7 @@
         #Code how to coherce x into the space
         #Admissible values of x?
         if isinstance(x,(HarmonicCocycle,pAutomorphicForm)):
-            return HarmonicCocycle(self,x)
+            return HarmonicCocycle(self,x.element())
         return HarmonicCocycle(self,x)
 
     def is_exact(self):
@@ -1369,7 +1389,7 @@
         S=self._X.get_edge_stabs()
         p=self._X._p
         d=self._k-1
-        for e in filter(lambda e:S[e.label]!=1,self._E):
+        for e in self._E:
             try:
                 g=filter(lambda g:g[2],S[e.label])[0]
                 C=self._U.l_matrix_representation(self.iota(g[0]))
@@ -1415,7 +1435,7 @@
         for jj in range(len(self._E)):
             t=d1[jj]
             tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma(self.iota))
-        return HarmonicCocycle(self,tmp)
+        return HarmonicCocycle(self,tmp,from_values=True)
 
     def __apply_hecke_operator(self,l,f):
         R=self._R
@@ -1434,7 +1454,7 @@
             for jj in range(len(self._E)):
                 t=d1[jj]
                 tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma(self.iota))
-        return HarmonicCocycle(self,[factor*x for x in tmp])
+        return HarmonicCocycle(self,[factor*x for x in tmp],from_values=True)
 
     def _compute_atkin_lehner_matrix(self,d):
         res=self.__compute_operator_matrix(lambda f:self.__apply_atkin_lehner(d,f))
@@ -1452,9 +1472,14 @@
         for rr in range(len(basis)):
             g=T(basis[rr])
             B.set_block(0,rr,Matrix(R,len(self._E)*(self._k-1),1,[g._F[e]._val[ii,0]  for e in range(len(self._E)) for ii in range(self._k-1) ]))
-        res=(A.solve_right(B)).transpose()
-        res.set_immutable()
-        return res
+        try:
+            res=(A.solve_right(B)).transpose()
+            res.set_immutable()
+            return res
+        except ValueError:
+            print A
+            print B
+            raise ValueError
 
 class HarmonicCocyclesSubmodule(sage.modular.hecke.submodule.HeckeSubmodule,HarmonicCocycles):
     """
@@ -1482,7 +1507,6 @@
 
 class pAutomorphicForm(ModuleElement):
     def __init__(self,_parent,vec,quick=False):
-
         ModuleElement.__init__(self,_parent)
         self._parent=_parent
         self._nE=2*len(_parent._E) # We record the values at the opposite edges
@@ -1502,10 +1526,17 @@
                 assert(2*len(vec._F)==self._nE)
                 assert(isinstance(_parent._U,OCVn))
                 E=self._parent._E
+
+
+                MMM=vec._parent._U.element_class
+                tmp=[]
                 for ii in range(len(vec._F)):
-                    self._F.append(_parent._U(self._MElement(vec._parent._U,vec._F[ii]).l_act_by(E[ii].rep.inverse())))
+                    newtmp=MMM(vec._parent._U,vec._F[ii]).l_act_by(E[ii].rep.inverse())
+                    tmp.append(newtmp)
+                    self._F.append(_parent._U(newtmp))
+                A=Matrix(QQ,2,2,[0,-1/_parent._X._p,-1,0])
                 for ii in range(len(vec._F)):
-                    self._F.append(_parent._U((-1*(self._MElement(vec._parent._U,self._F[ii])).r_act_by(Matrix(QQ,2,2,[0,-1/_parent._X._p,-1,0])))))
+                    self._F.append(_parent._U(-1*tmp[ii].r_act_by(A)))
                 self._make_invariant()
 
             elif(isinstance(vec,list) and len(vec)==self._nE):
@@ -1535,11 +1566,12 @@
         M=[e.rep for e in E]+[e.opposite.rep for e in E]
         lS=len(S)
         assert(2*len(S)==self._nE)
+        MMM=self._parent._U.element_class
         for ii in range(self._nE):
             Si=S[ii%lS]
-            if(Si!=1):
-                x=self._MElement(self._F[ii].parent(),self._F[ii]._val,quick=True)
-                self._F[ii]=self._MElement(self._F[ii].parent(),0)
+            if(any([v[2] for v in Si])):
+                x=MMM(self._F[ii].parent(),self._F[ii]._val,quick=True)
+                self._F[ii]=MMM(self._F[ii].parent(),0)
                 s=QQ(0)
                 m=M[ii]
                 for v in Si:
@@ -1554,7 +1586,6 @@
         mydata=dict([])
         mydata['p']=self._parent._X._p
         mydata['t']=self._parent._U._depth
-        mydata['level']=self._parent._X._level
         mydata['weight']=self._parent._U.weight()
         mydata['value']=[c._val for c in self._F]
         return mydata
@@ -1563,7 +1594,6 @@
         mydata=load(s)
         assert(mydata['p'])==self._parent._X._p
         assert(mydata['t'])==self._parent._U._depth
-        assert(mydata['level'])==self._parent._X._level
         assert(mydata['weight']==self._parent._U.weight())
         self._F=[self._parent._U(c) for c in mydata['value']]
 
@@ -1581,7 +1611,7 @@
 
     def evaluate(self,e1):
         X=self._parent._X
-        u=EdgeDecomposition(X,e1)
+        u=DoubleCosetReduction(X,e1)
         return (self._F[u.label+u.parity*self._nE/2]).r_act_by((u.t())*X._p**(u.power))
 
     def _rmul_(self,a):
@@ -1594,7 +1624,7 @@
         p=Y._p
         Tf=[]
         for e in E:
-            u=EdgeDecomposition(Y,alpha*e.rep)
+            u=DoubleCosetReduction(Y,alpha*e.rep)
             r=u.t()*p**(-(u.power))
             if(u.parity==0):
                 tmp=self._F[u.label].r_act_by(r)
@@ -1602,7 +1632,7 @@
                 tmp=self._F[u.label+len(E)].r_act_by(r)
             Tf.append(tmp)
         for e in E:
-            u=EdgeDecomposition(Y,alpha*e.opposite.rep)
+            u=DoubleCosetReduction(Y,alpha*e.opposite.rep)
             r=u.t()*gg*p**(-(u.power))
             if(u.parity==0):
                 tmp=self._F[u.label].r_act_by(r)
@@ -1631,17 +1661,18 @@
 
     def improve(self):
         MMM=self._parent
-        h2=MMM.__apply_Up_operator(self,True)
+        h2=MMM._apply_Up_operator(self,True)
         ii=0
         current_val=0
-        old_val=-1
-        while(current_val<MMM._prec and current_val>old_val):
+        old_val=-Infinity
+        init_val=self.valuation()
+        while(current_val>old_val):
             old_val=current_val
             ii+=1
+            self._F=[self._parent._U(c) for c in h2._F]
+            h2=MMM._apply_Up_operator(self,True)
+            current_val=(h2-self).valuation()-init_val
             print ii,"th iteration, precision=",current_val
-            self._F=[self._parent._U(c) for c in h2._F]
-            h2=MMM.__apply_Up_operator(self,True)
-            current_val=(h2-self).valuation()
         self._F=[self._parent._U(c) for c in h2._F]
 
     def integrate(self,f,center=1,level=0,method='moments'):
@@ -1839,7 +1870,7 @@
     def iota(self,g):
         return self._X.iota(g,self._prec)
 
-    def __apply_Up_operator(self,f,scale=False):
+    def _apply_Up_operator(self,f,scale=False):
         HeckeData=self._X._get_Up_data()
         if(scale==False):
             factor=(self._X._p)**(self._U.weight()/2)
@@ -1910,7 +1941,8 @@
         if(B is None):
             B=self._parent.basis()
         A=Matrix(self._parent._R,self._parent.dimension(),self._parent.dimension(),[[b._val[ii,0] for b in B] for ii in range(self._depth)])
-        return A.solve_right(self._val)
+        tmp=A.solve_right(self._val)
+        return tmp
 
     def _add_(self,y):
         val=self._val+y._val
@@ -1937,7 +1969,7 @@
             factor=R.prime()**(-t)
         try:
             x=self._parent._powers[(factor*a,factor*b,factor*c,factor*d)]
-            return self.__class__(self._parent,extrafactor*factor**(-self._n)*x*self._val,quick=True)
+            return self.__class__(self._parent,(extrafactor*factor**(-self._n))*(x*self._val),quick=True)
         except KeyError:
             tmp=self._parent._get_powers_and_mult(factor*a,factor*b,factor*c,factor*d,extrafactor*factor**(-self._n),self._val)
             return self.__class__(self._parent,tmp,quick=True)
@@ -1989,7 +2021,7 @@
 
     def valuation(self,l=None):
         if not self._parent.is_exact():
-            if(l!=self._parent._R.prime()):
+            if(not l is None and l!=self._parent._R.prime()):
                 raise ValueError, "This function can only be called with the base prime"
             return min([self._val[ii,0].valuation() for ii in range(self._depth)])
         else:
@@ -2091,6 +2123,7 @@
 
     def weight(self):
         return self._n
+
     def l_matrix_representation_list(self,v,B=None):
         if B is None:
             B=self.basis()
@@ -2214,3 +2247,56 @@
             n.append(0)
         wd=prod([v[x] for x in n])
         yield wd
+
+# # Assume properly formatted
+# # [(x,xinv),(y,yinv),(z,zinv)]
+# def enum_freegp_words(v):
+#     mydict=dict()
+#     L=2*len(v)
+#     n=[]
+#     while(True):
+#         add_new=True
+#         for jj in range(len(n)):
+#             newnj=(n[jj][0]+1)%L
+#             if n[jj][2]==0:
+#                 n[jj]=[newnj,n[jj][1],1]
+#             else:
+#                 n[jj]=[newnj,(n[jj][1]+1)%len(v),0]
+#             if n[jj][0]!=0:
+#                 add_new=False
+#                 break
+#         if add_new:
+#             n.append([0,0,0])
+# 
+#         is_legal=len(n)==1 or not any([n[jj][1]==n[jj+1][1] and n[jj][2]!=n[jj+1][2] for jj in range(len(n)-1)])
+#         while not is_legal:
+#             add_new=True
+#             for jj in range(len(n)):
+#                 newnj=(n[jj][0]+1)%L
+#                 if n[jj][2]==0:
+#                     n[jj]=[newnj,n[jj][1],1]
+#                 else:
+#                     n[jj]=[newnj,(n[jj][1]+1)%len(v),0]
+#                 if n[jj][0]!=0:
+#                     add_new=False
+#                     break
+#             if add_new:
+#                 n.append([0,0,0])
+#             is_legal=len(n)==1 or not any([n[jj][1]==n[jj+1][1] and n[jj][2]!=n[jj+1][2] for jj in range(len(n)-1)])
+# 
+#         #wd=''.join([v[floor(x/2)][x%2] for x in n])
+#         try:
+#             a=mydict[tuple([n[ii][0] for ii in range(len(n)-1)])]
+#         except:
+#             a=1
+#         x=n[len(n)-1]
+#         wd=a*v[x[1]][x[2]]
+# 
+#         mydict[tuple([n[ii][0] for ii in range(len(n))])]=wd
+#         #wd=prod([v[x[1]][x[2]] for x in n])
+#         yield (wd,len(n),n)
+# 
+# #I=enum_freegp_words([('u','v'),('p','d')])
+# I=enum_freegp_words([(132,1/132),(411,1/411)])
+# I10=itertools.islice(I,100000)
+# # time Wnew=[x for x in I10]