############################################################################# # PyCox-A Python version of chevie-gap. (Works for Python 2.7 and 3.2.) # # Copyright (C) 2011-2012 Meinolf Geck # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # ############################################################################# # Import into python: 'from chv1r61 import *' # ############################################################################# print(' ') print(' ##################################################################') print(' ## A PYTHON VERSION OF CHEVIE-GAP FOR (FINITE) COXETER GROUPS ##') print(' ## (m.geck@abdn.ac.uk, version 1.61, 3 Aug 2012) ##') print(' ## ##') print(' ## To get started type "help(coxeter)" or "allfunctions()". ##') print(' ## For notes about this version type "versioninfo(1.6)". ##') print(' ## Check http://www.abdn.ac.uk/~mth190 for updates. ##') print(' ## ##') print(' ## Import into "sage" (4.7 or higher, www.sagemath.org) works. ##') print(' ## ##') print(' ## The proposed name for this module is "PyCox". ##') print(' ## All comments welcome! ##') print(' ##################################################################') print(' ') import sys # uses sys.write and sys.version import math # define our own print def lprint(x): sys.stdout.write(x) sys.stdout.flush() # enable syntax completion try: import readline except ImportError: print("Module readline not available.") else: import rlcompleter readline.parse_and_bind("tab: complete") CURRENTVERSION=sys.version if CURRENTVERSION[0]=='2' and int(CURRENTVERSION[2])>=6: inp=raw_input mybytes=(lambda x: tuple(x)) elif CURRENTVERSION[0]=='3' and int(CURRENTVERSION[2])>=2: from functools import reduce inp=input mybytes=bytes else: print("# Warning: You need Python version 2.6 or higher") def writeto(fname,l): import simplejson f=open(fname, 'w') simplejson.dump(l,f) f.close() print("finished") def allfunctions(): allf=[ "---------------------------------------------------------------------", " For more details type, for example, 'help(coxeter)'. ", "---------------------------------------------------------------------", "coxeter ..................... creates Coxeter group (as python class)", "cartanmat ............................... Cartan matrix (finite type)", "affinecartanmat ......................... Cartan matrix (affine type)", "cartantotype .................... type recognition of a Cartan matrix", "longestperm ............... longest element in a finite Coxeter group", "reflections ............... all reflections in a finite Coxeter group", "reflectionsubgroup ................ subgroup generated by reflections", "bruhat ....................................... Bruhat-Chevalley order", "conjugacyclasses .......................... of a finite Coxeter group", "fusionconjugacyclasses ............ fusion of classes from a subgroup", "conjtomin ................. conjugate to an element of minimal length", "coxeterclasses ............... coxeter classes of parabolic subgroups", "allmats ............... all elements (as matrices) up to given length", "allwords ......... all elements (as reduced words) up to given length", "allelmsproperty .................. all elements with a given property", "redrightcosetreps ......... distinguished right coset representatives", "redinrightcoset ........ reduce to distinguished coset representative", "redleftcosetreps ........... distinguished left coset representatives", "\nCharacters:\n" "chartable ................. character table of a finite Coxeter group", "inducedchar ............ induced character from a reflection subgroup", "inductiontable .................. decomposition of induced characters", "ainvariants .................................. Lusztig's a-invariants", "classpolynomials ....... class polynomials for Iwahori-Hecke algebras", "poincarepol ..................................... Poincare polynomial", "heckechartable .......... character table of an Iwahori-Hecke algebra", "heckecharvalues ........ character values on arbitrary basis elements", "leadingcoefficients .................. Lusztig's leading coefficients", "heckecentraltable .................. table of central charater values", "schurelms ................ Schur elements of an Iwahori-Hecke algebra", "lcmschurelms ............ least common multiple of the Schur elements", "fakedegree ............................... fake degree of a character", "involutionmodel ...... involution model of Kottwitz and Lusztig-Vogan", "dimBu ................... dimension of the variety of Borel subgroups", "\nCells and Families:\n" "wgraph .................... Kazhdan-Lusztig W-graph (as python class)", "klpolynomials ........................... Kazhdan-Lusztig polynomials", "klcells ....................................... Kazhdan-Lusztig cells", "relklpols ...................... relative Kazhdan-Lusztig polynomials", "wgraphstarorbit ............... orbit of W-graph under star operation", "gentauorbits ................. orbits under generalised tau-invariant", "leftcellleadingcoeffs .......... leading coefficients for a left cell", "distinguishedinvolutions .... distinguished involutions in left cells", "constructible..................... Lusztig's constructible characters", "lusztigfamilies .... Lusztig's families and the partial order on them", "specialpieces .............................. Lusztig's special pieces", "\nCombinatorics:\n" "partitions ............................. all partitions of an integer", "dualpartition ......................... dual (or conjugate) partition", "centraliserpartition ........... centraliser order in symmetric group", "bipartitions .................. all pairs of partitions of an integer", "lusztigsymbolB ................. symbol associated with a bipartition", "redlusztigsymbolB ...... reduced symbol associated with a bipartition", "ainvbipartition ........................ the correspondig a-invariant", "partitiontuples .............. all tuples of partitions of an integer", "centralisertuple ....... order in wreath product with symmetric group", "\nPolynomials and numbers:\n" "intlcm ........................ least common multiple of two integers", "gcdex .................... extended greatest common divisor algorithm", "nextprime .................... next primer bigger than a given number", "zeta5 ............. quadratic extension generated by the golden ratio", "rootof1 ......................................... cyclotomic integers", "lpol ................... creates Laurent polynomial (as python class)", "lpolmod ............. creates truncated polynomials (as python class)", "interpolatepol ............................ interpolates a polynomial", "cyclpol ....................................... cyclotomic polynomial", "cycldec ........ decomposition into product of cyclotomic polynomials", "\nUtility functions:\n" "cartesian ............................ cartesian product of two lists", "flatlist ............................................. flatten a list", "displaymat .................... display matrix in a user-friendly way", "noduplicates ......................... removes duplicates from a list", "printfunction ................... print the source code of a function", "transposemat .................................. transpose of a matrix", "matmult, matadd .......................... multiply, add two matrices", "determinantmat ..................... determinant of an integer matrix", "kroneckerproduct .................. Kronecker product of two matrices", "directsummat ........................... block direct sum of matrices", "decomposemat ........................ block decomposition of a matrix", "blockLR .......................... block LR decomposition of a matrix", "test ................................................... a test suite"] for f in range(21): print(allf[f]) a=inp("==> Press ENTER to continue <==") for f in range(26): print(allf[21+f]) a=inp("==> Press ENTER to continue <==") for f in range(32): print(allf[47+f]) return None VG=""" This is intended to be the last major version of PyCox 1. (There will be further bug fix releases, but everything entirely new will go into what may eventually be called PyCox 2.) Now, the new things in this release are: the function 'displaymat' which allows for a nice printing of various objects based on matrices, e.g., character tables, induction tables, and so on. There is also a much improved version of the function 'distinguishedinvolutions': it can compute (by a general method, and for the first time) the set of distinguished involutions for type E8; this takes about 18 days and 22GB main memory. MG, 23 Apr 2012 Patch 1.61: The help to 'chartableD' now contains a more precise description of the conventions used, especially for the case where n is even. An error has been corrected in the list of characters returned by the function 'libdistinv'. MG, 03 Aug 2012 (For the previous version type 'versioninfo(1.5)'.) """ VF=""" This release contains, first of all, some bug fixes and minor (but hopefully useful) additions, like the function 'allelmsproperty'. There is now a simple arithmetic for cyctlotomic integers, so that we can deal with dihedral groups in general; see 'rootof1'. And it also contains some basic support for Kazhdan-Lusztig polynomials, cells and W-graphs; see 'klcells', 'wgraphs' and 'klpolynomials'. Here, we make systematic use of the concept of induced cells and relative Kazhdan-Lusztig polynomials. The resulting algorthm is remarkably efficient. For example, the function 'klcells' can compute the W-graphs of all left cells for groups of rank up to about 8 (except for type E8). Some timings: Type F4: 72 left cells, about 1 second. Type H4: 206 left cells, about 370 seconds. Type E6: 652 left cells, about 45 seconds. Type E7: 6364 left cells, about 4 hours. Type A8: 2620 left cells, about 140 seconds. Type D8: 11504 left cells, about 4 hours. Type B8: 15304 left cells, about 58 hours. (For B8 at least 9GB are required; otherwise, 4GB are sufficient.) The programs are not yet completely optimised but, still, the ultimate challenge, i.e., the computation of the 101796 left cells in type E8, seems to remain out of reach for the time being. MG, 27 Jan 2012 The patch 1.51 contains a number of minor fixes; it now also has an implementation of 'relklpols' for unequal parameters. (E.g., it computes all left cells in type F_4, for any choice of parameters, in about 5 seconds; it is capable of dealing with type B_n and unequal parameters for n up to around 7.) A further addition is the function 'dimBu' which I had written years ago for gap-chevie and which is now included here. MG, 04 Feb 2012 (For the previous version type 'versioninfo(1.4)'.) """ VE=""" This module has now reached a state where it contains a number of features which have never been included in the official gap-chevie release, for example: * Lusztig's constructible characters and families (this already appeared in version 1.1; see 'lusztigfamilies'); * the algorithm for computing the sizes of special pieces (see 'specialpieces'); * the computation of character values on central elements (see 'heckecentraltable'). (However, there are also a number of features of gap-chevie which are not yet available in this module; for example, Coxeter cosets, Kazhdan-Lusztig polynomials and various things from Jean Michel's development version of gap-chevie.) I intend to add support for Kazhdan-Lusztig cells and their relative version (with possibly unequal parameters) to a later version. I am certain that further extensive checking and experimenting is required to make these programs as robust as possible. At least there is now, I believe, sufficient material to develop further applications. MG, 30 Sep 2011 (For the previous version type 'versioninfo(1.3)'.) """ VD=""" This version contains basic support for Iwahori-Hecke algebras and their characters; see 'heckechartable' and the further references there. The functions are written in such a way that the parameters can be any non-zero elements in some base ring. We also provide a simple arithmetic for Laurent polynomials in one variable (see 'lpol'), in order to be able to work with generic algebras and their characters. If something more efficient is required, then one has to import external modules (e.g., 'sympy') or work within sage (an example is given in the help for 'classpolynomials'). Another addition in this version is a simple arithmetic for the quadratic extension generated by the golden ratio (1+sqrt(5))/2, so that we now have exact arithmetic for the types I2(5), H_3, and H_4; see 'zeta5'. This also provides a more user-friendly printing of the irrationalities. MG, 23 Sep 2011 (For the previous version type 'versioninfo(1.2)'.) """ VC=""" This version mainly contains bug fixes and internal improvements to some algorithms, in particular character tables of types A,B,D. For ranks at most 8 (i.e., cases that are involved in exceptional types), this now works reasonably very fast. Some examples for larger ranks: chartable(coxeter("A",15)) now takes about 5 seconds CPU. chartable(coxeter("D",10)) now takes about 15 seconds CPU. chartable(coxeter("B",11)) now takes about 65 seconds CPU. chartable(coxeter("A",19)) now takes about 95 seconds CPU. lusztigfamilies(coxeter("E",8)) takes about 100 seconds CPU. If this is not fast enough in some applications, then one needs to re-write the functions in the gap library file 'ctsymmet.g' in python. Also added some utility functions: 'transposemat' (which made me learn quite a bit about python ...) and flatlist. Having seen how transposemat performs, I also revise my statement in versioninfo 1.0 concerning general list handling: some operations take longer than in gap, but others are faster, for example, this version of transposemat. The test suite still runs faster in python 2.7 than in 3.2. MG, 02 Sep 2011 (For the previous version type 'versioninfo(1.1)'.) """ VB=""" This second version now includes support for ordinary character tables of finite Coxeter groups. The material is developed to the point where one can compute induce/restrict tables, a-invariants and Lusztig's families in python. See the help for 'chartable' and 'lusztigfamilies' for more details. The whole module works both in python 2.7 and 3.2, but it seems to run almost twice as fast in python 2.7 than in 3.2 ! (I have not yet tried to find out why.) On the whole, the programmes work out so I intend to continue with this project. Due to other obligations, development will slow down now for a while. Some further testing and profiling is required to see where performance could be improved, e.g., in 'chartableB' and 'fusionconjugacyclasses'. The next step will be to think about a package for polynomials, on which Hecke algebras, their character tables and Kazhdan-Lusztig polynomials will rely. MG, 28 Aug 2011 (For the previous version type 'versioninfo(1.0)'.) """ VA=""" The original version of CHEVIE was developed for GAP3 (and MAPLE). This module now is the result of my efforts (1) to learn one of the more modern programming languages and (2) to see if at least parts of the gap part of chevie can be implemented in it. I chose python (version 2.7) mainly because of its popularity, and then also because 'sage' is based on python; in particular, this module can just be imported into 'sage'. The functions in this version basically implement the purely group theoretical part: creation of a (possibly infinite) Coxeter group, working with the elements. For finite Coxeter groups, there are functions for reflection subgroups and conjugacy classes. The main difficulty was to find good replacements for the fast permutation arithmetic in gap. It also seems that, in a number of applications, the general handling of lists is considerably more efficient in gap than in python. Some work will be needed to deal more properly with the irrational numbers involved in type H3, H4 and dihedral types. (Currently I just use float numbers, which appears to be ok for H3 and H4.) Overall, the functions work with satisfactory efficieny, including type E8. Plans for the next version include: * basic character table operations * Kazhdan-Lusztig polynomials MG, 23 Aug 2011 """ def versioninfo(nr): if nr>1.6: print("not yet available") elif nr==1.6: print(VG) elif nr==1.5: print(VF) elif nr==1.4: print(VE) elif nr==1.3: print(VD) elif nr==1.2: print(VC) elif nr==1.1: print(VB) elif nr==1.0: print(VA) else: print("no information available") ########################################################################## #I This file is organised in sections: #I ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #I Section 1: Numbers, polynomials, other utility functions #I Section 2: Coxeter groups, Cartan matrices, reflections #I Section 3: Characters, Schur elements, families #I Section 4: Kazhdan-Lusztig cells #I Section 5: Tests ## ########################################################################## ## #Y Section 1: Numbers, polynomials, other utility functions ## #F class-zeta5 class zeta5: """creates elements in the quadatic extension generated by the golden ratio (1+sqrt(5))/2. (This is relevant for Coxeter groups of type H_3 and H_4.) Any such element is represented as a + b * (1+sqrt(5))/2 where a,b are integers. There is an optional argument 'iname' by which one can speficy how (1+sqrt(5))/2 is printed; the default value is 'ir5'. >>> zeta5(1,1) 1+ir5 >>> zeta5(0,1)*zeta5(0,1)-zeta5(0,1)-1 0 >>> (2*zeta5(0,1)-1)**2 5 >>> W=coxeter("H",3) >>> W.roots[:W.N] >>> [(1, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (ir5, 1, 0), (1, ir5, 0), (ir5, ir5, 0), (ir5, 1, 1), (1, ir5, ir5), (ir5, ir5, ir5), (ir5, 1+ir5, 1), (ir5, 1+ir5, ir5), (1+ir5, 1+ir5, 1), (1+ir5, 1+ir5, ir5), (1+ir5, 2*ir5, ir5)] """ def __init__(self,a,b,iname='ir5'): self.a=a self.b=b self.ir5=iname def __repr__(self): if self.b==0: return repr(self.a) else: if self.a==0: r='' else: if self.b>0: r=repr(self.a)+'+' else: if type(self.b)==type(0): r=repr(self.a) else: r=repr(self.a)+'+' if self.b==1: r+=self.ir5 elif self.b>1: r+=repr(self.b)+'*'+self.ir5 elif self.b==-1: r+='-'+self.ir5 else: r+=repr(self.b)+'*'+self.ir5 return r def __eq__(self,f): if type(f)==type(0): return self.a==f and self.b==0 elif type(self)==type(f) and self.__class__==f.__class__: return self.a==f.a and self.b==f.b else: return False def __ne__(self,f): return not self==f def __gt__(self,f): if type(f)==type(0): c=self.a-f d=self.b else: c=self.a-f.a d=self.b-f.b return c+1.618033988749895*d>0 def __lt__(self,f): if type(f)==type(0): c=self.a-f d=self.b else: c=self.a-f.a d=self.b-f.b return c+1.618033988749895*d<0 def __ge__(self,f): return self==f or self>f def __le__(self,f): return self==f or self>> v=lpol([1],1,'v') # creates the indeterminate v >>> (v+1)**2-v**2-2*v-1 0 >>> p=(2*v-1)**3*v**(-2); p -v**(-2)+6*v**(-1)-12+8*v >>> print(p) lpol([-1, 6, -12, 8],-2,'v') >>> p.degree 1 >>> p.valuation -2 >>> p.value(-1) -27 >>> u=lpol([1],1,'u') # create another indeterminate >>> lpol([u**2+2,-3,u**(-1)],-17,'v') (2+u**2)*v**(-17)-3*v**(-16)+(u**(-1))*v**(-15) >>> u+v # Warning: different variables ! False >>> u*v**0+v # force u to be treated as constant polynomial in v u+v See also 'interpolatepol' """ def __init__(self,coeffs,val,vname): self.vname=vname self.vcyc=False if all(c==0 for c in coeffs): self.coeffs=[] self.val=0 self.degree=0 else: a,e=0,len(coeffs) while coeffs[a]==0: a+=1 while coeffs[e-1]==0: e-=1 self.coeffs=coeffs[a:e] self.val=val+a self.degree=val+e-1 def __repr__(self): if self.coeffs==[]: return '0' r='' for i in range(len(self.coeffs)): if self.coeffs[i]!=0: if r!='' and not (type(self.coeffs[i])==type(0) and self.coeffs[i]<0): r+='+' if i+self.val==0: if type(self.coeffs[i])==type(0): r+=repr(self.coeffs[i]) else: r+='('+repr(self.coeffs[i])+')' else: if self.coeffs[i]==-1: r+='-'+self.vname elif self.coeffs[i]==1: r+=self.vname else: if type(self.coeffs[i])==type(0): r+=repr(self.coeffs[i])+'*'+self.vname else: r+='('+repr(self.coeffs[i])+')*'+self.vname if i+self.val<0: r+='**('+str(i+self.val)+')' if i+self.val>1: r+='**'+str(i+self.val) return r def __str__(self): r='lpol('+str(self.coeffs)+','+str(self.val)+",'"+str(self.vname)+"')" return r def value(self,x): """evaluates a polynomial. """ if self.coeffs==[]: return 0 y=0 for i in range(len(self.coeffs)): y=x*y+self.coeffs[-i-1] if self.val<0 and x in [-1,1]: return y*x**(-self.val) else: return y*x**self.val def __neg__(self): return lpol([-c for c in self.coeffs],self.val,self.vname) def __eq__(self,f): if self.coeffs==[]: return f==0 elif type(self)==type(f) and self.__class__==f.__class__: return self.coeffs==f.coeffs and self.val==f.val and self.vname==f.vname elif type(f)==type(self.coeffs[0]): return len(self.coeffs)==1 and self.val==0 and self.coeffs[0]==f else: return False def __ne__(self,f): return not self==f #def __div__(self,f): # division by power of variable, python3: truediv # if f.coeffs==[1]: # return lpol(self.coeffs,self.val-f.val,self.vname) # else: # return False def __radd__(self,scal): # scalar addition on left if scal==0: return self if self.coeffs==[]: return scal f=self.coeffs[:] if self.val<0: if len(self.coeffs)+self.val<=0: f+=(-len(self.coeffs)-self.val)*[0] f.append(scal) else: f[-self.val]+=scal return lpol(f,self.val,self.vname) elif self.val==0: f[0]+=scal return lpol(f,self.val,self.vname) else: f=self.val*[0]+f f[0]=scal return lpol(f,0,self.vname) def __add__(self,f): if type(f)!=type(self): return f+self if self.__class__==f.__class__ and self.vname!=f.vname: print("# Warning: different variables !") return False if f.val<=self.val: ng=(self.val-f.val)*[0]+self.coeffs if len(f.coeffs)>len(ng): ng.extend((len(f.coeffs)-len(ng))*[0]) for i in range(len(f.coeffs)): ng[i]+=f.coeffs[i] if all(c==0 for c in ng): return 0; else: a,e=0,len(ng) return lpol(ng,f.val,self.vname) else: nf=(f.val-self.val)*[0]+f.coeffs if len(self.coeffs)>len(nf): nf.extend((len(self.coeffs)-len(nf))*[0]) for i in range(len(self.coeffs)): nf[i]+=self.coeffs[i] if all(c==0 for c in nf): return 0; else: return lpol(nf,self.val,self.vname) def __rmul__(self,scal): # scalar multiplication from the left if scal==0: #return 0 return lpol([0],0,self.vname) else: return lpol([scal*c for c in self.coeffs],self.val,self.vname) def __mul__(self,f): if type(self)==type(f) and self.__class__==f.__class__: if self.vname==f.vname: m=len(self.coeffs)+len(f.coeffs)-1 return lpol([sum(self.coeffs[i]*f.coeffs[k-i] for i in range(m) if i>> v=lpol([1],1,"v") >>> divmod(v**2+2,-v**3*(v+v**(-1))) (-v**(-2),1) In some cases this may lead to unexpected results: >>> divmod(2*v,v+1) (0, 2*v) """ v=lpol([1],1,self.vname) r=v**(-self.val)*self #if type(g)==type(self.coeffs[0]) or type(g)==type(int(self.coeffs[0]))\ # or (type(self.coeffs[0])==type(10000000000) and type(g)==type(0)): if type(g)==type(0): if all(c%g==0 for c in r.coeffs): return (lpol([c//g for c in self.coeffs], self.val,self.vname),0) else: return False ng=v**(-g.val)*g if not all(c%ng.coeffs[-1]==0 for c in r.coeffs): return False quot=0 while type(r)==type(ng) and r.degree>=ng.degree: q=(r.coeffs[-1]//ng.coeffs[-1])*v**(r.degree-ng.degree) r-=q*ng quot+=q #if not self==v**(self.val-g.val)*quot*g+v**self.val*r: # print("mist") # return "mist" return (v**(self.val-g.val)*quot,v**self.val*r) def __rfloordiv__(self,f): if f==1: return self**(-1) else: return False # end of definition of class lpol v=lpol([1],1,"v") # polynomial v #F cyclpol def cyclpol(n,u): """returns the n-th cyclotomic polynomial in the variable u. """ if n==1: return u-1 m=n//2 while n%m!=0: m-=1 f=divmod(u**n-1,u**m-1)[0] for d in range(1,n): if n%d==0 and m%d!=0: f=divmod(f,cyclpol(d,u))[0] return f #F cyclpol5 def cyclpol5(u): """returns those cyclotomic polynomials over the number field generated by (1+sqrt(5))/2 which are relevant for Coxeter groups of type H_3 and H_4. """ ir5=ir(5) return {'5a':u**2 +ir5*u+1, '5b':u**2 +(1-ir5)*u+1, '10a':u**2-ir5*u+1, '10b':u**2+(ir5-1)*u+1, '15a':u**4-ir5*u**3+ir5*u**2-ir5*u+1, '15b':u**4+(ir5-1)*u**3 +(1-ir5)*u**2+(ir5-1)*u+1, '20a':u**4-ir5*u**2+1, '20b':u**4+(ir5-1)*u**2+1, '30a':u**4+ir5*u**3+ir5*u**2+ir5*u+1, '30b':u**4+(1-ir5)*u**3+(1-ir5)*u**2+(1-ir5)*u+1} #F class-lpolmod: truncated polynomials class lpolmod: """creates a polynomial in one variable with integer coefficients, similar to 'lpol' but where all arithmetic is done modulo a given monic polynomial. The resulting python class has the following components: coeffs list of coefficients val order of the pole at 0 degree degree of the polynomial phi monic polynomial zname name for the class of the variable In particular, by working modulo a cyclotomic polynomial, this provides an arithmetic for cyclotomic integers. See also 'rootof1'. """ def __init__(self,coeffs,val,mpol,zname): self.zname=zname self.phi=mpol if all(c==0 for c in coeffs): self.coeffs=[] self.val=0 self.degree=0 self.phi=mpol self.truncated=True else: a,e=0,len(coeffs) while coeffs[a]==0: a+=1 while coeffs[e-1]==0: e-=1 self.coeffs=coeffs[a:e] self.val=val+a self.degree=val+e-1 self.truncated=False def __repr__(self): if self.coeffs==[]: return '0' r='' for i in range(len(self.coeffs)): if self.coeffs[i]!=0: if r!='' and not (type(self.coeffs[i])==type(0) and self.coeffs[i]<0): r+='+' if i+self.val==0: if type(self.coeffs[i])==type(0): r+=repr(self.coeffs[i]) else: r+='('+repr(self.coeffs[i])+')' else: if self.coeffs[i]==-1: r+='-'+self.zname elif self.coeffs[i]==1: r+=self.zname else: if type(self.coeffs[i])==type(0): r+=repr(self.coeffs[i])+'*'+self.zname else: r+='('+repr(self.coeffs[i])+')*'+self.zname if i+self.val<0: r+='**('+str(i+self.val)+')' if i+self.val>1: r+='**'+str(i+self.val) return r def __str__(self): r='lpolmod('+str(self.coeffs)+','+str(self.val)+",'"+str(self.zname)+"')" return r def truncate(self): if self.truncated==True: return self else: v=lpol([1],1,self.phi.vname) r=lpol(self.coeffs,self.val,self.phi.vname) while type(r)==type(v) and r.degree>=self.phi.degree: r-=r.coeffs[-1]*v**(r.degree-self.phi.degree)*self.phi if type(r)==type(0): return r elif r.coeffs==[]: return 0 elif r.val==0 and len(r.coeffs)==1: return r.coeffs[0] else: return lpolmod(r.coeffs,r.val,self.phi,self.zname) def value(self,x): """evaluates a truncated polynomial. """ if self.coeffs==[]: return 0 y=0 for i in range(len(self.coeffs)): y=x*y+self.coeffs[-i-1] if self.val<0 and x in [-1,1]: return y*x**(-self.val) else: return y*x**self.val def __eq__(self,f): if type(f)==type(0): if self.coeffs==[]: return f==0 else: return self.val==0 and len(self.coeffs)==1 and self.coeffs[0]==f else: if type(self)==type(f) and 'zname' in dir(f) and self.zname==f.zname: return self.coeffs==f.coeffs and self.val==f.val and self.phi==f.phi else: return False def __ne__(self,f): return not self==f def __gt__(self,f): z=self-f if type(z)==type(0): return z>0 elif z.zname[0]=='E': n=int(z.zname[2:-1]) z1=z.value(rootof1(n)**-1) if z==z1: rz=z.value(math.cos(2*math.pi/n)+math.sin(2*math.pi/n)*1j).real return rz>0 else: return False else: return False def __lt__(self,f): z=f-self if type(z)==type(0): return z>0 elif z.zname[0]=='E': n=int(z.zname[2:-1]) z1=z.value(rootof1(n)**-1) if z==z1: rz=z.value(math.cos(2*math.pi/n)+math.sin(2*math.pi/n)*1j).real return rz>0 else: return False else: return False def __ge__(self,f): return self==f or self>f def __le__(self,f): return self==f or self0 or (l[0]!=1 and l[0]!=-1): return False else: return (lpolmod([-l[0]*i for i in l[1:]],0,self.phi, self.zname).truncate())**(-n) elif len(self.coeffs)==1 and self.coeffs[0] in [-1,1]: if n%2==0: return lpolmod([1],n*self.val,self.phi,self.zname).truncate() else: return lpolmod([self.coeffs[0]],n*self.val,self.phi, self.zname).truncate() else: x=1 for i in range(n): x*=self return x def __mod__(self,d): """(Only works if d is an integer.) """ if type(d)==type(0): g=d elif type(d)==type(self) and d.val==0 and d.degree==0: g=d.coeffs[0] else: return False return lpolmod([c%g for c in self.coeffs], self.val,self.phi,self.zname).truncate() def __floordiv__(self,d): """(Only works if d is an integer.) """ if type(d)==type(0): g=d elif type(d)==type(self) and d.val==0 and d.degree==0: g=d.coeffs[0] else: return False if all(c%g==0 for c in self.coeffs): return lpolmod([c//g for c in self.coeffs], self.val,self.phi,self.zname) else: return False def __divmod__(self,f): return (self//f,self%f) def __rfloordiv__(self,f): if f==1: return self**(-1) else: return False # end of definition of class lpolmod def rootof1(n): """creates a primitive root of unity. Internally, this is done by using the 'lpolmod' function. It is not very efficient; the main reason why we have it here is because of its use for dihedral groups I_2(m) where m>6. >>> z=rootof1(4) >>> z**2 -1 >>> A=cartanmat("I8",2); A [[2, -1], [-2-E(8)+E(8)**3, 2]] # E(8) default name (as in GAP) >>> W=coxeter(A); W.cartantype [['I8', [0, 1]]] >>> a=rootof1(8); x=a-a**3; x E(8)-E(8)**3 >>> x>0 True >>> x**2 2 (Thus, x is the positive square root of 2.) See also 'lpolmod' and 'zeta5'. """ return lpolmod([1],1,cyclpol(n,lpol([1],1,'unity'+str(n))),'E('+str(n)+')') def E(n): return rootof1(n) def ir(m): if m==2: return 0 elif m==3: return 1 elif m==5: return zeta5(0,1) else: return (rootof1(2*m)+rootof1(2*m)**-1).truncate() ir5=ir(5) #F cycldec def cycldec(pol,maxe=1000): """checks if a polynomial is a product of cyclotomic polynomials, up to a constant and a power of the indeterminate. If this is the case, the function returns a triple (c,n,l) where c is the constant, n is the exponent of the indeterminate, and l is a list of pairs (e_i,m_i) such that pol = c * u^n * prod_i Phi_{e_i}(u)^{m_i}; here, Phi_e denotes the e-th cyclomotic polynomial. This triple will then be assigned to the vcyc component of pol. If no such product decomposition exists, the function returns 'False'. By default, the function only tries the polynomials Phi_e where e<=1000. One can increase this bound by setting the optional argument 'maxe' to a higher value. For example, all the Schur elements of generic Iwahori-Hecke algebras admit such a decomposition. >>> p=poincarepol(coxeter("F",4),v); p 1+4*v+9*v**2+16*v**3+25*v**4+36*v**5+48*v**6+60*v**7+71*v**8+ 80*v**9+87*v**10+92*v**11+94*v**12+92*v**13+87*v**14+80*v**15+ 71*v**16+60*v**17+48*v**18+36*v**19+25*v**20+16*v**21+9*v**22+ 4*v**23+v**24 >>> p.vcyc False >>> cycldec(p) [1, 0, [[2, 4], [3, 2], [4, 2], [6, 2], [8, 1], [12, 1]]] >>> p.vcyc [1, 0, [[2, 4], [3, 2], [4, 2], [6, 2], [8, 1], [12, 1]]] See also 'lcmcyclpol'. """ u=lpol([1],1,pol.vname) np=u**(-pol.val)*pol l=[] e=1 phi=u-1 while np.degree>0 and e<=maxe: q,r=divmod(np,phi) m=0 while r==0: np=lpol(q.coeffs,q.val,q.vname) m+=1 q,r=divmod(q,phi) if m>0: l.append([e,m]) e+=1 phi=cyclpol(e,u) if np.degree==0: pol.vcyc=[np.coeffs[0],pol.val,l[:]] return pol.vcyc else: return False #F lcmcyclpol def lcmcyclpol(pols): """returns the least common multiple of a list of polynomials which have a decomposition as returned by 'cycldec'. """ if len(pols)==1: return pols[0] q=lpol([1],1,pols[0].vname) ds=[cycldec(p) for p in pols] res=(max([c[2][-1][0] for c in ds])+1)*[0] res[0]=1 for c in ds: res[0]=intlcm(res[0],c[0]) for m in c[2]: if m[1]>res[m[0]]: res[m[0]]=m[1] p=res[0] for i in range(1,len(res)): if res[i]>0: p*=cyclpol(i,q)**res[i] return p #F interpolate polynomial def interpolatepol(v,x,y): """returns the unique polynomial in v of degree less than n which has value y[i] at x[i] for all i=0,...n-1. (Taken from the gap-library). >>> v=lpol([1],1,"v") >>> p=interpolatepol(v,[1,2,3,4],[3,2,4,1]); p 15-121/6*v+19/2*v**2-4/3*v**3 >>> y=[p.value(x) for x in [1,2,3,4]]; y [Fraction(3,1),Fraction(2,1),Fraction(4,1),Fraction(1,1)] >>> y==[3,2,4,1] True """ from fractions import Fraction a=len(x)*[0] t=y[:] for i in range(len(x)): for k in range(i)[::-1]: t[k]=Fraction(t[k+1]-t[k],x[i]-x[k]) a[i]=t[0] p=a[-1] for i in range(len(x)-1)[::-1]: p=p*(v-x[i])+a[i] if all(c.denominator==1 for c in p.coeffs): return lpol([c.numerator for c in p.coeffs],p.val,p.vname) else: return p #F isprime def isprime(n): if n==0 or n==1: return False if n<0: return isprime(-n) i=2 while i*i<=n: if n%i==0: return False i+=1 return True #F nextprime def nextprime(n): """returns the next prime number greater than a given integer. (Taken from the gap-library.) >>> nextprime(10**8) 100000007 >>> isprime(100000007) True See also 'isprime'. """ if -3==n: n=-2 elif -3>> gcdex(4,15) {'coeff3': -15, 'coeff2': -1, 'coeff1': 4, 'gcd': 1, 'coeff4': 4} (Thus, gcd(4,15)=1; we have 1=4*4+(-1)*15 and 0=(-15)*4+4*15.) """ if 0<=m: f,fm=m,1 else: f,fm=-m,-1 if 0<=n: g,gm=n,0 else: g,gm=-n,0 while g!=0: q,h,hm=f//g,g,gm g=f-q*g gm=fm-q*gm f,fm=h,hm if n==0: return {'gcd':f,'coeff1':fm,'coeff2':0,'coeff3':gm,'coeff4':1} else: return {'gcd':f,'coeff1':fm,'coeff2':(f-fm*m)//n,'coeff3':gm, 'coeff4':(0-gm*m)//n} #F idmat def idmat(rng,scalar): """returns the scalar matrix of size len(rng) with a given scalar on the diagonal. >>> idmat([0,1,2],-3) [[-3, 0, 0], [0, -3, 0], [0, 0, -3]] """ m=[len(rng)*[0] for x in rng] for x in range(len(rng)): m[x][x]=scalar return m #F transposemat def transposemat(mat): """returns the transpose of a matrix. >>> transposemat([[1,2,3],[4,5,6]]) [[1,4], [2,5], [3,6]] """ return list(map(lambda *row: list(row), *mat)) # return zip(*mat) #F flatlist def flatlist(lst): """returns the list of all elements that are contained in a given list or in any of its sublists. (Taken from the gap library.) >>> flatlist([1,[2,3],[[1,2],3]]) [1,2,3,1,2,3] >>> flatlist([]); [] """ flt=[] for elm in lst: if type(elm)!=type([]): flt.append(elm) else: flt.extend(flatlist(elm)) return flt #F flatblockmat def flatblockmat(blmat): """flattens a block matrix. """ a=[] for b in range(len(blmat)): for i in range(len(blmat[b][b])): a.append(flatlist(l[i] for l in blmat[b])) return a #F transclos def transclos(n,r): """returns the transitive closure of a relation on the integers 0,1,...,n-1 given by a list of pairs in r. """ m=[] for i in range(n): l=n*[False] l[i]=True for p in r: if p[0]==i: l[p[1]]=True m.append(l) for i in range(n): for j in range(n): if m[j][i]==1: m[j]=[m[i][k] or m[j][k] for k in range(n)] return m #F noduplicates def noduplicates(seq): """returns a new list in which all duplicates in the original list have been removed. Also works for non-hashable types. (Learned this from stackoverflow.) >>> noduplicates([[1, 1], [2, 1], [1, 2], [1, 1], [3, 1]]) [[1, 1], [2, 1], [1, 2], [3, 1]] """ seen = set() return [x for x in seq if str(x) not in seen and not seen.add(str(x))] #F permmult def permmult(p,q): """returns the composition of two permutations (acting from the right.) """ return tuple([q[i] for i in p]) #F printfunction def printfunction(f): """prints the source code of a function. >>> printfunction(transposemat) def transposemat(mat): return list(map(lambda *row: list(row), *mat)) """ import inspect print(''.join(inspect.getsourcelines(f)[0])) #F perminverse def perminverse(p): """returns the inverse of a permutation. """ np=len(p)*[0] for i in range(len(p)): np[p[i]]=i return tuple(np) #F matadd def matadd(a,b): """returns the sum of two matrices. """ return [[a[i][j]+b[i][j] for j in range(len(a[0]))] for i in range(len(a))] #F matsub def matsub(a,b): """returns the difference of two matrices. """ return [[a[i][j]-b[i][j] for j in range(len(a[0]))] for i in range(len(a))] #F matmult def matmult(a,b): """returns the matrix product of the matrices a and b. See also 'matadd' and 'scalmatmult'. """ return [[sum(row[k]*b[k][j] for k in range(len(b))) for j in range(len(b[0]))] for row in a] #F scalmatmult def scalmatmult(a,b): """multiplies a matrix b with a scalar a. """ return [[a*b[i][j] for j in range(len(b[0]))] for i in range(len(b))] #F directsummat def directsummat(a,b): """returns the matrix direct sum of the matrices a and b. >>> c=directsummat(cartanmat("A",2),cartanmat("G",2)) [[2,-1,0,0],[-1,2,0,0],[0,0,2,-1],[0,0,-3,2]] """ if a==[[]]: return b elif b==[[]]: return a else: c=idmat(range(len(a[0])+len(b[0])),0) for i in range(len(a[0])): for j in range(len(a[0])): c[i][j]=a[i][j] for i in range(len(b[0])): for j in range(len(b[0])): c[len(a[0])+i][len(a[0])+j]=b[i][j] return c #F kroneckerproduct def kroneckerproduct(mat1,mat2): """returns the Kronecker product of the matrices mat1 and mat2. If mat1 has size m times n and mat2 has size p times q, then the Kronecker product is a matrix of size m*p times n*q. >>> mat1=[[ 0,-1, 1], [-2, 0,-2]] >>> mat2=[[1,1], [0,1]] >>> kroneckerproduct(mat1,mat2) [[ 0, 0,-1,-1, 1, 1], [ 0, 0, 0,-1, 0, 1], [-2,-2, 0, 0,-2,-2], [ 0,-2, 0, 0, 0,-2]] (The program is taken from the gap library and re-written almost 1-1 in python.) """ krpr=[] for row1 in mat1: for row2 in mat2: row=[] for i in row1: row.extend([i*x for x in row2]) krpr.append(row) return krpr #F decomposemat def decomposemat(mat): """tests if a matrix can be decomposed in block diagonal form; it is assumed that mat[i][j]=0 if and only if mat[j][i]=0. The result is range(len(mat[0])) if mat can not be decomposed. Otherwise, the lists of block indices are returned. >>>d=decomposemat([[ 2, 0,-1, 0, 0], [ 0, 2, 0,-3, 0], [-2, 0, 2, 0,-1], [ 0,-1, 0, 2, 0], [ 0, 0,-1, 0, 2]] [[0, 2, 4], [1, 3]] Thus, there are two blocks, obtained by taking the submatrices with row and colum indices (0,2,4) and (1,3), respectively. """ l=list(range(len(mat[0]))) orbs=[] while l!=[]: orb=[l[0]] for o in orb: for i in l: if mat[o][i]!=0 and not i in orb: orb.append(i) for i in orb: l.remove(i) orbs.append(orb) return orbs #F determinantmat1 def determinantmat1(mat): n=len(mat[0]) if n==1: return mat[0][0] else: d=0 for k in range(n): l=list(range(n)) l.remove(k) if mat[k][0]!=0: d+=(-1)**k*mat[k][0]*determinantmat1([[mat[i][j] for j in range(1,n)] for i in l]) return d #F determinantmat def determinantmat(mat): """returns the determinant of a matrix. If all coefficients are integers, the function uses a simplified version of the algorithm for computing the elementary divisors of a matrix; in particular, it does not use fractions. In general, it uses induction on the size of the matrix and expansion along the first column. (Thus, it will work for matrices of moderate size over any commutative ring). """ a=[list(l) for l in mat] if not all(type(x)==type(0) for x in flatlist(a)): return determinantmat1(mat) n=len(mat[0]) d=1 for p in range(n-1): i=p while i=n: return 0 if i!=p: d=-d for j in range(n): x=a[p][j] a[p][j]=a[i][j] a[i][j]=x fertig=False i=p+1 while i>> cartesian([1,2],[3,4],[4,5]) [[1,3,4],[1,3,5],[1,4,4],[1,4,5],[2,3,4],[2,3,5],[2,4,4],[2,4,5]] >>> cartesian([1,2,2],[1,1,2]) [[1,1],[1,1],[1,2],[2,1],[2,1],[2,2],[2,1],[2,1],[2,2]] In the first form the argument is a comma-separated sequence l1, l2, ..., and the function returns the cartesian product of l1, l2, ... In the second form the argument is a list of lists [l1,l2,,...], and and the function returns the cartesian product of those lists. If more than two lists are given, cartesian(l1,l2,...) is the same (up to some nested bracketing) as cartesian(cartesian(l1,l2),...). >>> cartesian(cartesian([1,2],[3,4]),[4,5]) [[1,3],4],[[1,3],5],[[1,4],4],[[1,4],5],[[2,3],4], [[2,3],5],[[2,4],4],[[2,4],5]] The ordering is exactly the same as in gap. (The program is actually taken from the gap library and re-written almost 1-1 in python.) """ if len(arg)==1: return cartesian2(arg[0],len(arg[0]),[],0) else: return cartesian2(arg,len(arg),[],0) #F helppartitions def helppartitions(n,m,part,i): if n==0: part=part[:] parts=[part] elif n<=m: part=part[:] parts=[part] for l in range(2,n+1): part[i]=l parts.extend(helppartitions(n-l,l,part,i+1)) for l in range(i,i+n): part[l]=1 else: part=part[:] parts=[part] for l in range(2,m+1): part[i]=l parts.extend(helppartitions(n-l,l,part,i+1)) for l in range(i,i + n): part[l]=1 return parts #F partitions def partitions(n): """returns the list of all partitions of n. >>> partitions(5) [[1,1,1,1,1],[2,1,1,1],[2,2,1],[3,1,1],[3,2],[4,1],[5]] The ordering is exactly the same as in gap. (The program is actually taken from the gap library and re-written almost 1-1 in python.) See also 'partitiontuples'. """ return [[x for x in p if x>0] for p in helppartitions(n,n,n*[0],0)] #F dualpartition def dualpartition(mu): """returns the dual (or conjugate) partition to mu. """ if mu==[]: return [] else: return [len([l for l in mu if l>j]) for j in range(mu[0])] #F centraliser partition def centraliserpartition(n,mu): """returns the order of the centraliser of an element of cycle type of a given partition in the full symmetric group. (The program is taken from the gap library and re-written almost 1-1 in python.) """ res,last,k=1,0,1 for p in mu: res*=p if p==last: k+=1 res*=k else: k=1 last=p return res #F differencepartitions def differencepartitions(gamma,alpha): """returns a dictionary with information about the difference of two partitions (if it exists); this function is needed for the computation of character values in type B_n. It is taken almost 1-1 from the gap-chevie library. See also 'heckevalueB'. """ dp={'cc':0, 'll':0} if len(alpha)>len(gamma): return False old=[] inhook=False alpha=alpha[:] for i in range(len(alpha),len(gamma)): alpha.append(0) for i in range(len(gamma)): if alpha[i]>gamma[i]: return False new=list(range(alpha[i],gamma[i])) intsec=[r for r in old if r in new] if len(intsec)>1: return False elif len(intsec)==1: dp['ll']+=1 else: if inhook: dp['cc']+=1 dp['d']=old[0]-i inhook=False if new!=[]: inhook=True old=new if inhook: dp['cc']+=1 dp['d']=old[0]-len(gamma) return dp #F bipartitions def bipartitions(n): """returns the list of all bipartitions of n (as in gap). The ordering is different from that of partitiontuples(n,2). """ if n==0: return [[[],[]]] pm=[[] for i in range(n)] for m in range(1,n+1): pm[m-1].append([[],[m]]) for k in range(m+1,n+1): for t in pm[k-m-1]: s=[[],[m]] s[1].extend(t[1]) pm[k-1].append(s) for m in range(1,n//2+1): pm[m-1].append([[m],[]]) for k in range(m+1,n-m+1): for t in pm[k-m-1]: s=[[m],t[1]] s[0].extend(t[0]) pm[k-1].append(s) res=[] for k in range(1,n): for t in pm[n-k-1]: s=[[k],t[1]] s[0].extend(t[0]) res.append(s) res.append([[n],[]]) res.extend(pm[n-1]) return res; #F partitiontuples def partitiontuples(n,r): """returns the list of all r-tuples of partitions of n. >>>partitiontuples(3,2) [[[1,1,1],[]], [[1,1],[1]], [[1],[1,1]], [[],[1,1,1]], [[2,1],[]], [[1],[2]], [[2],[1]], [[],[2,1]], [[3],[]], [[],[3]]] The ordering is exactly the same as in gap. (The program is actually taken from the gap library and re-written almost 1-1 in python.) See also 'partitions'. """ empty={'tup':[[] for x in range(r)],'pos':(n-1)*[1]} if n==0: return [empty['tup']] pm=[[] for x in range(1,n)] for m in range(1,n//2+1): for i in range(1,r+1): s={'tup':[l[:] for l in empty['tup']],'pos':empty['pos'][:]} s['tup'][i-1]=[m] s['pos'][m-1]=i pm[m-1].append(s) for k in range(m+1,n-m+1): for t in pm[k-m-1]: for i in range(t['pos'][m-1],r+1): t1={'tup':[l[:] for l in t['tup']],'pos':t['pos'][:]} s=[m] s.extend(t['tup'][i-1]) t1['tup'][i-1]=s t1['pos'][m-1]=i pm[k-1].append(t1) res=[] for k in range(1,n): for t in pm[n-k-1]: for i in range(t['pos'][k-1],r+1): t1=[l[:] for l in t['tup']] s=[k] s.extend(t['tup'][i-1]) t1[i-1]=s res.append(t1) for i in range(1,r+1): s=[l[:] for l in empty['tup']] s[i-1]=[n] res.append(s) return res #F centralisertuple def centralisertuple(n,r,mu): """returns the order of the centraliser of an element of a given type (specified by an r-tuple of partitions mu) in the wreath product of a cyclic group of order r with the full symmetric group of degree n. (The program is taken from the gap library and re-written almost 1-1 in python.) """ res=1 for i in range(r): last,k=0,1 for p in mu[i]: res*=r*p; if p==last: k+=1 res*=k else: k=1 last=p return res #F lusztigsymbolB def lusztigsymbolB(n,vs,vt,dblpart): """returns the symbol associated with a bipartition, as defined by Lusztig, taking into account weights. In this form, the total number of entries in a symbol only depends on n and the parameters (but not on the bipartition). >>> bipartitions(2) [[[1], [1]], [[1, 1], []], [[2], []], [[], [1, 1]], [[], [2]]] >>> [lusztigsymbolB(2,1,1,pi) for pi in bipartitions(2)] [[[0, 1, 3], [0, 2]], [[0, 2, 3], [0, 1]], [[0, 1, 4], [0, 1]], [[0, 1, 2], [1, 2]], [[0, 1, 2], [0, 3]]] See also 'redlusztigsymbolB'. """ q,r=vt//vs,vt%vs a,b=dblpart[0][:],dblpart[1][:] if len(a)>len(b)+q: b.extend((len(a)-len(b)-q)*[0]) elif len(b)+q>len(a): a.extend((len(b)-len(a)+q)*[0]) while len(a)+len(b)<2*n+q: a.append(0) b.append(0) la,mu=(n+q)*[0],n*[0] for i in range(1,len(b)+1): la[i-1]=vs*(a[len(a)-i]+i-1)+r mu[i-1]=vs*(b[len(b)-i]+i-1) for i in range(len(b)+1,len(a)+1): la[i-1]=vs*(a[len(a)-i]+i-1)+r return [la,mu] #F redlusztigsymbolB def redlusztigsymbolB(vs,vt,dblpart): """similar to 'lusztigsymbolB' but now the number of entries in a symbol is as small as possible (depending on the bipartition). >>> bipartitions(2) [[[1], [1]], [[1, 1], []], [[2], []], [[], [1, 1]], [[], [2]]] >>> [redlusztigsymbolB(1,1,pi) for pi in bipartitions(2)] [[[0, 2], [1]], [[1, 2], [0]], [[2], []], [[0, 1, 2], [1, 2]], [[0, 1], [2]]] See also 'lusztigsymbolB'. """ q,r=vt//vs,vt%vs a,b=dblpart[0][:],dblpart[1][:] if len(a)>len(b)+q: b.extend((len(a)-len(b)-q)*[0]) elif len(b)+q>len(a): a.extend((len(b)-len(a)+q)*[0]) n=(len(a)+len(b)-q)//2 la,mu=(n+q)*[0],n*[0] for i in range(1,len(b)+1): la[i-1]=vs*(a[len(a)-i]+i-1)+r mu[i-1]=vs*(b[len(b)-i]+i-1) for i in range(len(b)+1,len(a)+1): la[i-1]=vs*(a[len(a)-i]+i-1)+r return [la,mu] #F ainvbipartition def ainvbipartition(n,vs,vt,bip): """returns the a-invariant of a bipartition, computed from the associated Lusztig symbol. See also 'lusztigsymbolB'. """ q,r=vt//vs,vt%vs p=[] s=lusztigsymbolB(n,vs,vt,bip) for i in s[0]: p.append(i) for i in s[1]: p.append(i) p.sort() N=(len(p)-q)//2 z0=[i*vs for i in range(N)] z0.extend([i*vs+r for i in range(N+q)]) z0.sort() return sum(i*(p[-i-1]-z0[-i-1]) for i in range(len(p))) #F displaymat def displaymat(mat,rows=[],cols=[],width=78): """displays a matrix, where the optional arguments 'rows' and 'cols' can be used to specify labels for the rows and columns. There is a further optional argument by which one can set the 'width' of the display, i.e., the maximum number of characters printed (the default value is 78 characters per line). >>> displaymat(chartable(coxeter("H",3))['irreducibles']) 1 -1 1 1 1 -1 1 -1 -1 -1 1 1 1 1 1 1 1 1 1 1 5 -1 . 1 -1 . . 1 . -5 5 1 . 1 -1 . . -1 . 5 3 -1 ir5 -1 . 1-ir5 1-ir5 . ir5 3 3 -1 1-ir5 -1 . ir5 ir5 . 1-ir5 3 3 1 ir5 -1 . -1+ir5 1-ir5 . -ir5 -3 3 1 1-ir5 -1 . -ir5 ir5 . -1+ir5 -3 4 . -1 . 1 1 -1 -1 1 -4 4 . -1 . 1 -1 -1 1 -1 4 (Values equal to 0 are printed as '.'.) See also 'displaychartable'. """ m=len(mat) n=len(mat[0]) csp=[max([len(repr(mat[i][j])) for i in range(m)]) for j in range(n)] if cols!=[]: csp=[max(len(str(cols[j])),csp[j])+1 for j in range(n)] else: csp=[csp[j]+1 for j in range(n)] if rows!=[]: maxr=max([len(str(r)) for r in rows]) else: maxr=0 co=0 cut=[0] for j in range(len(csp)): if co+csp[j]<=width-maxr: co+=csp[j] else: cut.append(j) co=csp[j] if cut[:-1]!=n: cut.append(n) for k in range(len(cut)-1): if cols!=[]: lprint(width*'-') lprint('\n') lprint(maxr*' ') for j in range(cut[k],cut[k+1]): lprint((csp[j]-len(str(cols[j])))*' '+str(cols[j])) lprint('\n') lprint(width*'-') lprint('\n') for i in range(m): if rows!=[]: lprint((maxr-len(str(rows[i])))*' '+str(rows[i])) for j in range(cut[k],cut[k+1]): if mat[i][j]==0: lprint((csp[j]-len(repr(mat[i][j])))*' '+'.') elif type(mat[i][j])==type(0): lprint((csp[j]-len(repr(mat[i][j])))*' '+str(mat[i][j])) else: lprint((csp[j]-len(repr(mat[i][j])))*' '+repr(mat[i][j])) lprint('\n') lprint('\n') return None ########################################################################## ## #Y Section 2: Coxeter groups, Cartan matrices, reflections ## #F cartanmatA def cartanmatA(n): if n==0: return [[]] else: a=[n*[0] for x in range(n)] for x in range(n): a[x][x]=2 if x0: a[x][x-1]=-1 return a #F cartanmat def cartanmat(typ,n): """returns a Cartan matrix (of finite Dynkin type) where typ is a string specifying the type. The convention is such that the (i,j)-entry of this matrix equals (e_i,e_j) 2 --------- (e_i,e_i) where e_0, e_1, ... are the simple roots and ( , ) is an invariant bilinear form. (This is the same convention as in gap-chevie.) See also 'affinecartanmat', 'directsummat' and 'cartantotype'. >>> cartanmat("A",2) [[2,-1],[-1,2]] >>> cartanmat("B",3) # diagram 0 <= 1 -- 2 [[2,-2,0],[-1,2,-1], [0,-1,2]] >>> cartanmat("C",3) # diagram 0 => 1 -- 2 [2,-1,0],[-2,2,-1],[0,-1,2]] >>> cartanmat("I5",2) # see 'zeta5' for the [[2,-ir5],[-ir5,2]] # definition of ir5 The complete list of the graphs with their labelling is as follows: 0 1 2 n-1 0 1 2 n-1 A_n o---o---o-- . . . --o B_n o=<=o---o-- . . . --o 1 o \ 3 n-1 0 1 2 n-1 D_n 2 o---o--- . . . --o C_n o=>=o---o-- . . . --o / 0 o 0 1 0 1 2 3 0 2 3 4 5 G_2 0->-0 F_4 o---o=>=o---o E_6 o---o---o---o---o 6 | o 1 0 2 3 4 5 6 0 2 3 4 5 6 7 E_7 o---o---o---o---o---o E_8 o---o---o---o---o---o---o | | o 1 o 1 0 1 0 1 2 0 1 2 3 I_2(m) o->-o H_3 o---o---o H_4 o---o---o---o m 5 5 """ if typ[0]=='A': return cartanmatA(n) if typ[0]=='B': a=cartanmatA(n) if n>=2: a[0][1]=-2 return a if typ[0]=='C': a=cartanmatA(n) if n>=2: a[1][0]=-2 return a if typ[0]=='D': if n==2: return [[2,0],[0,2]] if n>=3: a=cartanmatA(n) a[0][1]=0 a[0][2]=-1 a[1][0]=0 a[1][2]=-1 a[2][0]=-1 a[2][1]=-1 return a if typ[0]=='G': return [[ 2,-1], [-3, 2]] if typ[0]=='F': return [[ 2,-1, 0, 0], [-1, 2,-1, 0], [ 0,-2, 2,-1], [ 0, 0,-1, 2]] if typ[0]=='E' and n==6: return [[ 2, 0,-1, 0, 0, 0], [ 0, 2, 0,-1, 0, 0], [-1, 0, 2,-1, 0, 0], [ 0,-1,-1, 2,-1, 0], [ 0, 0, 0,-1, 2,-1], [ 0, 0, 0, 0,-1, 2]] if typ[0]=='E' and n==7: return [[ 2, 0,-1, 0, 0, 0, 0], [ 0, 2, 0,-1, 0, 0, 0], [-1, 0, 2,-1, 0, 0, 0], [ 0,-1,-1, 2,-1, 0, 0], [ 0, 0, 0,-1, 2,-1, 0], [ 0, 0, 0, 0,-1, 2,-1], [ 0, 0, 0, 0, 0,-1, 2]] if typ[0]=='E' and n==8: return [[ 2, 0,-1, 0, 0, 0, 0, 0], [ 0, 2, 0,-1, 0, 0, 0, 0], [-1, 0, 2,-1, 0, 0, 0, 0], [ 0,-1,-1, 2,-1, 0, 0, 0], [ 0, 0, 0,-1, 2,-1, 0, 0], [ 0, 0, 0, 0,-1, 2,-1, 0], [ 0, 0, 0, 0, 0,-1, 2,-1], [ 0, 0, 0, 0, 0, 0,-1, 2]] if typ[0]=='H' and n==3: return [[ 2,-ir(5), 0], [-ir(5), 2, -1], [ 0, -1, 2]] if typ[0]=='H' and n==4: return [[ 2,-ir(5), 0, 0], [-ir(5), 2, -1, 0], [ 0, -1, 2, -1], [ 0, 0, -1, 2]] if typ[0]=='I': m=int(typ[1:]) if m%2==0: if m==4: return [[2, -1], [-2, 2]] elif m==6: return [[2, -1], [-3, 2]] else: return [[ 2, -1], [-2-ir(m//2), 2]] else: if m==3: return [[2, -1], [-1, 2]] elif m==5: return [[2, -ir(m)], [-ir(m), 2]] else: d=gcdex(2+m,2*m)['coeff1'] z=rootof1(m) if d%2==0: z1=-z**d-z**(-d) else: z1=z**d+z**(-d) return [[2,z1], [z1,2]] #F affinecartanmat def affinecartanmat(typ,n): """returns a generalised Cartan matrix of affine type, where typ is a string specifying the corresponding finite type. >>> affinecartanmat("A",1) [[2,-2],[-2,2]] >>> affinecartanmat("G",2) # diagram 0 -- 1 >= 2 [[ 2,-1, 0], [-1, 2,-1], [ 0,-3, 2]] >>> affinecartanmat("F",4) # diagram 4 -- 0 -- 1 => 2 -- 3 [[ 2,-1, 0, 0, 0], [-1, 2,-1, 0, 0], [ 0,-1, 2,-1, 0], [ 0, 0,-2, 2,-1], [ 0, 0, 0,-1, 2]] >>> affinecartanmat("B",3) # diagram 0 <= 2 -- 3 [[ 2,-2, 0, 0], | [-1, 2,-1,-1], 4 [ 0,-1, 2, 0], [ 0,-1, 0, 2]] >>> affinecartanmat("C",3) # diagram 0 => 1 -- 2 <= 3 [[ 2,-1, 0, 0], [-2, 2,-1, 0], [ 0,-1, 2,-2], [ 0, 0,-1, 2]] See also 'cartanmat'. """ a=cartanmat(typ,n+1) if typ[0]=='A' and n==1: a[0][1]=-2 a[1][0]=-2 return a if typ[0]=='A' and n>=2: a[0][n]=-1 a[n][0]=-1 return a if typ[0]=='B' and n>=3: a[n-1][n]=0 a[n][n-1]=0 a[n-2][n]=-1 a[n][n-2]=-1 return a if typ[0]=='C' and n>=2: a[n-1][n]=-2 a[n][n-1]=-1 return a if typ[0]=='D' and n>=4: a[n-1][n]=0 a[n][n-1]=0 a[n-2][n]=-1 a[n][n-2]=-1 return a if typ[0]=='G': return [[ 2,-1, 0], [-1, 2,-1], [ 0,-3, 2]] if typ[0]=='F': return [[ 2,-1, 0, 0,-1], [-1, 2,-1, 0, 0], [ 0,-2, 2,-1, 0], [ 0, 0,-1, 2, 0], [-1, 0, 0, 0, 2]] if typ[0]=='E' and n==6: return [[ 2, 0,-1, 0, 0, 0, 0], [ 0, 2, 0,-1, 0, 0,-1], [-1, 0, 2,-1, 0, 0, 0], [ 0,-1,-1, 2,-1, 0, 0], [ 0, 0, 0,-1, 2,-1, 0], [ 0, 0, 0, 0,-1, 2, 0], [ 0,-1, 0, 0, 0, 0, 2]] if typ[0]=='E' and n==7: return [[ 2, 0,-1, 0, 0, 0, 0,-1], [ 0, 2, 0,-1, 0, 0, 0, 0], [-1, 0, 2,-1, 0, 0, 0, 0], [ 0,-1,-1, 2,-1, 0, 0, 0], [ 0, 0, 0,-1, 2,-1, 0, 0], [ 0, 0, 0, 0,-1, 2,-1, 0], [ 0, 0, 0, 0, 0,-1, 2, 0], [-1, 0, 0, 0, 0, 0, 0, 2]] if typ[0]=='E' and n==8: return [[ 2, 0,-1, 0, 0, 0, 0, 0, 0], [ 0, 2, 0,-1, 0, 0, 0, 0, 0], [-1, 0, 2,-1, 0, 0, 0, 0, 0], [ 0,-1,-1, 2,-1, 0, 0, 0, 0], [ 0, 0, 0,-1, 2,-1, 0, 0, 0], [ 0, 0, 0, 0,-1, 2,-1, 0, 0], [ 0, 0, 0, 0, 0,-1, 2,-1, 0], [ 0, 0, 0, 0, 0, 0,-1, 2,-1], [ 0, 0, 0, 0, 0, 0, 0,-1, 2]] #F typecartanmat def typecartanmat(mat): """identifies the type of an indecomposable Cartan matrix. """ n=len(mat[0]) if n==0: return ['A',[]] if n==1: return ['A',[0]] if n==2: if mat[0][1]*mat[1][0]<4: if mat[1][0]==-3: return ['G',[0,1]] if mat[0][1]==-3: return ['G',[1,0]] if mat[1][0]==-2: return ['C',[0,1]] if mat[0][1]==-2: return ['C',[1,0]] if mat[0][1]==-1 and mat[1][0]==-1: return ['A',[0,1]] if mat[0][1]==-ir(5) and mat[1][0]==-ir(5): return ['I5',[0,1]] #m=3 #while mat[0][1]!=-ir(m): m+=1 p=[[-1,mat[0][1]],[-mat[1][0],mat[0][1]*mat[1][0]-1]] p1=[[-1,mat[0][1]],[-mat[1][0],mat[0][1]*mat[1][0]-1]] m=1 while p1[0][0]!=1 or p1[0][1]!=0 or p1[1][0]!=0 or p1[1][1]!=1: p1=matmult(p1,p) m+=1 if m%2==0 and mat[1][0]==-1: return ['I'+str(m),[1,0]] else: return ['I'+str(m),[0,1]] else: return ['U',[0,1]] else: typ=['A','B','C','D'] cs=[cartanmat('A',n),cartanmat('B',n),cartanmat('C',n),cartanmat('D',n)] if n==3: typ.append('H') cs.append(cartanmat('H',3)) if n==4: typ.append('F') cs.append(cartanmat('F',4)) typ.append('H') cs.append(cartanmat('H',4)) if n==6: typ.append('E') cs.append(cartanmat('E',6)) if n==7: typ.append('E') cs.append(cartanmat('E',7)) if n==8: typ.append('E') cs.append(cartanmat('E',8)) if mat in cs: return [typ[cs.index(mat)],range(n)] nb=[[j for j in range(n) if i!=j and mat[i][j]!=0] for i in range(n)] es=[i for i in range(n) if len(nb[i])==1] # end nodes if len(es)==0: # circle return ['U',range(n)] elif len(es)==2: # straight line p=[es[0]] for s in p: for j in nb[s]: if not j in p: p.append(j) a=[[mat[i][j] for j in p] for i in p] if a in cs: return [typ[cs.index(a)],p] else: p.reverse() a=[[mat[i][j] for j in p] for i in p] if a in cs: return [typ[cs.index(a)],p] else: return ["U",range(n)] elif len(es)==3: # three end nodes p0=[es[0]] for s in p0: if len(nb[s])!=3: for j in nb[s]: if not j in p0: p0.append(j) p1=[es[1]] for s in p1: if len(nb[s])!=3: for j in nb[s]: if not j in p1: p1.append(j) p2=[es[2]] for s in p2: if len(nb[s])!=3: for j in nb[s]: if not j in p2: p2.append(j) ps=[p0,p1,p2] lp=[len(p0),len(p1),len(p2)] # branch lengths lp1=lp[:] lp1.sort() p=n*[0] if lp1==[2,2,n-2]: # type D or affB typ='D' if n==4: r=1 # like this it fits with embedding of D in B else: r=lp.index(n-2) for i in range(n-2): p[i+2]=ps[r][-i-1] l1=[0,1,2] l1.remove(r) p[0]=ps[l1[0]][0] p[1]=ps[l1[1]][0] if not [[mat[i][j] for j in p] for i in p] in cs: # testing return ["U",range(n)] return [typ,p] elif lp1 in [[2,3,3],[2,3,4],[2,3,5]]: # type E typ='E' r2=lp.index(2) r3=lp.index(3) p[0]=ps[r3][0] p[2]=ps[r3][1] p[1]=ps[r2][0] l1=[0,1,2] l1.remove(r2) l1.remove(r3) r=l1[0] for i in range(len(ps[r])): p[i+3]=ps[r][-i-1] if not [[mat[i][j] for j in p] for i in p] in cs: # testing return ["U",range(n)] return [typ,p] else: return ["U",range(n)] else: return ["U",range(n)] def finitetypemat(mat): """identifies the type of an indecomposable Cartan matrix, which is assumed to be of finite type. """ n=len(mat[0]) if n==0: return ['A',[]] if n==1: return ['A',[0]] if n==2: if mat[1][0]==-3: return ['G',[0,1]] if mat[0][1]==-3: return ['G',[1,0]] if mat[1][0]==-2: return ['C',[0,1]] if mat[0][1]==-2: return ['C',[1,0]] if mat[0][1]==-1 and mat[1][0]==-1: return ['A',[0,1]] if mat[0][1]==-ir(5) and mat[1][0]==-ir(5): return ['I5',[0,1]] m=3 while mat[0][1]!=-ir(m): m+=1 return ['I'+str(m),[0,1]] else: rowsums=[sum(l) for l in mat] nb=[[j for j in range(n) if i!=j and mat[i][j]!=0] for i in range(n)] es=[i for i in range(n) if len(nb[i])==1] # end nodes if len(es)==2: # straight line p=[es[0]] for s in p: for j in nb[s]: if not j in p: p.append(j) totsum=sum(rowsums) if totsum==2: return ['A',p] elif totsum<1: if mat[p[0]][p[1]]!=-1: return ['H',p] else: p.reverse() return ['H',p] else: # totsum 1 if mat[p[2]][p[1]]==-2: return ['F',p] if mat[p[1]][p[2]]==-2: p.reverse() return ['F',p] if -1 in rowsums: # type C if mat[p[1]][p[0]]==-2: return ['C',p] else: p.reverse() return ['C',p] else: if mat[p[0]][p[1]]==-2: return ['B',p] else: p.reverse() return ['B',p] else: # three end nodes p0=[es[0]] for s in p0: if len(nb[s])!=3: for j in nb[s]: if not j in p0: p0.append(j) p1=[es[1]] for s in p1: if len(nb[s])!=3: for j in nb[s]: if not j in p1: p1.append(j) p2=[es[2]] for s in p2: if len(nb[s])!=3: for j in nb[s]: if not j in p2: p2.append(j) ps=[p0,p1,p2] lp=[len(p0),len(p1),len(p2)] # branch lengths lp1=lp[:] lp1.sort() p=n*[0] if lp1==[2,2,n-2]: # type D or affB typ='D' if n==4: r=1 # like this it fits with embedding of D in B else: r=lp.index(n-2) for i in range(n-2): p[i+2]=ps[r][-i-1] l1=[0,1,2] l1.remove(r) p[0]=ps[l1[0]][0] p[1]=ps[l1[1]][0] return [typ,p] elif lp1 in [[2,3,3],[2,3,4],[2,3,5]]: # type E typ='E' r2=lp.index(2) r3=lp.index(3) p[0]=ps[r3][0] p[2]=ps[r3][1] p[1]=ps[r2][0] l1=[0,1,2] l1.remove(r2) l1.remove(r3) r=l1[0] for i in range(len(ps[r])): p[i+3]=ps[r][-i-1] return [typ,p] #F cartantotype def cartantotype(mat): """returns [['U',range(n)]] if mat is a generalised Cartan matrix which is not of finite type, where n is the rank. Otherwise, the function returns a sequence of pairs (typ,l) where typ is a string (Cartan type) and l is a list of integers such that [mat[i][j] for j in l] for i in l]==cartanmat(typ,len(l)) (exactly as in gap-chevie). Thus, for finite types, the Cartan matrix is uniquely determined by the result of cartantotype. >>> cartantotype([[ 2, 0,-1, 0, 0], [ 0, 2, 0,-3, 0], [-2, 0, 2, 0,-1], [ 0,-1, 0, 2, 0], [ 0, 0,-1, 0, 2]] [['C',[0,2,4]],['G',[3,1]]] >>> cartantotype([[ 2, -2], [-2, 2]]) [['U',[0,1]] See also 'cartantypetomat'. """ if mat==[[]]: return [['A',[]]] else: l=decomposemat(mat) t=[] for l1 in l: c=typecartanmat([[mat[i][j] for j in l1] for i in l1]) t.append([c[0],[l1[i] for i in c[1]]]) if c[0][0]=='U': # not of finite type: exit return [['U',range(len(mat[0]))]] t.sort() t.sort(key=(lambda x: len(x[1])),reverse=True) # sort as in gap-chevie for c in t: # testing if c[0][0]!='U' and c[0][0]!='I': if [[mat[i][j] for j in c[1]] for i in c[1]]!=cartanmat(c[0],len(c[1])): print("mist !") return "mist" return t #F cartantypetomat def cartantypetomat(ct): """reconstructs the Cartan matrix from its cartan type, if ct corresponds to a list of finite type Cartan matrices. Thus, if c is a Cartan matrix of finite type, then c==cartantypetomat(cartantotype(c)). This function only works for finite type. >>> cartantypetomat([['C',[0,2,4]],['G',[3,1]]]) [[ 2, 0, 1, 0, 0], [ 0, 2, 0,-3, 0], [-2, 0, 2, 0,-1], [ 0,-1, 0, 2, 0], [ 0, 0,-1, 0, 2]] """ b=[[]] p=[] for c in ct: b=directsummat(b,cartanmat(c[0],len(c[1]))) p.extend(c[1]) n=range(len(p)) return [[b[p.index(i)][p.index(j)] for j in n] for i in n] #F degreesdata def degreesdata(typ,n): """returns the reflection degrees of the finite Coxeter group of type 'typ' and rank 'n'. The data are taken from the corresponding files in gap-chevie. By Solomon's Theorem, the degrees d_1, ..., d_r (where r is the rank of W) are determined by the equation u^{d_i}-1 sum_{w in W} u^{l(w)} = prod_i ---------- u-1 where i runs over 1, ..., r. In particular, the product of all degrees is the order of W. -- When 'coxeter' is called, the resulting python class will have a component 'degrees'. (If W is not irreducible, the degrees of W are the disjoint union of the degrees of the irreducible components of W.) >>> W=coxeter("H",3) >>> W.degrees [2, 6, 10] >>> W.order 120 See also 'coxeter'. """ if typ[0]=='G': return [2,6] if typ[0]=='F': return [2,6,8,12] if typ[0]=='E' and n==6: return [2,5,6,8,9,12] if typ[0]=='E' and n==7: return [2,6,8,10,12,14,18] if typ[0]=='E' and n==8: return [2,8,12,14,18,20,24,30] if typ[0]=='A': return [i+2 for i in range(n)] if typ[0]=='B' or typ[0]=='C': return [2*(i+1) for i in range(n)] if typ[0]=='D': l=[2*(i+1) for i in range(n)] l[-1]=n return l if typ[0]=='H' and n==3: return [2,6,10] if typ[0]=='H' and n==4: return [2,12,20,30] if typ[0]=='I': return [2,int(typ[1:])] #F roots ## elms[i] applied to root[i] yields simple root (orbit rep) def roots(cmat): rng=range(len(cmat[0])) gen=idmat(rng,1) l1,elms,orbits=[],[],[] while gen!=[]: orb=[gen[0][:]] orbe=[[]] i=0 while i=0 and not nr in orb: orb.append(nr) w=orbe[i][:] w.append(s) orbe.append(w[:]) i+=1 orbits.append(orb) gen=[s for s in gen if s not in orb] l1.extend(orb) elms.extend(orbe) l=[r[:] for r in l1] l.sort(reverse=True) l.sort(key=sum) return ([tuple(r) for r in l+[[-x for x in y] for y in l]], [elms[l1.index(r)][::-1] for r in l], [[l.index(r) for r in orb] for orb in orbits]) def roots1(cmat): rng=range(len(cmat[0])) l=idmat(rng,1) elms=[[] for i in rng] i=0 while i=0 and not nr in l: l.append(nr) w=elms[i][:] w.append(s) elms.append(w[:]) i+=1 l1=[r[:] for r in l] l.sort(reverse=True) l.sort(key=sum) return ([tuple(r) for r in l+[[-x for x in y] for y in l]], [elms[l1.index(r)][::-1] for r in l]) #F permroots def permroots(cmat,roots): rng=range(len(cmat[0])) gens=[len(roots)*[0] for s in rng] for s in rng: for i in range(len(roots)): nr=list(roots[i]) nr[s]-=sum(cmat[s][t]*nr[t] for t in rng if cmat[s][t]!=0) gens[s][i]=roots.index(tuple(nr)) return [tuple(s) for s in gens] #F class-coxeter class coxeter: """creates a Coxeter group (as a python 'class'), from a Cartan matrix (see 'cartanmat') or from a pair (typ,r) where typ is a string specifying the type and r specifies the rank. >>> c=cartanmat("G",2) [[2,-1],[-3,2]] >>> W=coxeter(c) >>> W.N # number of positive roots 6 >>> W.roots # all roots [( 1,0),(0, 1),( 1, 1),( 1, 2),( 1, 3),( 2, 3), (-1,0),(0,-1),(-1,-1),(-1,-2),(-1,-3),(-2,-3)] >>> W=coxeter("H",4); W.order 14400 Alternatively, the input may in fact be any generalised Cartan matrix, leading to an infinite Coxeter group in general. >>> W=coxeter([[2,0,-1,0],[0,2,0,-1],[-1,0,2,0],[0,-3,0,2]] >>> W.cartantype [["A",[0,2]],["G",[1,3]]] >>> W=coxeter([[2,-2],[-2,2]]); W.cartantype [["U",[0,1]] # the letter "U" denotes any non-finite type If the Cartan matrix is of finite type, various more special methods are available which partly rely on explicitly known information concerning finite Coxeter groups (stored in files depending on this module), as in the gap-chevie system; see http://www.math.rwth-aachen.de/~CHEVIE for further details. (While some knowledge of gap-chevie may be useful, it is not a prerequisite for using this program.) The result of 'coxeter' always has the following attributes: cartan Cartan matrix (see also 'cartanmat') rank list [0,...,r-1] where r is the rank coxetermat Coxeter matrix cartantype see 'cartantotype' for explanation cartanname unique string identifying this group matgens matrices of the simple reflections fusions see 'reflectionsubgroup' for explanation weightL see 'ainvariants' for explanation paramL see 'heckechartable' for explanation If W is finite, there are further attributes: roots full list of all roots (as in gap-chevie) N number of positive roots roots rootorbits orbits of the action on the set of roots rootsorbits1 simple root representative for each root symform relative root lengths (symmetrizes cartan) degrees reflection degrees order order of the group permgens permutations of generators on all roots coxgens effect on simple roots For finite Coxeter groups, the basic design features are taken over from gap-chevie, with the difference that (as is common in python) lists are indexed starting with 0. In particular: the roots are numbered from 0 to 2*W.N-1, where the first W.N roots are positive; these are followed by the corresponding negative ones. We have for i in [0,..., W.N-1]: * W.roots[W.N+i]=-W.roots[i] * W.roots[i] lies in W-orbit of the k-th simple root where k=W.rootorbits1[i] Elements can be represented as reduced words, permutations or matrices, where we act from the right. (Thus, row convention is used for matrices.) If w is given as a permutation and s_i is the i-th simple reflection, then we have: l(s_iw)=l(w)+1 if and only if w[i]>> w='.'.join([str(i) for i in [0,1,4,3,2]]); w '0.1.4.3.2' >>> [int(i) for i in w.split('.')] [0, 1, 4, 3, 2] A compact representation of w in W is given as follows. For each i, let r_i be the root obtained by applying w to the i-the simple root. Let a_i be the index of r_i in the list of roots; then w is uniquely determined by (a_1, ..., a_r). Such tuples will be called 'coxelms'. For example, the matrix of w (with respect to the basis given by the simple roots) is the list [W.roots[a_1], ..., W.roots[a_r]]. If p is the complete permutation representing w, then the corresponding coxelm is p[:len(W.rank)]. Several built-in functions (as python methods in W) convert between these formats. (In Python 3, we can also pack a list of 'coxelms' into a sequence of bytes; if the list of elements is long, a few hundred thousands or millions say, then this way of storing requires roughly r bytes per element where r is the rank of W.) For infinite Coxeter groups, elements are represented either by matrices or reduced words, and the conversion functions work in the general case. >>> W=coxeter("A",3) >>> w0=longestperm(W); print w0 # the longest element (8,7,6,10,9,11,2,1,0,4,3,5) >>> W.permlength(w0) # the length of w 6 >>> W.coxelmtoword(w0) [0,1,0,2,1,0] >>> W.reducedword([0,1,0,1,2,1,0],W) [1,0,2,1,0] >>> W=coxeter([[2,-2],[-2,2]]); W.cartantype [['U',[0,1]]] >>> W.wordtomat([0,1,0,1,0,1,0,1,0,1]) ((-9,-10),(10,11)) The optional argument weightL is a list of integers specifying a weight function in the sense of Lusztig. If the argument is itself an integer, then all weights on simple reflections will be equal to that integer. The default value is 0. Similarly, the optional argument param specifies the parameter of the corresponding Iwahori-Hecke algebra; the default value is 1, in which case the Iwahori-Hecke algebra is a group algebra. The optional argument 'split' is a list defining a permutation of the generators such that the induced map is a Coxeter group automorphism. The defualt value is 'True' which corresponds to the identity automorphism. To see all available functions type 'allfunctions()'. For further theoretical background see Chapter 1 of [Ge-Pf] M. Geck and G. Pfeiffer, Characters of finite Coxeter groups and Iwahori-Hecke algebras, OUP, Oxford, 2000. """ def __init__(self,typ,rank=0,split=True,fusion=[],weightL=0,param=1): if type(typ)==type([[]]): self.cartan=typ self.rank=range(len(typ[0])) self.cartantype=cartantotype(typ) else: self.cartan=cartanmat(typ,rank) self.rank=range(rank) if rank==1: self.cartantype=[['A',[0]]] elif rank==2 and typ=='D': self.cartantype=[['A',[0]],['A',[1]]] else: self.cartantype=[[typ,range(rank)]] if self.cartantype[0][0]=='U': self.cartanname=str('U') for l in self.cartan: for i in l: if i<0: self.cartanname+=str(-i) else: self.cartanname+=str(i) else: self.cartanname='' for t in self.cartantype: self.cartanname+=t[0] self.cartanname+=str(len(t[1])) for i in t[1]: self.cartanname+='c' self.cartanname+=str(i) self.coxetermat=idmat(self.rank,1) for s in self.rank: for t in range(s): if self.cartan[s][t]==0: self.coxetermat[s][t],self.coxetermat[t][s]=2,2 elif self.cartan[s][t]*self.cartan[t][s]==1: self.coxetermat[s][t],self.coxetermat[t][s]=3,3 elif self.cartan[s][t]*self.cartan[t][s]==2: self.coxetermat[s][t],self.coxetermat[t][s]=4,4 elif self.cartan[s][t]*self.cartan[t][s]==3: self.coxetermat[s][t],self.coxetermat[t][s]=6,6 elif self.cartan[s][t]*self.cartan[t][s]==1-self.cartan[s][t]: self.coxetermat[s][t],self.coxetermat[t][s]=5,5 else: ty=typecartanmat([[self.cartan[s][s],self.cartan[t][s]], [self.cartan[s][t],self.cartan[t][t]]]) if ty[0][0]=='I': m=int(ty[0][1:]) self.coxetermat[s][t],self.coxetermat[t][s]=m,m else: self.coxetermat[s][t],self.coxetermat[t][s]='U','U' if split==True: self.split=self.rank else: self.split=split for s in self.rank: for t in range(s): if self.coxetermat[s][t]!=self.coxetermat[split[s]][split[t]]: print("#W invalid permutation of generators !") self.split=False self.fusions={self.cartanname:{'subJ':self.rank, 'parabolic':True}} if fusion!=[]: self.fusions[fusion[0]]={} for k in fusion[1].keys(): self.fusions[fusion[0]][k]=fusion[1][k] self.matgens=[] for s in self.rank: m=idmat(self.rank,1) for t in self.rank: m[t][s]-=self.cartan[s][t] self.matgens.append(tuple([tuple(t) for t in m])) if type(weightL)==type(0): self.weightfunctions=[len(self.rank)*[weightL]] else: self.weightfunctions=[weightL[:]] self.param=param if self.cartantype[0][0]!='U': self.roots,self.rootrepelms,self.rootorbits=roots(self.cartan) self.N=len(self.roots)//2 for o in self.rootorbits: o.sort() self.rootorbits1=self.N*[0] for o in range(len(self.rootorbits)): for i in self.rootorbits[o]: self.rootorbits1[i]=self.rootorbits[o][0] self.coxgens=[tuple([self.roots.index(tuple(t)) for t in m]) for m in self.matgens] self.permgens=permroots(self.cartan,self.roots) self.degrees=[] for c in self.cartantype: self.degrees.extend(degreesdata(c[0],len(c[1]))) self.degrees.sort() self.order=1 for d in self.degrees: self.order*=d b=[] for c in self.cartantype: bl=len(c[1])*[2] if c[0]=='B': bl[0]=1 if c[0]=='C': for i in range(1,len(c[1])): bl[i]=1 if c[0]=='F': bl[2]=1 bl[3]=1 if c[0]=='G': bl[0]=3 bl[1]=1 if c[0][0]=='I' and int(c[0][1:])%2==0: bl[0]=-cartanmat(c[0],len(c[1]))[1][0] bl[1]=1 b.extend(bl) p=[] for c in self.cartantype: p.extend(c[1]) self.symform=[b[p.index(i)] for i in self.rank] t1=[[self.symform[i]*self.cartan[i][j] for j in self.rank] # testing for i in self.rank] for i in self.rank: for j in self.rank: if i!=j and t1[i][j]!=t1[j][i]: print("mist !") return "mist" #if type(typ)==type([[]]): # lprint('#I type: '+str(self.cartantype)+'\n') def __repr__(self): ct=self.cartantype if (len(ct)==1 and ct[0][0]!='U' and list(ct[0][1])== list(range(len(ct[0][1])))): return "coxeter('"+ct[0][0]+"',"+str(len(ct[0][1]))+')' else: return 'coxeter('+str(self.cartan)+')' def coxelmlength(self,elm): """returns the length of a coxelm. """ l=0 idm=self.roots[:len(self.rank)] m=[self.roots[i] for i in elm] while m!=idm: s=0 while all(m[s][t]>=0 for t in self.rank): s+=1 m=[tuple([m[t][u]-self.cartan[s][t]*m[s][u] for u in self.rank]) for t in self.rank] l+=1 return l def matlength(self,mat): """returns the length of an element given as a matrix. """ l=0 idm=self.roots[:len(self.rank)] m=list(mat) while m!=idm: s=0 while all(m[s][t]>=0 for t in self.rank): s+=1 m=[tuple([m[t][u]-self.cartan[s][t]*m[s][u] for u in self.rank]) for t in self.rank] l+=1 return l def permlength(self,p): """returns the length of an element given as a full permutation. (Only works for finite W.) """ return len([i for i in p[:self.N] if i>=self.N]) def coxelmtomat(self,elm): # works for perms and coxelms """converts elm (assumed to be a coxelm) into a matrix with respect to the simple reflections. This also works if elm is a full permutation on the roots. """ return tuple([self.roots[r] for r in elm[:len(self.rank)]]) def coxelmtoperm(self,elm): # works for perms and coxelms """converts a coxelm to a full permutation on the set of roots. (Only works for finite W.) """ return tuple([self.roots.index(tuple([sum([r[i]*self.roots[elm[i]][s] for i in self.rank]) for s in self.rank])) for r in self.roots]) def coxelmtoword(self,elm): # works for perms and coxelms """converts a coxelm into a reduced expression in the simple reflections. """ #m=[list(self.roots[r]) for r in elm[:len(self.rank)]] m=[self.roots[r] for r in elm[:len(self.rank)]] word=[] weiter=True while weiter: s=0 while s=0 for t in self.rank): s+=1 if s=0 for t in self.rank): s+=1 if s=0 for t in W1.rank): s+=1 if s=self.N] def rightdescentsetperm(self,pw): """returns the right descent set of an element, given as a full permutation on all roots. """ ip=perminverse(pw) return [s for s in self.rank if ip[s]>=self.N] def leftdescentsetmat(self,mw): """returns the left descent set of an element, given as a matrix with respect to the basis of simple roots. """ return [s for s in self.rank if all(mw[s][t]<=0 for t in self.rank)] def rightdescentsetmat(self,mw): """returns the right descent set of an element, given as a matrix with respect to the basis of simple roots. """ m=self.wordtomat(self.mattoword(mw)[::-1]) return [s for s in self.rank if all(m[s][t]<=0 for t in self.rank)] # end of definition of class coxeter #F rmultgenmat def rmultgenmat(W,s,m): """returns the result of multiplying an element of W on the right by a simple reflection. The element is assumed to be given as a matrix. >>> W=coxeter("B",3) >>> m=rmultgenmat(W,2,W.wordtomat([0,1])) [[-1,-1,-1],[2,1,1],[0,1,0]] >>> W.mattoword(m) [0,1,2] See also 'lmultmatgen' and 'conjgenmat'. """ nw=[list(m[x]) for x in W.rank] for t in W.rank: nw[t][s]-=sum(nw[t][u]*W.cartan[s][u] for u in W.rank if W.cartan[s][u]!=0) return nw #F lmultgenmat # is slightly faster than rmultgen def lmultgenmat(W,s,m): """returns the result of multiplying an element of W on the left by a simple reflection. The element is assumed to be given as a matrix. See also 'rmultgenmat'. """ return tuple([tuple([m[t][u]-W.cartan[s][t]*m[s][u] for u in W.rank]) for t in W.rank]) #F conjgenmat def conjgenmat(W,s,m): """conjugates an element (given as a matrix) by a generator. See also 'rmultgenmat'. """ nw=[[m[t][u]-W.cartan[s][t]*m[s][u] for u in W.rank] for t in W.rank] for t in W.rank: nw[t][s]-=sum(nw[t][u]*W.cartan[s][u] for u in W.rank if W.cartan[s][u]!=0) return tuple([tuple(l) for l in nw]) #F conjgencoxelm def conjgencoxelm(W,s,w): """conjugates an element (given as a coxelm) by a generator. """ return tuple([W.permgens[s][r] for r in [W.roots.index(tuple([W.roots[w[t]][u]- W.cartan[s][t]*W.roots[w[s]][u] for u in W.rank])) for t in W.rank]]) #F conjgenperm def conjgenperm(W,s,pw): """conjugates an element (given as a full permutation on the set of roots by a generator. """ return tuple([W.permgens[s][pw[W.permgens[s][r]]] for r in range(2*W.N)]) #F conjugacyclass def conjugacyclass(W,pw): """returns the set of all elements in a conjugacy class, ordered by increasing lenth. The argument is supposed to be a permutation and the elements in the resulting list are permutations. >>> W=coxeter("F",4) >>> [W.permtoword(w) for w in conjugacyclass(W,W.permgens[1])] #I Size of class: 12 [[1], [0], [0, 1, 0], [2, 1, 2], [0, 2, 1, 0, 2], [3, 2, 1, 2, 3], [1, 0, 2, 1, 0, 2, 1], [0, 3, 2, 1, 0, 2, 3], [1, 0, 3, 2, 1, 0, 2, 1, 3], [2, 1, 0, 3, 2, 1, 0, 2, 1, 3, 2], [1, 2, 1, 0, 3, 2, 1, 0, 2, 1, 3, 2, 1], [0, 1, 2, 1, 0, 3, 2, 1, 0, 2, 1, 3, 2, 1, 0]] See also 'conjugacyclasses' and 'involutions'. """ cl=[pw] cl1=set([bytes(pw[:len(W.rank)])]) for x in cl: for s in W.permgens: sxs=tuple([s[x[s[r]]] for r in range(2*W.N)]) bsxs=bytes(sxs[:len(W.rank)]) if not bsxs in cl1: cl.append(sxs) cl1.add(bsxs) lprint('#I Size of class: '+str(len(cl))+'\n') lc=[W.permlength(w) for w in cl] l=list(range(len(cl))) l.sort(key=(lambda i:lc[i])) return [cl[i] for i in l] #F involutions def involutions(W): """returns the list of involutions in a finite Coxeter groups (as full permutations on the roots). """ return flatlist([conjugacyclass(W,W.wordtoperm(w)) for w in conjugacyclasses(W)['reps'] if W.wordtocoxelm(2*w)==tuple(W.rank)]) #F cyclicshiftorbit def cyclicshiftorbit(W,pw): """returns the orbit of an element under cyclic shift. See also 'testcyclicshift'. """ bahn=[pw[:]] l=len([i for i in pw[:W.N] if i>=W.N]) for b in bahn: for s in W.rank: nw=tuple([W.permgens[s][b[W.permgens[s][r]]] for r in range(2*W.N)]) if len([i for i in nw[:W.N] if i>=W.N])==l and not nw in bahn: bahn.append(nw) return bahn #F testcyclicshift def testcyclicshift(W,pw): """tests if an element (given as a full permutation on the roots) has minimal length in its conjugacy class; if this is the case, True is returned. Otherwise, the function returns a pair (x,s) where x is an element and s is a simple reflection such that l(x)>> W=coxeter("F",4) >>> t=testcyclicshift(W,W.wordtoperm([0,1,2,1,0])); t [(13,32,2,9,4,29,12,7,25,3,10,16,6,0,18,15,11,22,14,19,20,21,17,23,37, 8,26,33,28,5,36,31,1,27,34,40,30,24,42,39,35,46,38,43,44,45,41,47),0] >>> W.permtoword(t[0]) [1,2,1] See also 'classpolynomials'. """ bahn=[pw[:]] l=len([i for i in pw[:W.N] if i>=W.N]) for b in bahn: for s in W.rank: nw=tuple([W.permgens[s][b[W.permgens[s][r]]] for r in range(2*W.N)]) lnw=len([i for i in nw[:W.N] if i>=W.N]) if lnw>> W=coxeter("E",6); w0=longestperm(W); W.permlength(w0) 36 >>> conjtomin(W,w0) [1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 4] See also 'cyclicshiftclass', 'testcyclicshift' and 'conjugacyclasses'. """ alt=pw[:] neu=testcyclicshift(W,alt) while neu!=True: alt=neu[0][:] neu=testcyclicshift(W,alt) return W.permtoword(alt) #F longestperm def longestperm(W,J=0): """returns the unique element of maximal length in W (where W is assumed to be finite) as a permutation of the set of all roots. If the additional argument J is specified, the function returns the longest element in the parabolic subgroup generated by J. >>> W=coxeter("F",4) >>> w0=longestperm(W) (24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, 46,47,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) >>> W.permtoword(w0) # reduced expression [0,1,0,2,1,0,2,1,2,3,2,1,0,2,1,2,3,2,1,0,2,1,2,3] >>> [W.roots[i] for i in w0] # as a matrix [(-1, 0, 0, 0), (0, -1, 0, 0), (0, 0, -1, 0), (0, 0, 0, -1)] >>> W.permtoword(longestperm(W,[1,2])) # parabolic of type C_2 [1,2,1,2] """ if J==0: J=W.rank if J==W.rank and 'longestperm' in dir(W): return W.longestperm else: p=list(range(2*W.N)) while any(p[t]=W.N: s+=1 p=[p[i] for i in W.permgens[J[s]]] if J==list(W.rank): W.longestperm=tuple(p) return W.longestperm else: return tuple(p) #F bruhatmat def bruhatmat(W,y,w): """returns true or false according to whether y is less than or equal to w in the Bruhat-Chevalley order on W. Here, y and w are assumed to be matrices. (This function also works for infinite W.) The algorithm is based on the following recursion. Let w in W. If w is not the identity, let s be any simple reflection such that l(sw)=l(w)-1. Then y <= w if and only if sy<=sw (if l(sy)=l(y)-1) or y<=sw (if l(sy)=l(y)+1). See also 'bruhatperm' and 'bruhat'. """ id=tuple([tuple(l) for l in idmat(W.rank,1)]) if y==id or y==w: return True elif w==id: return y==w else: s=0 while s=0 for t in W.rank): s+=1 if s==len(W.rank): print [y,w,s] nw=lmultgenmat(W,s,w) if any(y[s][t]<0 for t in W.rank): ny=lmultgenmat(W,s,y) return bruhatmat(W,ny,nw) else: return bruhatmat(W,y,nw) #F bruhatcoxelm def bruhatcoxelm(W,y,w): """returns true or false according to whether y is less than or equal to w in the Bruhat-Chevalley order on W. Here, y and w are assumed to be coxelms. The algorithm is based on the following recursion. Let w in W. If w is not the identity, let s be any simple reflection such that l(sw)=l(w)-1. Then y <= w if and only if sy<=sw (if l(sy)=l(y)-1) or y<=sw (if l(sy)=l(y)+1). >>> W=coxeter("H",3); >>> w=W.wordtocoxelm([0,1,0,2]) >>> b=list(filter(lambda y:bruhatcoxelm(W,y,w),redleftcosetreps(W))) >>> [W.coxelmtoword(y) for y in b] [[],[0],[1],[2], # all y in W such that y<=w [0,1],[0,2],[1,0],[1,2],[1,0,2],[0,1,0],[0,1,2],[0,1,0,2]] See also 'bruhatperm' and 'bruhatmat'. """ if y==tuple(W.rank) or y==w: return True elif w==tuple(W.rank): return y==w else: s=0 while w[s]=W.N: ny=tuple([W.roots.index(tuple([W.roots[y[t]][u]-W.cartan[s][t]* W.roots[y[s]][u] for u in W.rank])) for t in W.rank]) return bruhatcoxelm(W,ny,nw) else: return bruhatcoxelm(W,y,nw) #F bruhatperm def bruhatperm(W,x,y,lx=False,ly=False): """returns true or false according to whether y is less than or equal to w in the Bruhat-Chevalley order on W. Here, y and w are assumed to be full permutations on the roots. The lengths of y,w can be passed as optional arguments. The algorithm is based on the following recursion. Let w in W. If w is not the identity, let s be any simple reflection such that l(sw)=l(w)-1. Then y <= w if and only if sy<=sw (if l(sy)=l(y)-1) or y<=sw (if l(sy)=l(y)+1). See also 'bruhat' and 'bruhatcoxelm'. """ if x==tuple(range(2*W.N)) or x==y: return True elif y==tuple(range(2*W.N)): return x==y else: if lx==False: lx=W.permlength(x) if ly==False: ly=W.permlength(y) while lx=W.N: x=tuple([x[r] for r in W.permgens[s]]) lx-=1 y=tuple([y[r] for r in W.permgens[s]]) ly-=1 return lx==0 or (lx==ly and x==y) #F bruhat def bruhat(W,y,w): """returns true or false according to whether y is less than or equal to w in the Bruhat-Chevally order on W. This is a generic function which checks the type of y and w and then calls one of the more specialised functions, 'bruhatcoxelm', 'bruhatmat', 'bruhatperm'. The function is based on the following recursion. Let w in W. If w is not the identity, let s be any simple reflection such that l(sw)=l(w)-1. Then y <= w if and only if sy<=sw (if l(sy)=l(y)-1) or y<=sw (if l(sy)=l(y)+1). >>> W=coxeter("H",3); >>> w=W.wordtocoxelm([0,1,0,2]) >>> b=list(filter(lambda y:bruhat(W,y,w),redleftcosetreps(W))) >>> [W.coxelmtoword(y) for y in b] [[],[0],[1],[2], # all y in W such that y<=w [0,1],[0,2],[1,0],[1,2],[1,0,2],[0,1,0],[0,1,2],[0,1,0,2]] If y,w are known to be coxelms, then it is more efficient to call directly 'bruhatcoxelm(W,y,w)'. Similarly for matrices and full permutations. """ if len(y)==len(W.rank) and type(y[0])==type(0): # coxelms return bruhatcoxelm(W,y,w) elif len(y)==2*W.N: # full permutations return bruhatperm(W,y,w) elif type(y[0])==type([]): # matrices return bruhatmat(W,y,w) else: return "mist !" #F reflections def reflections(W): """returns the list of reflections of W (where W is assumed to be finite), as permutations on the set of all roots. The i-th element in this list is the reflection whose root is the i-th element of W.roots. >>> reflections(coxeter("I5",2)) >>> W=coxeter("I5",2) >>> r=reflections(W); print r [(5,2,1,4,3,0,7,6,9,8),(3,6,4,0,2,8,1,9,5,7),(9,3,7,1,5,4,8,2,6,0), (2,9,0,8,6,7,4,5,3,1),(6,5,8,7,9,1,0,3,2,4)] >>> [W.permtoword(p) for p in r] [0],[1],[0,1,0],[1,0,1],[0,1,0,1,0]] See also 'reflectionsubgroup'. """ if 'reflections' in dir(W): return W.reflections else: refl=[] for r in range(W.N): w=W.rootrepelms[r][:] w.append(W.rootorbits1[r]) w.extend(W.rootrepelms[r][::-1]) refl.append(w[:]) W.reflections=[W.wordtoperm(r) for r in refl] return W.reflections #F innerprodroots def innerprodroots(W,r1,r2): p=0 for u in W.rank: if W.roots[r1][u]!=0: for v in W.rank: if W.cartan[u][v]!=0 and W.roots[r2][v]!=0: p+=W.roots[r1][u]*W.symform[u]*W.cartan[u][v]*W.roots[r2][v] return p #F reflectionsubgroup def reflectionsubgroup(W,J): """returns the subgroup of W generated by the reflections in the list J. The result is again a Coxeter group in its own right; the embedding into W is specified in the attribute 'fusions'. Any Coxeter group has such a 'fusions' attribute; this is a nested dictionary which contains at least one entry: that of the embedding of W into itself. (This design is different from that in gap-chevie; it appears to be better suited to recursive algorithms involving various reflection subgroups.) Let H be a Coxeter group created as above, as a subgroup of some bigger group W. Then 'H.fusions' will have an entry { W.cartanname: { "subJ": list, ... }, ... } where list gives the embedding of the simple reflections of H into the set of reflections of W. If J just consists of simple reflections of W, then H is a parabolic subgroup and the list equals J; otherwise, list[i] is the index of the i-th simple reflection of the subgroup in the list of reflections of W. (Further entries of 'H.fusions' may contain information on the fusion of conjugacy classes and so on.) See also 'reflections'. >>> W=coxeter("F",4) >>> W.cartantype [["F",[0,1,2,3]] >>> W.cartanname "F4c0c1c2c3" >>> W.fusions # W only has the fusion into itself {"F4c0c1c2c3":{"subJ":[0,1,2,3], 'parabolic':True}} >>> H=reflectionsubgroup(W,[1,2,3,23]) >>> H.cartantype [["C",[0,1,2]],["A",[3]] >>> H.cartanname "C3c0c1c2A1c3" >>> H.fusions # H has the fusion into itself and into W {"C3c0c1c2A1c3":{"subJ": [0,1,2,3], "parabolic":True}, "F4c0c1c2c3" :{"subJ":[1,2,3,23], "parabolic":False}} >>> w=longestperm(H) (10,11,12,13) >>> w1=H.permtoword(w) # reduced word inside H [0,1,0,1,2,1,0,1,2,3] >>> H.reducedword(w1,W) # and now in W, using the fusion map [0,1,0,2,1,0,2,1,2,3,2,1,0,2,1,2,3,2,1,0,2,1,2,3] >>> W=coxeter(affinecartanmat("G",2)); W.cartanname "U210121032" >>> H=reflectionsubgroup(W,[1,2]); H.cartanname "G2c0c1" >>> H.fusions {"G2c0c1":{'subJ':[0,1], 'parabolic':True}, "U210121032":{'subJ':[1,2],'parabolic':False}} >>> H.reducedword([0,1],W) [1,2] Remark: If J is a subset of the set of simple reflections of W then J will not be sorted internally. So this can be used to create a copy of W with the simple reflections relabelled. >>> W=coxeter("F",4); >>> H=reflectionsubgroup(W,[1,3,0,2]) >>> H.cartantype [['F', [2,0,3,1]]] >>> H.fusions {'F4c0c1c2c3': {'parabolic': True, 'subJ': [1,3,0,2]}, 'F4c2c0c3c1': {'parabolic': True, 'subJ': [0,1,2,3]}} >>> W.cartanname 'F4c0c1c2c3' """ para=True if J==[]: cartanJ=[[]] fundJ=[] elif all(s in W.rank for s in J): fundJ=list(J[:]) # fundJ.sort() cartanJ=[[W.cartan[s][t] for t in fundJ] for s in fundJ] # reflectionsJ=[] # for r in roots(cartanJ)[0]: # if all(u>=0 for u in r): # c=len(W.rank)*[0] # for j in range(len(J)): # c[fundJ[j]]=r[j] else: para=False refls=reflections(W) gen=[] for j in J: if j>=W.N: gen.append(refls[j-W.N]) else: gen.append(refls[j]) orb=J[:] for o in orb: for g in gen: if not g[o] in orb: orb.append(g[o]) reflectionsJ=set(filter(lambda x: x=W.N,range(W.N))) & reflectionsJ)==1: fundJ.append(r) fundJ.sort() if max(flatlist(W.coxetermat))>5: mat=[[innerprodroots(W,r1,r2) for r2 in fundJ] for r1 in fundJ] cartanJ=[[(2*mat[i][j])//mat[i][i] for j in range(len(mat))] for i in range(len(mat))] else: cartanJ=[[(sum(W.roots[j])-sum(W.roots[refls[i][j]]))//sum(W.roots[i]) for j in fundJ] for i in fundJ] return coxeter(cartanJ,fusion=[W.cartanname,{'subJ':fundJ[:], 'parabolic':para}]) #F allmats def allmats(W,maxl=-1): """returns all elements of W of length at most maxl, represented as matrices. Along the computation, the function prints the number of elements of length i for i between 0 and max. This function also works for infinite W. If the argument 'maxl' is omitted and W is finite, all elements will be computed. >>> W=coxeter("A",2) >>> allmats(W) #I 1 2 2 1 #I total = 6 [[[[1,0],[0,1]]], # length 0 [((1,1),(0,-1)),((-1,0),(1,1))], # length 1 [((0,1),(-1,-1)),((-1,-1),(1,0))], # length 2 [((0,-1),(-1,0))]] # length 3 >>> W=coxeter(affinecartanmat("G",2)); >>> W.cartantype [['U',[0,1,2]]] >>> print W.cartan [[2,-1,0],[-1,2,-1],[0,-3,2]]) >>> [[W.mattoword(m) for m in l] for l in allmats(W,3)] #I 1 3 5 7 #I total = 16 [[[]], [[2],[0],[1]], [[1,0],[1,2],[2,1],[0,1],[0,2]], [[1,0,2],[2,1,0],[2,1,2],[1,2,1],[0,1,2],[0,1,0],[0,2,1]]] See also 'allcoxelms', 'allwords' and 'allelmsproperty'. """ if 'N' in dir(W): if maxl==-1: maxlen=W.N else: maxlen=min(maxl,W.N) else: maxlen=maxl if all(type(x)==type(0) for x in flatlist(W.cartan)): cryst=True else: cryst=False l=[[tuple(tuple(l) for l in idmat(W.rank,1))]] poin=[1] lprint('#I 1 ') for i in range(maxlen): nl=[] for w in l[i]: for s in W.rank: if all(w[s][t]>=0 for t in W.rank): nl.append(tuple([tuple([w[t][u]-W.cartan[s][t]*w[s][u] for u in W.rank]) for t in W.rank])) if cryst==True: l.append(list(set(nl))) else: nl1=[] for x in nl: if not x in nl1: nl1.append(x) l.append(nl1[:]) poin.append(len(l[i+1])) lprint(str(len(l[i+1]))+' ') W.allmats=[l[i] for i in range(maxlen+1)] lprint('\n') #if W.cartantype[0][0]!='U' and max!=W.N: # lprint('\n#I total = '+str(sum(poin))+'\n') return l #F allcoxelms def allcoxelms(W,maxl=-1): """returns all elements of W of length at most maxl, representated as coxelms, that is, permutations of the set of all roots where only the images of the simple roots are given. This only works for finite W. (If maxl is not specified, then all elements will be returned.) >>> W=coxeter("A",2) >>> a=allcoxelms(W); print a #I 1 2 2 1 #I total = 6 [[(0,1)], # length 0 [(3,2),(2,4)], # length 1 [(1, 5),(5,0)], # length 2 [(4,3)]] # length 3 >>> [[W.coxelmtoword(i) for i in l] for l in a]; [[[]],[[0],[1]], [[1, 0], [0, 1]], [[0, 1, 0]]] (Use 'flatlist' to create one long list of the elements.) See also 'allmats, 'allelmchain' and 'allelmsproperty'. """ if maxl==-1: maxlen=W.N else: maxlen=min(maxl,W.N) l=[[tuple(W.rank)]] poin=[1] ll=longestperm(W) lprint('#I 1 ') for i in range(maxlen): if i>> W=coxeter("A",4) >>> a=redleftcosetreps(W,[0,1,2]) #I total = 5 >>> a [(0,1,2,3), (0,1,6,13), (0,8,16,2), (9,18,1,2), (19,0,1,2)] >>> [W.coxelmtoword(c) for c in a] [[], [3], [2,3], [1,2,3], [0,1,2,3]] See also 'redrightcosetreps' and 'redinrightcoset'. """ l,ol=[tuple(W.rank)],[tuple(W.rank)] while len(ol)>0: nl=[] for w in ol: for s in W.rank: if w[s]0: l.extend(list(ol)) #if J!=[]: # lprint('#I total = '+str(len(l))+'\n') return l #F redrightcosetreps def redrightcosetreps(W,H): """returns the list of distinguished right coset representatives of the reflection subgroup H of W. An element w in W belongs to this set if and only if w has minimal length in the coset H*w or, equivalently, w sends each simple root of H to a positive root of W. The elements are returned as coxelms, ordered by increasing length. >>> W=coxeter("A",4) >>> a=redrightcosetreps(W,[0,1,2]) [(0,1,2,3), (0,1,6,13), (0,5,3,16), (4,2,3,18), (1,2,3,19)] #I total = 5 >>> [W.coxelmtoword(c) for c in a] [[], [3], [3,2], [3,2,1], [3,2,1,0]] >>>W=coxeter("F",4); >>> H=reflectionsubgroup(W,[3,1,2,W.N-1]);H.cartantype; [['C',[0,1,2]], ['A',[3]]] # non-parabolic >>> [W.coxelmtoword(p) for p in redrightcosetreps(W,H)] #I total = 12 [[],[0],[0,1],[0,1,2],[0,1,2,3],[0,1,2,1],[0,1,2,1,3],[0,1,2,1,0], [0,1,2,1,0,3],[0,1,2,1,3,2],[0,1,2,1,0,3,2],[0,1,2,1,3,2,1]] See also 'redinrightcoset' and 'redleftcosetreps'. """ if H.fusions[W.cartanname]['parabolic']: l,ol=[tuple(W.rank)],[tuple(W.rank)] else: l,ol=[tuple(range(2*W.N))],[tuple(range(2*W.N))] ol1=[] J=H.fusions[W.cartanname]['subJ'] while len(ol)>0: nl=[] for w in ol: for s in W.rank: nw=tuple([W.permgens[s][i] for i in w]) if not nw in ol1: if all(nw[u]0: l.extend(list(ol)) #if J!=[]: # lprint('#I total = '+str(len(l))+'\n') return [p[:len(W.rank)] for p in l] #F redinrightcoset def redinrightcoset(W,H,w): """returns the unique reduced element in the coset H*w; this is defined by the condition that it sends all simple roots of H to positive roots in W. Here, w can be either a coxelm or a full permutation on the roots, and the output will be of the same type. >>> W=coxeter("F",4) >>> H=reflectionsubgroup(W,[0,1,8,15]); H.cartantype [['D', [1,2,0,3]]] # non-parabolic >>> W.coxelmtoword(redinrightcoset(W,H,W.wordtocoxelm([2,1,2,3,2,1]))) [3,2] See also 'redrightcosetreps'. """ J=H.fusions[W.cartanname]['subJ'] refls=reflections(W) if len(w)==len(W.rank): nw=W.coxelmtoperm(w) l=len(W.rank) else: nw=w[:] l=2*W.N while any(nw[u]>=W.N for u in J): s=0 while nw[J[s]]>> W=coxeter("A",5) >>> a=allelmchain(W,depth=3) #I total = 6 #I total = 5 [[[],[0],[1],[2],[0,2],[2,1],[0,1],[1,2],[1,0],[0,1,0],[0,1,2], [1,0,2],[1,2,1],[2,1,0],[0,2,1],[1,2,1,0],[0,1,2,1],[1,0,2,1], [0,1,0,2],[0,2,1,0],[0,1,0,2,1],[0,1,2,1,0],[1,0,2,1,0], [0,1,0,2,1,0]], [[],[3],[3,2],[3,2,1],[3,2,1,0]], [[],[4],[4,3],[4,3,2],[4,3,2,1],[4,3,2,1,0]]] Here, the 24 elements of a[0] are all the elements of the parabolic subgroup generated by 0,1,2 (type A_3); then a[1] contains the right coset representatives of the latter subgroup inside the parabolic subgroup generated by 0,1,2,3 (type A_4); finally, a[2] contains the right coset representatives of the latter subgroup inside the whole group. Thus, { x + y + z | x in a[0], y in a[1], z in a[2] } contains each element of W exactly once (where, as usual in python, the '+' denotes concatenation of words). Furthermore, each reduced expression w=x+y+z obtained as above is equal to W.reducedword(w,W). See also 'redrightcosetreps' and 'allelmsproperty'. """ if len(W.rank)<=depth: el=[[W.coxelmtoword(c) for c in redrightcosetreps(W, reflectionsubgroup(W,[]))]] else: el=[[W.coxelmtoword(c) for c in redrightcosetreps(W, reflectionsubgroup(W,W.rank[:-1]))]] for k in range(len(W.rank)-depth-1): J=W.rank[:-k-1] W1=coxeter([[W.cartan[i][j] for j in J] for i in J]) el.append([W1.coxelmtoword(c) for c in redrightcosetreps(W1, reflectionsubgroup(W1,J[:-1]))]) W2=coxeter([[W.cartan[i][j] for j in range(depth)] for i in range(depth)]) el.append([W2.coxelmtoword(c) for c in redrightcosetreps(W2, reflectionsubgroup(W2,[]))]) size=1 for l in el: size*=len(l) if size!=W.order: print("mist !") return "mist" return el[::-1] #F allelmchain1 def allelmchain1(W): """returns a sequence (corresponding to a chain of parabolic subgroups) of lists of distinguished left coset representatives where, at each step in the chain, the rank decreases by 1 and the chain decreases until rank 3 is reached. (More precisely, we successfully remove the last simple reflection at each step.) This can be used to run through all elements of W (where W is finite) without constructing a complete list. The various left coset representatives are all expressed as reduced words in the simple reflections of W. This works efficiently even for groups of relatively large rank, e.g., type E_8. >>> W=coxeter("A",5) >>> a=allelmchain(W) #I total = 6 #I total = 5 #I total = 24 [[[],[4],[3,4],[2,3,4],[1,2,3,4],[0,1,2,3,4]], [[],[3],[2,3],[1,2,3],[0,1,2,3]], [[],[0],[1],[2],[0,1],[2,1],[0,2],[1,2],[1,0],[0,1,0],[0,1,2],[1,0,2], [1,2,1],[2,1,0],[0,2,1],[1,2,1,0],[0,1,2,1],[1,0,2,1],[0,1,0,2], [0,2,1,0],[0,1,0,2,1],[0,1,2,1,0],[1,0,2,1,0],[0,1,0,2,1,0]]] Here, the 6 elements of a[0] are the left coset representatives of the parabolic subgroup generated by 0,1,2,3 (of type A_4); then a[1] contains the 5 left coset representatives of the parabolic subgroup generated by 0,1,2 in the previous one; finally, a[2] contains all 24 elements of the parabolic subgroup generated by 0,1,2. Thus, { x + y + z | x in a[0], y in a[1], z in a[2] } contains each element of W exactly once (where, as usual in python, the '+' denotes concatenation of words). See also 'allcoxelms', 'allmats' and 'redleftcosetreps'. """ if len(W.rank)<=3: el=[[W.coxelmtoword(c) for c in redleftcosetreps(W)]] else: el=[[W.coxelmtoword(c) for c in redleftcosetreps(W,W.rank[:-1])]] for k in range(len(W.rank)-4): J=W.rank[:-k-1] W1=coxeter([[W.cartan[i][j] for j in J] for i in J]) el.append([W1.coxelmtoword(c) for c in redleftcosetreps(W1,J[:-1])]) W2=coxeter([[W.cartan[i][j] for j in [0,1,2]] for i in [0,1,2]]) el.append([W2.coxelmtoword(c) for c in redleftcosetreps(W2)]) size=1 for l in el: size*=len(l) if size!=W.order: print("mist !") return "mist" return el #F allelmsproperty def allelmsproperty(W,f,elm="word",pr=True): """returns all elements (as reduced expressions in the generators) for which a given function f returns True. Here, f takes as argument one group element of W, given as a reduced expression. This function uses 'allelmchain' so that it is possible to run even through all elements of very large groups without first constructing a complete list of all elements. >>> W=coxeter("A",2) >>> allelmsproperty(W,lambda x:W.wordtocoxelm(2*x)==(0,1)) #I Number of elements = 3 [[], [0], [1], [0, 1, 0]] # all elements of order <=2 >>> W=coxeter("B",2) >>> allelmsproperty(W,lambda x: set(x)==set([0,1])) #I Number of elements = 5 [[0, 1], [1, 0], [0, 1, 0], [1, 0, 1], [0, 1, 0, 1]] # all elements whose reduced expressions contain all generators See also 'allelmchain'. """ if len(W.rank)<=5: a=(5-len(W.rank))*[[[]]]+allelmchain(W) else: a=allelmchain(W,depth=len(W.rank)-4) if pr==True and len(a[0])>1: lprint('#I ('+str(len(a[4]))+') ') el=[] for i4 in a[4]: if pr==True and len(a[0])>1: lprint('+') for i3 in a[3]: for i2 in a[2]: e1=i2+i3+i4 el1=[] for i1 in a[1]: for i0 in a[0]: e=i0+i1+e1 if f(e)==True: el1.append(e) el.extend(el1) if pr==True and len(a[0])>1: lprint('\n') if pr==True: lprint('#I Number of elements = '+str(len(el))+'\n') return el #F allwords def allwords(W,maxl=-1): """returns all elements of a finite Coxeter group, as reduced words, up to a given length. If the optional argument maxl is not specified, then all elements will be returned. Each w in the resulting list is normalised in the sense that w=W.reducedword(w,W). This function is one of the fastest ways to obtain a complete list of all elements of W. (It uses a call to 'allelmchain'.) >>> W=coxeter("E",7);a=timer(allwords,W) 8.65 # time in seconds >>> len(a) 2903040 See also 'allbitcoxelms', 'allelmchain', 'allelmsproperty' and 'allmats'. """ if maxl==-1: maxlen=W.N else: maxlen=min(maxl,W.N) if len(W.rank)<=5: a=(5-len(W.rank))*[[[]]]+allelmchain(W) else: a=allelmchain(W,depth=len(W.rank)-4) el=[] if maxlen==W.N: for i4 in a[4]: for i3 in a[3]: for i2 in a[2]: e1=i2+i3+i4 el1=[] for i1 in a[1]: for i0 in a[0]: el1.append(i0+i1+e1) el.extend(el1) else: for i4 in a[4]: for i3 in a[3]: for i2 in a[2]: e1=i2+i3+i4 el1=[] for i1 in a[1]: for i0 in a[0]: e=i0+i1+e1 if len(e)<=maxlen: el1.append(e) el.extend(el1) el.sort(key=(lambda x:len(x))) return el #F allwordslength def allwordslength(W,l=-1): """returns all elements of a finite Coxeter group, as reduced words, up to a given length. If the optional argument maxl is not specified, then all elements will be returned. Each w in the resulting list is normalised in the sense that w=W.reducedword(w,W). This function is one of the fastest ways to obtain a complete list of all elements of W. (It uses a call to 'allelmchain'.) >>> W=coxeter("E",7);a=timer(allwords,W) 8.65 # time in seconds >>> len(a) 2903040 See also 'allbitcoxelms', 'allelmchain', 'allelmsproperty' and 'allmats'. """ if l==-1: maxlen=W.N else: maxlen=min(l,W.N) if len(W.rank)<=5: a=(5-len(W.rank))*[[[]]]+allelmchain(W) else: a=allelmchain(W,depth=len(W.rank)-4) el=[] for i4 in a[4]: for i3 in a[3]: for i2 in a[2]: e1=i2+i3+i4 if len(e1)<=maxlen: el1=[] for i1 in a[1]: for i0 in a[0]: e=i0+i1+e1 if len(e)==maxlen: el1.append(e) el.extend(el1) return el #F allwordstrings def allwordstrings(W,maxl=-1): """same as allwords but this functions returns strings of reduced words. >>> W=coxeter("E",7); a=timer(allwordstrings,W) 3.52 # time in seconds >>> len(a) 2903040 See also 'allwords', 'allelmsproperty' and 'allmats'. """ if len(W.rank)<=5: a=(5-len(W.rank))*[[[]]]+allelmchain(W) else: a=allelmchain(W,depth=len(W.rank)-4) sa=[] for ai in a: l=[''] for w in ai[1:]: sw='.'.join([str(i) for i in w]) sw+='.' l.append(sw) sa.append(l) el=[] for i4 in sa[4]: for i3 in sa[3]: for i2 in sa[2]: e1=i2+i3+i4 el1=[] for i1 in sa[1]: for i0 in sa[0]: e=i0+i1+e1 if e!='' and e[-1]=='.': el1.append(e[:-1]) else: el1.append(e) el.extend(el1) el.sort(key=(lambda x:len(x))) return el #F allbitcoxelms def allbitcoxelms(W,maxl=-1,pr=False): """returns all elements of a finite Coxeter group, as coxelm bit strings, up to a given length. (Only works in Python 3 and if the rank of W is <=8). If the optional argument maxl is not specified, then all elements will be returned. A coxelm bit string is the result of applying the Python function 'bytes' to a coxelm. This function is useful (or even essential) when it is necessary to deal with all elements of a large group W, e.g., type E_7 or E_8. In type E_7, all the bitcoxelms will fit into 200MB main memory; for type E_8, 40GB are required. See also 'allwords', 'allcoxelms' and 'allmats'. """ if maxl==-1: maxlen=W.N else: maxlen=min(maxl,W.N) if len(W.rank)<=5: a=(5-len(W.rank))*[[[]]]+allelmchain(W) else: a=allelmchain(W,depth=len(W.rank)-4) el=[] if pr==True: lprint('#I ') if maxlen==W.N: for i4 in a[4]: if pr==True: lprint('+') for i3 in a[3]: for i2 in a[2]: e1=i2+i3+i4 el1=[] for i1 in a[1]: for i0 in a[0]: el1.append(mybytes(i0+i1+e1)) el.extend(el1) else: for i4 in a[4]: if pr==True: lprint('+') for i3 in a[3]: for i2 in a[2]: e1=i2+i3+i4 el1=[] for i1 in a[1]: for i0 in a[0]: e=i0+i1+e1 if len(e)<=maxlen: el1.append(mybytes(e)) el.extend(el1) if pr==True: lprint('\n') el.sort(key=(lambda x:len(x))) return el #F longestcoxelm1 (for use in coxeterclasses) def longestcoxelm1(W,J): m=idmat(W.rank,1) weiter=True while weiter: s=0 while s>> coxeterclasses(coxeter("A",3)) #I 5 coxeter classes {'010': {'J': [1], 'class': 1, 'w0J': [0, 1, 2]}, '011': {'J': [1, 2], 'class': 2, 'w0J': [0, 2, 1]}, '001': {'J': [2], 'class': 1, 'w0J': [0, 1, 2]}, '000': {'J': [], 'class': 0, 'w0J': [0, 1, 2]}, '111': {'J': [0, 1, 2], 'class': 4, 'w0J': [2, 1, 0]}, '110': {'J': [0, 1], 'class': 2, 'w0J': [1, 0, 2]}, '100': {'J': [0], 'class': 1, 'w0J': [0, 1, 2]}, '101': {'J': [0, 2], 'class': 3, 'w0J': [0, 1, 2]}, 'coxclassreps': ['000', '100', '110', '101', '111']} Thus, the bit strings '010','011','001',.... correspond to the subsets [1],[1,2],[2],... of S=[0,1,2]. Representatives for the various Coxeter classes are given in 'coxclassreps'. An entry 'class':i means that the given subset belongs to the class with representative coxclassreps[i]. In the above example (type An), Coxeter classes correspond to the partitions of n+1. """ if 'coxeterclasses' in dir(W): return W.coxeterclasses W.coxeterclasses={} rest=[] for s in cartesian(len(W.rank)*[[0,1]]): Jb='' subs=[] for i in range(len(s)): if s[i]==0: Jb+='0' else: Jb+='1' subs.append(i) if len(subs)>8: w0=[subs[s] for s in w0conjugation([[W.cartan[k][l] for l in subs] for k in subs])] else: w0=longestcoxelm(W,subs) p0=list(W.rank[:]) for i in range(len(subs)): #p0[i]=w0[i]-W.N p0[subs[i]]=w0[i] W.coxeterclasses[Jb]={'J':subs[:],'w0J':p0[:]} rest.append(Jb) classes=[] numb=0 while rest!=[]: orb=[rest[0]] rest.remove(orb[0]) for subs in orb: for s in W.rank: if subs[s]=='0': l=list(subs) l[s]='1' bigsub=''.join(l) if W.coxeterclasses[bigsub]['w0J']!=W.rank: nsubs=''.join([subs[i] for i in W.coxeterclasses[bigsub]['w0J']]) if not nsubs in orb: orb.append(nsubs) rest.remove(nsubs) orb.sort(reverse=True) classes.append(orb[0]) for subs in orb: W.coxeterclasses[subs]['class']=numb numb+=1 W.coxeterclasses['coxclassreps']=classes[:] #lprint(str(len(classes))+' coxeter classes\n'); return W.coxeterclasses #F conjclassdata def conjclassdata(typ,n): """returns representatives of minimal length for all the conjugacy classes of the finite Coxeter group of type 'typ' and rank 'n'. The data are taken from the corresponding files in gap-chevie. """ reps=[] names=[] cents=[] if typ[0]=='A': for mu in partitions(n+1): w=[] i=0 for l in mu: w.extend(range(i+1,i+l)) i=i+l reps.append(w) cents.append(centraliserpartition(n+1,mu)) if n>0: names.append(str(mu)) else: names.append(' ') if typ[0]=='B' or typ[0]=='C': if n==2 and typ[0]=='B': reps=[[],[2],[2,1,2,1],[1],[2,1]] cents=[8,4,8,4,4] names=[str([[1,1],[]]),str([[1],[1]]),str([[],[1,1]]), str([[2],[]]),str([[],[2]])] elif n==2 and typ[0]=='C': reps=[[],[1],[1,2,1,2],[2],[1,2]] cents=[8,4,8,4,4] names=[str([[1,1],[]]),str([[1],[1]]),str([[],[1,1]]), str([[2],[]]),str([[],[2]])] else: reps=[] for mu in partitiontuples(n,2): w=[] i=1 for l in mu[1][::-1]: # reversed list w.extend(range(2,i+1)[::-1]) w.extend(range(1,i+l)) i=i+l for l in mu[0]: w.extend(range(i+1,i+l)) i=i+l reps.append(w) cents.append(centralisertuple(n,2,mu)) names.append(str(mu)) if typ[0]=='D': reps=[] for mu in partitiontuples(n,2): if len(mu[1])%2==0: w=[] i=1 for l in mu[1][::-1]: if i==1: w.extend(range(2,i+l)) else: w.extend(range(3,i+1)[::-1]) w.extend(range(1,i+l)) i=i+l for l in mu[0]: w.extend(range(i+1,i+l)) i=i+l if w!=[] and w[0]==2: w[0]=1 cent=centralisertuple(n,2,mu) if mu[1]==[] and all(x%2==0 for x in mu[0]): reps.append(w) cents.append(cent) names.append('['+str(mu[0])+', +]') w1=w[:] w1[0]=2 reps.append(w1) cents.append(cent) names.append('['+str(mu[0])+', -]') else: reps.append(w) cents.append(cent//2) names.append(str(mu)) if typ[0]=='G': reps=[[],[2],[1],[1,2],[1,2,1,2],[1,2,1,2,1,2]] cents=[12,4,4,6,6,12] names=[" ","~A_1","A_1","G_2","A_2","A_1+~A_1"] if typ[0]=='F': reps=[[],[1,2,1,3,2,1,3,2,3,4,3,2,1,3,2,3,4,3,2,1,3,2,3,4],[2,3,2,3], [2,1],[2,3,2,3,4,3,2,1,3,4],[3,2,4,3,2,1,3,2,4,3,2,1],[4,3], [1,2,1,4,3,2,1,3,2,3],[3,2,1,4,3,2,1,3,2,3,4,3,2,1,3,2], [3,2,4,3,2,1,3,2],[4,3,2,1],[1],[2,3,2,3,4,3,2,3,4],[1,4,3],[4,3,2], [2,3,2,1,3],[3],[1,2,1,3,2,1,3,2,3],[2,1,4],[3,2,1],[2,4,3,2,3],[1,3], [3,2],[2,3,2,3,4,3,2,1,3,2,4,3,2,1],[2,4,3,2,1,3]] cents=[1152,1152,64,36,36,96,36,36,72,72,12,96,96,12,12,16,96,96,12, 12,16,16,32,32,8] names=[" ","4A_1","2A_1","A_2","D_4","D_4(a_1)","~A_2","C_3+A_1", "A_2+~A_2","F_4(a_1)","F_4","A_1","3A_1","~A_2+A_1","C_3","A_3", "~A_1","2A_1+~A_1","A_2+~A_1","B_3","B_2+A_1","A_1+~A_1","B_2", "A_3+~A_1","B_4"] if typ[0]=='E' and n==6: reps=[[],[3,4,3,2,4,3,5,4,3,2,4,5],[1,4], [1,3,1,4,3,1,2,4,5,4,3,1,2,4,3,5,6,5,4,3,2,4,5,6],[1,3],[1,3,5,6], [3,4,3,2,4,5],[1,4,3,6],[1,4,3,2],[1,2,3,1,5,4,6,5,4,2,3,4],[3,4,2,5], [1,2,3,4,2,3,4,6,5,4,2,3,4,5],[1,3,2,5],[1,3,4,3,2,4,5,6], [1,4,6,2,3,5],[1],[1,4,6],[1,3,4,3,2,4,3,5,4,3,2,4,5],[1,4,3],[1,3,2], [1,3,2,5,6],[1,4,6,3,5],[1,3,4,2,5],[1,4,3,2,6],[1,4,2,5,4,2,3]] cents=[51840,1152,192,648,216,108,96,16,10,72,36,36,24,9,12,1440,96, 96,32,36,36,12,8,10,12] names=[" ","4A_1","2A_1","3A_2","A_2","2A_2","D_4(a_1)","A_3+A_1", "A_4","E_6(a_2)","D_4","A_5+A_1","A_2+2A_1","E_6(a_1)","E_6","A_1", "3A_1","A_3+2A_1","A_3","A_2+A_1","2A_2+A_1","A_5","D_5","A_4+A_1", "D_5(a_1)"] if typ[0]=='E' and n==7: reps=[[],[7,6,7,5,6,7,4,5,6,7,2,4,5,6,7,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3], [5,4,5,2,4,5,3,4,5,2,4,3],[7,5],[7,5,2,3],[7,6], [6,5,6,4,5,6,2,4,3,4,5,6,2,4,5,3,1,3,4,5,2,4,3,1],[7,6,4,2], [5,4,5,2,4,3],[7,5,6,2],[7,5,4,5,2,4,5,3,4,5,2,4,3,1], [7,6,7,5,6,4,5,2,4,5,3,4,5,2,4,3],[7,5,6,3],[7,6,5,4], [7,6,5,4,5,2,4,5,3,4,5,2,4,3],[5,4,2,3],[1,2,3,1,5,4,6,5,4,2,3,4], [7,6,4,1],[7,6,7,5,6,4,5,2,4,3],[1,2,3,4,2,3,4,6,5,4,2,3,4,5], [7,5,2,6,4,1],[7,6,5,4,3,1],[7,5,2,3,4,1],[3,4,2,3,4,7,6,5], [6,5,4,5,2,4,3,1],[7,6,5,4,2,3],[7,5,6,2,3,1],[7,5,4,5,2,4,3,1], [6,4,1,5,3,2],[7,6,4,2,3,1], [7,6,7,5,6,7,4,5,6,7,2,4,5,6,7,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1, 3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1], [7],[7,5,2],[7,5,4,5,2,4,5,3,4,5,2,4,3],[7,5,3], [7,6,7,5,6,7,4,5,6,7,2,4,5,6,7,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1], [7,6,7,5,6,7,4,5,6,2,4,3,4,5,2,4,3,1,3,4,2], [7,6,7,5,6,4,5,6,2,4,5,6,3,4,5,6,1,3,4,5,2,4,3], [7,6,7,5,6,7,4,5,6,7,2,4,5,6,7,3,4,5,6,7,2,4,5,6,3,1,3,4,5,2,4,3,1], [6,5,4,5,2,4,5,3,4,5,2,4,3],[7,5,6],[7,5,4,5,2,4,3],[7,5,6,2,3], [7,6,5,4,5,2,4,5,3,4,5,2,4,3,1],[7,6,4],[7,5,2,3,1], [7,6,5,6,4,5,6,2,4,3,4,5,6,2,4,5,3,1,3,4,5,2,4,3,1],[7,5,4,2,3], [7,6,4,2,1],[7,5,2,6,4],[7,5,3,6,4],[7,6,5,4,5,2,4,3,1],[6,5,4,2,3], [7,6,7,5,6,4,5,2,4,5,3,4,5,2,4,3,1],[7,6,5,4,2,3,1],[7,5,6,4,1], [3,4,2,3,4,6,5],[7,5,6,3,1],[7,6,7,5,6,4,5,2,4,3,1], [2,4,2,3,5,4,2,7,6,5,4,3,1]] cents=[2903040,46080,9216,3072,768,4320,1296,216,768,384,384,256,64, 60,288,288,144,96,72,72,24,14,32,32,18,20,48,48,24,30,2903040, 46080,9216,3072,768,4320,1296,216,768,384,384,256,64,60,288, 288,144,96,72,72,24,14,32,32,18,20,48,48,24,30] names=[" ","6A_1","4A_1''","2A_1","4A_1'","A_2","3A_2","2A_2","D_4(a_1)", "A_3+A_1'","A_3+3A_1","D_4(a_1)+2A_1","A_3+A_1''","A_4","D_4+2A_1", "D_4","E_6(a_2)","A_2+2A_1","D_6(a_2)","A_5+A_1''","A_5+A_1'","A_6", "D_5+A_1","D_6(a_1)","E_6(a_1)","D_6","A_3+A_2+A_1","D_5(a_1)+A_1", "E_6","A_4+A_2","7A_1","A_1","3A_1'","5A_1","3A_1''","D_4+3A_1", "E_7(a_4)","D_6(a_2)+A_1","2A_3+A_1","A_3+2A_1''","A_3", "D_4(a_1)+A_1","A_3+2A_1'","D_6+A_1","A_2+A_1","A_2+3A_1","A_5+A_2", "D_4+A_1","2A_2+A_1","A_5'","A_5''","E_7(a_1)","D_5","A_7","E_7", "A_4+A_1","D_5(a_1)","A_3+A_2","E_7(a_2)","E_7(a_3)"] if typ[0]=='E' and n==8: reps=[[],[8,7,8,6,7,8,5,6,7,8,4,5,6,7,8,2,4,5,6,7,8,3,4,5,6,7,8,2,4,5, 6,7,3,4,5,6,2,4,5,3,4,2,1,3,4,5,6,7,8,2,4,5,6,7,3,4,5,6,2,4,5,3,4,2, 1,3,4,5,6,7,8,2,4,5,6,7,3,4,5,6,2,4,5,3,4,2,1,3,4,5,6,7,8,2,4,5,6,3, 4,5,2,4,3,1,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1], [5,4,5,2,4,5,3,4,5,2,4,3],[6,1], [7,6,7,5,6,7,4,5,6,7,2,4,5,6,7,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3], [8,7,6,7,5,6,4,5,6,7,2,4,5,6,3,4,5,6,7,8,2,4,5,6,3,4,5,1,3,4,5,6,7,8, 2,4,5,6,7,3,4,5,6,2,4,5,3,4,2,1,3,4,5,6,7,2,4,5,3,1],[7,5,2,3],[6,7], [1,2,3,1,4,2,3,1,4,3,5,4,2,3,1,4,3,5,4,2,6,5,4,2,3,1,4,3,5,4,2,6,5,4, 3,1,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,1,7,6,5,4,2,3,4,5,6,7,8], [8,7,8,6,7,5,6,7,4,2,4,5,3,4,5,6,7,8,2,4,5,6,3,4,5,2,1,3,4,5,6,7,8,2, 4,5,6,7,3,4,5,6,2,4,5,3,4,1,3,4,5,6,7,8,2,4,5,6,7,3,4,5,6,2,4,5,3,4, 2,1,3,4,5,6,2,4,5,3,4,1], [5,6,4,3,4,5,6,7,8,2,4,5,6,3,4,2,1,3,4,5,6,7,8,2,4,5,6,7,3,4,5,2,1,3, 4,5,6,7,8,2],[6,5,2,4,5,6,3,4,5,2,4,3,1,3,4,5,6,2,4,5,3,4,1,3], [2,3,4,2,3,4,5,4,2,3,1,4,5,6,8,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,1,7,6, 5,4,2,3,4,5,6,7],[7,8,2,4],[2,4,2,5,4,2,6,5,4,2,3,4,5,6,7,6,5,4,2,3, 4,5,6,7,8,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,1,7,8],[4,2,4,3,4,5], [1,2,3,1,4,2,3,1,4,3,5,4,2,3,1,4,3,5,4,2,6,5,4,2,3,1,4,3,5,4,2,6,5,4, 3,1,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,1,7,8,7,6,5,4,2,3,4,5,6,7,8], [2,3,4,2,3,4,6,5,7,6,5,4,2,3,4,5],[3,7,6,8], [1,2,3,4,2,3,4,5,4,2,3,4,5,7],[4,5,6,7,3,4,5,2,4,3,1,3,4,5,6,7,8,2,4, 5,3,4,2,1,3,4,5,6,7,8],[7,6,8,1,4,3],[5,6,3,4], [3,4,3,5,4,3,6,5,4,3,8,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,7,6,5,4,2], [7,5,6,2,4,5,3,4,5,6,7,8,2,4,5,6,7,1,3,4,5,6,7,8,2,4,5,6,7,3,4,5,2,4, 3,1,3,4,5,6,7,8,2,4,5,6,3,4], [8,7,6,7,5,4,5,6,2,4,3,4,5,6,7,1,3,4,5,6,2,4,5,3], [8,7,5,4,5,2,4,5,3,4,5,2,4,3],[5,4,2,3],[1,2,3,1,4,2,3,1,4,5,4,2,3,1, 4,3,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,1,7,6,5,4,2,3,4,5,6,7,8,7], [7,4,5,1],[5,4,5,2,4,5,3,4,5,6,7,2,4,3], [8,7,6,7,5,2,4,3,4,5,2,4,1,3,4,5,6,2,4,5],[3,4,2,3,5,4,2,3,1,4,5,6], [4,3,5,4,2,3,4,5,6,5,4,2,3,4,5,6,7,6,5,4,2,3,4,5,6,7,8,7,6,5,4,2,3,1, 4,3,5,4,2,6,5,4,3,1,7,8],[2,3,4,2,3,4,5,4,2,3,1,4,5,6],[8,7,5,2,4,3], [7,8,5,2,3,1],[6,5,6,7,4,2,4,5,3,4], [5,4,6,5,4,2,3,7,6,5,4,2,3,8,7,6,5,4,2,3,1,4],[6,4,5,2,7,1], [8,5,6,7,2,4],[2,7,6,5,4,2,3,4,8,7,6,5,4,2,3,1,4,3],[8,5,6,2,3,4], [2,4,2,3,4,5,6,7], [2,4,2,5,4,2,3,6,5,4,2,3,4,5,6,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3,1,7,8], [5,3,4,5,6,2,4,1],[3,4,3,5,4,2,3,1,4,3,5,4,2,8,7,6], [1,3,4,3,1,5,4,2,3,4,5,8,7,6,5,4,2,3,1,4,3,5,4,2,6,5,7,6], [2,3,4,3,1,5,4,2,3,4,5,6,7,8],[6,7,8,5,2,1],[7,5,6,2,3,4], [6,7,8,5,6,7,4,5,3,4,2,1],[7,8,4,2,3,4,5,2], [1,3,1,4,5,4,3,6,5,4,2,3,1,4,3,5,4,2,7,6,5,4,2,3,1,4,3,5,4,2,6,5,4,3, 1,7,6,5,4,2,3,4,5,6,7,8], [1,2,3,4,2,3,1,4,3,5,4,2,3,1,4,5,6,5,4,2,3,4,5,6,8,7], [1,2,3,1,4,2,5,4,2,3,1,4,6,5,4,2,3,4,5,6,7,8],[6,4,1,5,3,2], [1,2,3,1,4,2,3,1,4,3,5,4,3,1,6,5,4,2,3,4,5,6,7,8],[5,7,6,2,1,3], [8,1,2,3,4,2,5,4],[8,7,6,2,4,5,3,4,1,3],[8,7,6,5,3,1], [3,4,2,3,5,4,2,3,4,6,5,4,2,3,7,6,5,4,2,3,1,4,5,6,7,8], [8,6,5,4,2,3,4,5,6,7,1,3,4,5,2,4],[7,8,6,2,1,3,4,5],[3], [7,6,7,5,6,7,4,5,6,7,2,4,5,6,7,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1,3,4,5, 6,7,2,4,5,6,3,4,5,2,4,3,1,3,4,5,6,7,2,4,5,6,3,4,5,2,4,3,1],[8,6,1], [7,5,4,5,2,4,5,3,4,5,2,4,3],[4,1,3], [2,3,4,2,3,4,5,4,2,3,4,5,6,5,4,2,3,4,5,6,7,6,5,4,2,3,4,5,6,7,8], [2,3,4,2,3,4,5,4,2,3,4,5,6],[8,5,4,2,3,4,2], [2,4,2,3,4,5,4,2,3,1,4,3,5,6,5,4,2,3,4,5,6,7,6,5,4,2,3,1,4,3,5,6,7], [2,3,4,5,4,2,3,4,5,7,6,8,7,6,5,4,2,3,4,5,6],[8,5,2,4,1],[4,2,1], [2,3,4,2,3,4,5,4,2,3,4,5,6,5,4,2,3,4,5,6,7,6,5,4,2,3,1,4,5,6,7], [8,5,6,4,5,6,3,4,5,6,2,4,3,1,3,4,5,6,2,4,5,3,4,2,1], [5,2,4,5,1,3,4,5,6,2,4,5,3,4,2,1,3,4,5,6,7],[8,5,2,3,1],[7,5,4,2,3], [8,6,5,3,1],[2,3,4,2,3,1,4,5,4,2,6,5,4,2,3,1,4,3,5,4,6,5,7], [1,2,3,1,4,2,3,5,4,2,3,1,4,3,5,4,6,5,4,2,3,4,5,6,7], [1,2,3,4,2,6,5,4,2,3,4,5,8],[7,5,2,6,4], [8,2,3,4,2,3,4,5,4,2,3,1,4,5,6],[2,3,5,1,4], [2,3,4,2,3,4,5,4,2,3,4,5,7,6,8],[2,3,5,6,5,4,2,3,4,7,6,5,4,2,3,1,4], [4,6,8,1,3,5,7],[5,6,2,4,1],[6,7,5,4,5,2,4,5,1,3,4,5,2,4,3], [2,3,4,2,3,4,5,4,2,3,1,4,5,8,7],[4,2,5,4,2,3,1],[7,6,4,2,3], [2,3,4,2,3,4,5,7,6,5,4,2,3,4,5,6,8],[1,4,2,5,4,2,3,7,8], [6,7,8,5,4,2,3],[8,6,4,1,2,3,5],[4,2,5,4,3,1,6,5,7,6,5], [8,6,7,2,4,5,1],[5,3,4,5,6,7,2,4,1],[8,5,6,3,4,2,1,3,4], [6,7,5,2,4,3,1],[8,6,7,4,1,2,3],[7,8,5,6,4,2,4,3,4],[7,8,4,5,2,3,1], [4,3,5,4,2,3,4,5,6,8,7],[8,6,7,5,2,3,1],[2,4,2,3,1,6,5,4,2,3,4,5,7]] cents=[696729600,696729600,221184,184320,184320,46080,6144,311040, 311040,155520,155520,7776,7776,2592,2592,18432,18432,1024,768, 768,192,128,1200,1200,600,600,6912,6912,1728,1152,1152,288,864, 864,432,216,288,288,72,48,28,28,64,128,128,108,108,54,54,80,80, 20,576,576,288,288,144,144,96,96,24,60,60,30,30,5806080,5806080, 18432,18432,15360,15360,4608,1536,1536,768,256,8640,8640,2592, 2592,576,576,432,432,288,288,144,144,384,384,64,16,120,120,576, 576,192,192,72,24,48,48,28,28,36,36,40,40,48,48,60,60] names=[" ","8A_1","4A_1'","2A_1","6A_1","2D_4(a_1)","4A_1''","A_2", "D_4+4A_1","4A_2","E_8(a_8)","3A_2","E_7(a_4)+A_1","2A_2","2D_4", "D_4(a_1)","2A_3+2A_1","2A_3'","A_3+A_1","A_3+3A_1","D_8(a_3)", "2A_3''","A_4","D_6+2A_1","2A_4","E_8(a_6)","A_2+4A_1","D_4", "E_6(a_2)+A_2","A_2+2A_1","D_4+2A_1","E_8(a_3)","E_6(a_2)", "A_5+A_2+A_1","A_5+A_1'","D_4+A_2","2A_2+2A_1","D_6(a_2)","D_8(a_1)", "A_5+A_1''","A_6","D_8","D_5+A_1","D_6(a_1)","A_7+A_1","E_6(a_1)", "E_7+A_1","A_8","E_8(a_4)","A_4+2A_1","D_6","E_8(a_2)", "D_4(a_1)+A_2","D_5(a_1)+A_3","E_6+A_2","E_8(a_7)","E_6", "E_7(a_2)+A_1","A_3+A_2+A_1","D_5(a_1)+A_1","E_8(a_1)","A_4+A_2", "D_8(a_2)","E_8(a_5)","E_8","A_1","7A_1","3A_1","5A_1","A_3", "A_3+4A_1","A_3+2A_1'","D_4(a_1)+A_1","2A_3+A_1","D_4(a_1)+A_3", "A_3+2A_1''","A_2+A_1","D_4+3A_1","3A_2+A_1","E_7(a_4)","A_2+3A_1", "D_4+A_1","2A_2+A_1","D_6(a_2)+A_1","A_5+A_2","E_6(a_2)+A_1","A_5", "A_5+2A_1","D_5","D_5+2A_1","A_7'","A_7''","A_4+A_1","D_6+A_1", "A_3+A_2+2A_1","D_5(a_1)","A_3+A_2","D_4+A_3","D_5(a_1)+A_2","D_7", "E_6+A_1","E_7(a_2)","A_6+A_1","E_7(a_1)","E_6(a_1)+A_1","E_7", "A_4+A_3","D_7(a_1)","D_5+A_2","D_7(a_2)","A_4+A_2+1","E_7(a_3)"] if typ[0]=='H' and n==3: reps=[[],[1],[1,2],[1,3],[2,3],[1,2,3],[1,2,1,2],[1,2,1,2,3], [1,2,1,2,3,2,1,2,3],[1,2,1,2,1,3,2,1,2,1,3,2,1,2,3]] cents=[120,8,10,8,6,10,10,6,10,120] if typ[0]=='H' and n==4: reps=[[],[1],[1,2],[1,3],[2,3],[1,2,3],[1,2,4],[1,3,4],[2,4,3], [1,2,1,2],[1,2,3,4],[1,2,1,2,3],[1,2,1,2,4],[1,2,1,2,3,4], [1,2,3,2,1,2,3,4],[1,2,1,2,3,2,1,2,3],[1,2,1,2,3,2,1,2,3,4], [1,3,2,1,2,1,3,2,1,2,3,4],[1,2,1,3,2,1,2,1,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,2,3],[1,2,1,2,1,3,2,1,2,1,3,2,1,2,3,4], [1,2,1,2,3,2,1,2,3,4,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,4,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,4,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,3,4], [1,2,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4], [2,1,2,1,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4], [1,2,1,2,3,2,1,2,1,3,2,1,4,3,2,1,2,1,3,2,1,4,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,2,4,3,2,1,2,1,3,2,1,4,3,2,1,2,3,4], [1,2,1,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4,3, 2,1,2,1,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4,3, 2,1,2,1,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,2,4,3,2,1,2,1,3,2,1,2,4,3,2,1,2,1, 3,2,1,4,3,2,1,2,3,4], [1,2,1,2,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2, 1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4], [1,2,1,2,1,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2, 1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4,3,2,1,2,1,3,2,1,2,3,4]] cents=[14400,240,100,32,36,20,20,12,8,100,30,12,20,20,30,20,12,600, 50,240,100,30,20,360,36,600,50,30,240,600,100,360,600,14400] if typ[0]=='I': cl=[1] m=int(typ[1:]) if m%2==0: reps=[[],[1],[2]] cl.extend([m//2,m//2]) x=[1,2]; for i in range(1,m//2+1): reps.append(x[:]) cl.append(2) x.extend([1,2]) cl[-1]=1 else: reps=[[],[1]] cl.append(m) x=[1,2] for i in range(1,(m-1)//2+1): reps.append(x[:]); cl.append(2) x.extend([1,2]) cents=[2*m//c for c in cl] if names==[]: for w in reps: if w==[]: names.append(' ') else: nn=str(w[0]) for i in w[1:]: nn+=str(i) names.append(nn) return {'reps':[[s-1 for s in w] for w in reps], 'centralisers':cents[:],'names':names[:]} #F conjugacyclasses def conjugacyclasses(W): """returns representatives of minimal length in the conjugacy classes of W. The result is a dictionary with entries: reps list of representatives of minimal length classlengths list of sizes of conjugacy classes classnames list of tuples of names for the classes put together from the irreducible components of W The conventions are the same as in gap-chevie; in particular, the ordering of the classes is the same as in gap-chevie. The raw data for the various types of irreducible finite Coxeter groups are explicitly stored in this module and called via the function 'conjclassdata(typ,rank)'. For a general finite Coxeter group W the data are then built together from the irreducible components using 'W.cartantype'. >>> conjugacyclasses(coxeter("G",2)) {'reps' : [[],[1],[0],[0,1],[0,1,0,1],[0,1,0,1,0,1]], 'classlengths': [1,3,3,2,2,1], 'classnames' : [(' ',),('~A_1',),('A_1',),('G_2',), ('A_2',),('A_1+~A_1',)]} >>> W=coxeter([[2,0,-1,0],[0,2,0,-2],[-1,0,2,0],[0,-1,0,2]]) >>> W.cartantype [['A',[0,2]],['C',[3,1]]] >>> conjugacyclasses(W)['reps'] [[],[3],[3,1,3,1],[1],[3,1],[0],[0,3],[0,3,1,3,1],[0,1], [0,3,1],[0,2],[0,2,3],[0,2,3,1,3,1],[0,2,1],[0,2,3,1]] The representatives of the classes are ``good'' in the sense of M. Geck and J. Michel, ``Good'' elements in finite Coxeter groups and representations of Iwahori--Hecke algebras, Proc. London Math. Soc. (3) 74 (1997), 275-305. See also 'conjugacyclass' and 'conjtomin'. """ if 'conjugacyclasses' in dir(W): return W.conjugacyclasses cl=[] cent=[] nam=[] for t in W.cartantype: c=conjclassdata(t[0],len(t[1])) cl.append([[t[1][s] for s in w] for w in c['reps']]) cent.append(c['centralisers']) nam.append(c['names']) ncl=[] for w in cartesian(cl): nw=[] for l in w: nw.extend(l) ncl.append(nw) ncent=[] for cc in cartesian(cent): nc=1 for c in cc: nc*=c ncent.append(nc) nnam=[tuple(st) for st in cartesian(nam)] W.conjugacyclasses={'reps':[w[:] for w in ncl],'classnames':nnam[:], 'classlengths':[W.order//c for c in ncent]} return W.conjugacyclasses #F identifyclasses def identifyclasses(W,elms,minrep=False): """identifies the conjugacy classes to which the elements (given as reduced words) in a list belong. If it is already known that the elements have minimal length in their conjugacy classes, then the optional argument 'minrep' should be set to True. >>> W=coxeter("A",5) >>> identifyclasses(W,[W.permtoword(longestperm(W))]) [3] >>> conjugacyclasses(W)['reps'] [[],[0],[0,2],[0,2,4],[0,1],[0,1,3],[0,1,3,4],[0,1,2],[0,1,2,4], [0,1,2,3],[0,1,2,3,4]] (Thus, the longest element belongs to the conjugacy class with respresentive [0,2,4].) See also 'fusionconjugacyclasses'. """ clcl=coxeterclasses(W) clW=conjugacyclasses(W)['reps'] invW=[] for w in clW: fpw='' bw='' sw=set(w) for i in W.rank: if i in sw: bw+='1' else: bw+='0' fpw+='c' fpw+=str(clcl[bw]['class']) for o in W.rootorbits: fpw+='o' #fpw+=str(len(list(filter(lambda i:i in o,w)))) fpw+=str(len([i for i in w if i in o])) invW.append(fpw) invH=[] if minrep==False: elms1=[conjtomin(W,W.wordtoperm(w)) for w in elms] else: elms1=elms for w in elms1: fpw='' bw='' sw=set(w) for i in W.rank: if i in sw: bw+='1' else: bw+='0' fpw+='c' fpw+=str(clcl[bw]['class']) for o in W.rootorbits: fpw+='o' #fpw+=str(len(list(filter(lambda i:i in o,w)))) fpw+=str(len([i for i in w if i in o])) invH.append(fpw) check=[invW.count(f) for f in invH] if set(check)==set([1]): fus=[invW.index(f) for f in invH] else: lprint('#I using also traces ...') #troubleH=list(filter(lambda i:check[i]>1,range(len(elms1)))) troubleH=[i for i in range(len(elms1)) if check[i]>1] troublefp=[invH[i] for i in troubleH] #troubleW=list(filter(lambda i:invW[i] in troublefp,range(len(clW)))) troubleW=[i for i in range(len(clW)) if invW[i] in troublefp] matsH=[] for i in troubleH: mm=W.wordtomat(elms1[i]) matsH.append([list(l) for l in mm]) matsW=[] for i in troubleW: mm=W.wordtomat(clW[i]) matsW.append([list(l) for l in mm]) for i in range(len(troubleH)): spur=sum([matsH[i][j][j] for j in W.rank]) if spur<0: invH[troubleH[i]]+='m' invH[troubleH[i]]+=str(-spur) else: invH[troubleH[i]]+='p' invH[troubleH[i]]+=str(spur) for i in range(len(troubleW)): spur=sum([matsW[i][j][j] for j in W.rank]) if spur<0: invW[troubleW[i]]+='m' invW[troubleW[i]]+=str(-spur) else: invW[troubleW[i]]+='p' invW[troubleW[i]]+=str(spur) newcheck=[invW.count(f) for f in invH] if set(newcheck)==set([1]): lprint(' okay now\n') fus=[invW.index(f) for f in invH] else: lprint(' and characteristic polynomials \n') j=0 while j>> W=coxeter("H",4) >>> H=reflectionsubgroup(W,[0,1,2]); print H.cartantype [['H',[0,1,2]] >>> H.fusions {'H4c0c1c2c3': {'subJ':[0,1,2], 'parabolic':True}, 'H3c0c1c2' : {'subJ':[0,1,2], 'parabolic':True}} >>> len(conjugaclasses(W)['reps']); len(conjugacyclasses(H)['reps']) 34 10 >>> f=fusionconjugacyclasses(H,W); f [0,1,2,3,4,5,9,11,15,19] >>> f==identifyclasses(W,conjugacyclasses(H)['reps'],minrep=True) True >>> H.fusions {'H4c0c1c2c3': {'classes':[0,1,2,3,4,5,9,11,15,19], 'subJ':[0,1,2],'parabolic':True}, 'H3c0c1c2' : {'subJ':[0,1,2], 'parabolic':True}} (Now H.fusions has an additional entry containing the fusion of conjugacyclasses.) >>> W=coxeter("B",6) >>> H=reflectionsubgroup(W,[1,2,3,4,5,11]) >>> H.cartantype # non-parabolic [['D',[0,5,1,2,3,4]]] >>> fusionconjugacyclasses(H,W) [0,2,4,6,7,10,11,14,15,17,19,21,23,25,26,26,28,30,33,34,37,38, 41,43,44,46,48,49,52,53,55,55,58,59,62,63,63] """ fh=H.fusions[W.cartanname] if 'classes' in fh.keys(): return fh['classes'] ch=conjugacyclasses(H) if H.cartanname==W.cartanname: fh['classes']=range(len(ch['reps'])) return fh['classes'] if fh['parabolic']==True: clH=[[fh['subJ'][s] for s in w] for w in ch['reps']] else: clH=[] for w in ch['reps']: nw=[] for s in w: nw.extend(W.rootrepelms[fh['subJ'][s]]) nw.append(W.rootorbits1[fh['subJ'][s]]) nw.extend(W.rootrepelms[fh['subJ'][s]][::-1]) clH.append(conjtomin(W,W.wordtoperm(nw))) H.fusions[W.cartanname]['classes']=identifyclasses(W,clH,minrep=True) return H.fusions[W.cartanname]['classes'] ########################################################################## ## #Y Section 3: Characters, Schur elements, families ## #F inducedchar def inducedchar(W,H,psi): """returns the values of the induced character Ind(psi) (from H to W) on the conjugacy classes of W, where the character psi is given by the list of its values on the conjugacy classes of H. >>> W=coxeter("A",5) >>> H=reflectionsubgroup(W,[0,1,2,3]) >>> c=conjugacyclasses(H) >>> c=conjugacyclasses(H)['reps']; c [[], [0], [0,2], [0,1], [0,1,3], [0,1,2], [0,1,2,3]] >>> inducedchar(W,H,[(-1)**len(w) for w in c]) # induce sign character [6, -4, 2, 0, 3, -1, 0, -2, 0, 1, 0] """ clW=conjugacyclasses(W)['classlengths'] clH=conjugacyclasses(H)['classlengths'] fus=fusionconjugacyclasses(H,W) ind=len(clW)*[0] for i in range(len(clW)): for j in range(len(clH)): if i==fus[j] and psi[j]!=0: ind[i]+=(W.order*clH[j]*psi[j])//(clW[i]*H.order) return ind # chartablesymmetric def chartablesymmetric(n): """returns the character table of the symmetric group of degree n. The rows and columns are indexed by the partitons of n, as ordered in partitions(n). The function computes the permutation characters on all Young subgroups and then decomposes them into irreducibles. (The implementation is much less efficient than that in gap3 but it reasonably works up to n around 15.) >>> partitions(4) [[1,1,1,1],[2,1,1],[2,2],[3,1],[4]] >>> chartablesymmetric(4) [[1,-1, 1, 1,-1], # sign character [3,-1,-1, 0, 1], # reflection character [2, 0, 2,-1, 0], [3, 1,-1, 0,-1], [1, 1, 1, 1, 1]] # trivial character """ W=coxeter("A",n-1) pt=partitions(n) ti=[] for mu in pt[::-1]: J=list(range(n)) l=0 for i in mu: l+=i J.remove(l-1) H=reflectionsubgroup(W,J) ch=conjugacyclasses(H) cl=ch['classlengths'] nam=[partitions(len(t[1])+1) for t in H.cartantype] fus=[] # more efficient than fusionconjugacyclasses for st in cartesian(nam): p=flatlist(st) p.sort(reverse=True) while sum(p)>> partitiontuples(3,2) [[[1,1,1],[]],[[1,1],[1]],[[1],[1,1]],[[],[1,1,1]],[[2,1],[]], [[1],[2]],[[2],[1]],[[],[2,1]],[[3],[]],[[],[3]]] >>> chartableB(3) [[1, 1, 1, 1,-1,-1,-1,-1, 1, 1], [3, 1,-1,-3,-1,-1, 1, 1, 0, 0], [3,-1,-1, 3,-1, 1,-1, 1, 0, 0], [1,-1, 1,-1,-1, 1, 1,-1, 1,-1], # sign character [2, 2, 2, 2, 0, 0, 0, 0,-1,-1], [3,-1,-1, 3, 1,-1, 1,-1, 0, 0], [3, 1,-1,-3, 1, 1,-1,-1, 0, 0], # reflection character [2,-2, 2,-2, 0, 0, 0, 0,-1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], # trivial character [1,-1, 1,-1, 1,-1,-1, 1, 1,-1]] See also 'heckevalueB'. """ W=coxeter("C",n) # use C_n because of B_2=C_2 convention. pt=partitiontuples(n,2) cw=conjugacyclasses(W) refls=reflections(W) wrho=[[0]] for i in range(n-1): wrho.append([i+1]+wrho[i]+[i+1]) rho=[refls.index(W.wordtoperm(p)) for p in wrho] ti=chartablehalfC(n) labels=[[p,[]] for p in partitions(n)] if n>=10: lprint('#I +') for a in range(1,n): # type C_a x C_{n-a} H=reflectionsubgroup(W,list(range(a))+[rho[a]]+list(range(a+1,n))) l0=len(H.cartantype[0][1]) l1=len(H.cartantype[1][1]) fus=[] # much faster than fusionconjugacyclasses for t in cartesian(partitiontuples(l0,2),partitiontuples(l1,2)): x=flatlist(t[0][0]+t[1][0]) x.sort(reverse=True) y=flatlist(t[0][1]+t[1][1]) y.sort(reverse=True) fus.append(pt.index([x,y])) H.fusions[W.cartanname]['classes']=fus[:] if 0 in H.cartantype[0][1]: f0,f1=False,True labels.extend([[p[0],p[1]] for p in cartesian(partitions(l0), partitions(l1))]) else: f0,f1=True,False labels.extend([[p[1],p[0]] for p in cartesian(partitions(l0), partitions(l1))]) ti.extend([inducedchar(W,H,psi) for psi in kroneckerproduct(chartablehalfC(l0,other=f0), chartablehalfC(l1,other=f1))]) if n>=10: lprint('+') ti.extend(chartablehalfC(n,other=True)) labels.extend([[[],p] for p in partitions(n)]) if n>=10: lprint('\n'); return [ti[labels.index(mu)] for mu in pt] # older version def chartableBold(n): """returns the character table of the finite Coxeter group of type B_n. The rows and columns are indexed by pairs of partitons of n, as ordered in partitiontuples(n,2). The function computes the permutation characters on suitable reflection subgroups (products of various types B and D, see [Ge-Pf, 5.5.3]) and then decomposes them into irreducibles. (The implementation is much less efficient than that in gap3 but it reasonably works for values of n up to around 8; it is also an excellent test for 'reflectionsubgroup' and 'fusionconjugacyclasses'). """ W=coxeter("C",n) cw=conjugacyclasses(W) cycw=[W.cycletyperoots(W.wordtoperm(w)) for w in cw['reps']] refls=reflections(W) rho=[[0]] trho=[[0,1,0]] for i in range(n-2): rho.append([i+1]+rho[i]+[i+1]) trho.append(rho[i+1]+[i+2]+rho[i+1]) rho.append([n-1]+rho[n-2]+[n-1]) rho=[refls.index(W.wordtoperm(p)) for p in rho] trho=[refls.index(W.wordtoperm(p)) for p in trho] pt=partitiontuples(n,2) binv=[2*sum(i*mu[0][i] for i in range(len(mu[0])))+2*sum(i*mu[1][i] for i in range(len(mu[1])))+sum(mu[1]) for mu in pt] nn=list(range(len(binv))) nn.sort(key=(lambda i:binv[i]),reverse=True) ti=[] for k in nn: mu=pt[k] J=list(range(1,n+1)) l=0 for i in dualpartition(mu[0]): if i>=2: J.append(trho[l]) l+=i J.remove(l) for i in dualpartition(mu[1]): J.append(rho[l]) l+=i J.remove(l) H=reflectionsubgroup(W,J) fus=[] ch=conjugacyclasses(H) cl=ch['classlengths'] f=H.fusions[W.cartanname]['subJ'] for w in [[f[s] for s in y] for y in ch['reps']]: pw=list(range(2*W.N)) for r in w: for i in range(2*W.N): pw[i]=W.reflections[r][pw[i]] fus.append(cycw.index(W.cycletyperoots(tuple(pw)))) H.fusions[W.cartanname]['classes']=fus[:] sgn=[(-1)**len(w) for w in ch['reps']] neu=inducedchar(W,H,sgn) for irr in ti: scal=sum(sgn[c]*cl[c]*irr[fus[c]] for c in range(len(cl)))//H.order neu=[neu[i]-scal*irr[i] for i in range(len(neu))] ti.append(neu[:]) return [ti[nn.index(i)] for i in range(len(nn))] #F chartableD def chartableD(n): """returns the character table of the finite Coxeter group of type D_n. The rows and columns are indexed by suitable pairs of partitons of n, as in gap-chevie. This is done by taking the irreducible characters of type B_n and restricting them to a reflection subgroup of type D_n. (Hence, the efficiency of this program heavily relies on 'chartableB'.) If n is odd, all the restrictions are irreducible. If n is even, the restrictions of all those characters of B_n which are labelled (alpha, alpha), where alpha is a partition of n/2, split into two irreducible components; these two components are denoted by (alpha, +) and (alpha, -). The notation is chosen as in Lusztig's book (4.6.2) (see also [Ge-Pf, 5.6.3] but note that the notation needs to be adjusted there, as indicated below). Let alpha be a partition of n/2 and denote by alpha+alpha the partition of n obtained by taking each part of alpha exactly twice. Let H be the parabolic subgroup of W(D_n) (isomorphic to the symmetric group of degree n) which is generated by the simple reflections labelled by 1,2,...,n-1. Then Ind(chi_{alpha+alpha})=chi_{(alpha,-)} + characters with higher b-invariant, where Ind denotes induction from H to W(D_n). Note that this function uses the following convention for the embedding of W(D_n) into W(B_n): 0 1 2 n-1 B_n o=<=o---o-- . . . --o 1' o \ 3' (n-1)' D_n 2' o---o--- . . . --o / 0' o where 0' -> 1, 1' -> 010, 2' -> 2, 3' -> 3, 4' -> 4, ... Further note the following compatibility with the corresponding CHEVIE-GAP table in the case where n is even: * If n/2 is even, then the tables (together with the labels) are exactly the same. * If n/2 is odd, then labels are the same, but in the table, all rows corresponding to +/- characters have to be swapped. >>> W=coxeter("D",4); chartable(W)['irreducibles'] [[3,-1, 3,-1, 1,-1, 3,-1,-1, 0, 0,-1, 1], [3,-1, 3,-1, 1,-1,-1, 3,-1, 0, 0, 1,-1], [4, 0,-4,-2, 0, 2, 0, 0, 0, 1,-1, 0, 0], [1, 1, 1,-1,-1,-1, 1, 1, 1, 1, 1,-1,-1], [6,-2, 6, 0, 0, 0,-2,-2, 2, 0, 0, 0, 0], [8, 0,-8, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0], [3, 3, 3,-1,-1,-1,-1,-1,-1, 0, 0, 1, 1], [3,-1, 3, 1,-1, 1, 3,-1,-1, 0, 0, 1,-1], [3,-1, 3, 1,-1, 1,-1, 3,-1, 0, 0,-1, 1], [2, 2, 2, 0, 0, 0, 2, 2, 2,-1,-1, 0, 0], [4, 0,-4, 2, 0,-2, 0, 0, 0, 1,-1, 0, 0], [3, 3, 3, 1, 1, 1,-1,-1,-1, 0, 0,-1,-1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] >>> H=reflectionsubgroup(W,[1,2,3]); H.cartantype [['A', [0,1,2]]] >>> t=inductiontable(H,W) >>> t1=transposemat(t['scalar']) >>> t['charH'] [('[1,1,1,1]',), ('[2,1,1]',), ('[2,2]',), ('[3,1]',), ('[4]',)] >>> chartable(W)['b'] [6, 6, 7, 12, 4, 3, 6, 2, 2, 4, 1, 2, 0] >>> [chartable(W)['charnames'][i] for i in [0,1,7,8]] [('[[1,1],+]',), ('[[1,1],-]',), ('[[2],+]',), ('[[2],-]',)] >>> t1[0] # take alpha=(1,1), position 0 in t['charH'] [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] >>> t['charW'][1] ('[[1,1],-]',) >>> t1[2] # take alpha=(2), position 2 in t['charH'] [0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0] >>> t['charW'][8] ('[[2],-]',) """ W1=coxeter("B",n) r1=reflections(W1) W=reflectionsubgroup(W1,list(range(1,n))+ [r1.index(W1.wordtoperm([0,1,0]))]) if n==2: suba=[0] else: suba=W.cartantype[0][1][:] # define embedding of S_n in W suba.remove(0) cw=conjugacyclasses(W) cl=cw['classlengths'] pt=partitiontuples(n,2) fus=[] # faster than fusionconjugacyclasses for i in range(len(pt)): mu=pt[i] if len(mu[1])%2==0: if mu[1]==[] and all(x%2==0 for x in mu[0]): fus.append(i) fus.append(i) else: fus.append(i) #if fus!=fusionconjugacyclasses(W,W1): # print("mist") # return "mist" pt1=[] # tuples relevant for D binv=[] trouble=[] ct=chartableB(n) ti=[] # table of restrictions for mu in pt: if sum(mu[0])>=sum(mu[1]): b=(2*sum(i*mu[0][i] for i in range(len(mu[0])))+2*sum(i*mu[1][i] for i in range(len(mu[1])))+sum(mu[1])) else: b=(2*sum(i*mu[0][i] for i in range(len(mu[0])))+2*sum(i*mu[1][i] for i in range(len(mu[1])))+sum(mu[0])) if mu[0]==mu[1]: trouble.append([mu[0][:],b]) pt1.append(mu) binv.append(b) ti.append([ct[pt.index(mu)][j] for j in fus]) pt1.append(mu) binv.append(b) ti.append([ct[pt.index(mu)][j] for j in fus]) elif mu[0]t[1]: scal=sum(cl[k]*ti[i][k]*neu[k] for k in range(len(cl)) if neu[k]!=0)//W.order for k in range(len(cl)): neu[k]-=scal*ti[i][k] i=pt1.index([t[0],t[0]]) ti[i+1]=[neu[j] for j in range(len(cl))] ti[i]=[ti[i][j]-neu[j] for j in range(len(cl))] return ti #F irrchardata def irrchardata(typ,n,chars=True): """returns the irreducible characters of the finite Coxeter group of type 'typ' and rank 'n'. The data are taken from the corresponding files in gap-chevie. See also 'chartable, 'chartablesymmetric', 'chartableB', 'chartableD'. """ ti=[[False]] if typ[0]=='A' and n==0: if chars: ti=[[1]] nam=['1'] binv=[0] ainv=[0] if typ[0]=='A' and n>=1: if chars: ti=chartablesymmetric(n+1) binv,nam=[],[] for mu in partitions(n+1): binv.append(sum(i*mu[i] for i in range(len(mu)))) nam.append(str(mu)) ainv=binv[:] if typ[0]=='B' or typ[0]=='C': if chars: ti=chartableB(n) binv,ainv,nam=[],[],[] for mu in partitiontuples(n,2): binv.append(2*sum(i*mu[0][i] for i in range(len(mu[0]))) +2*sum(i*mu[1][i] for i in range(len(mu[1])))+sum(mu[1])) ainv.append(ainvbipartition(n,1,1,mu)) nam.append(str(mu)) if typ[0]=='D': if chars: ti=chartableD(n) binv,ainv,nam=[],[],[] for mu in partitiontuples(n,2): if sum(mu[0])>=sum(mu[1]): b=(2*sum(i*mu[0][i] for i in range(len(mu[0])))+2*sum(i*mu[1][i] for i in range(len(mu[1])))+sum(mu[1])) else: b=(2*sum(i*mu[0][i] for i in range(len(mu[0])))+2*sum(i*mu[1][i] for i in range(len(mu[1])))+sum(mu[0])) if mu[0]==mu[1]: binv.append(b) ainv.append(ainvbipartition(n,1,0,mu)) nam.append('['+str(mu[0])+', +]') binv.append(b) ainv.append(ainvbipartition(n,1,0,mu)) nam.append('['+str(mu[1])+', -]') elif mu[0]>> chartable(coxeter("G",2)) {'irreducibles' : [[1, 1, 1, 1, 1, 1], [1,-1,-1, 1, 1, 1], [1, 1,-1,-1, 1,-1], [1,-1, 1,-1, 1,-1], [2, 0, 0, 1,-1,-2], [2, 0, 0,-1,-1, 2]], 'classnames' : [(' ',),('~A_1',),('A_1',),('G_2',),('A_2',), ('A_1+~A_1',)], 'classreps' : [[],[1],[0],[0,1],[0,1,0,1],[0,1,0,1,0,1]], 'classlengths' : [1,3,3,2,2,1], 'b' : [0,6,3,3,1,2], 'a' : [0,6,1,1,1,1], 'charnames' : [('phi_{1,0}',),('phi_{1,6}',),("phi_{1,3}'",), ("phi_{1,3}'',)",('phi_{2,1}',),('phi_{2,2}',)]], 'position_id' : 0, 'position_sgn : 1, 'position_refl': 4, 'permsgn : [1,0,3,2,4,5]} See also 'displaychartable'. """ if 'chartable' in dir(W) and (chars==False or 'irreducibles' in W.chartable.keys()): return W.chartable t0=irrchardata(W.cartantype[0][0],len(W.cartantype[0][1]),chars) mat=t0['irreducibles'] bs=[t0['b']] a1s=[t0['a1']] nam=[t0['names']] for t in W.cartantype[1:]: ti=irrchardata(t[0],len(t[1])) mat=kroneckerproduct(mat,ti['irreducibles']) bs.append(ti['b']) a1s.append(ti['a1']) nam.append(ti['names']) nb=[sum(bi) for bi in cartesian(bs)] na1=[sum(ai) for ai in cartesian(a1s)] nnam=[tuple(st) for st in cartesian(nam)] cw=conjugacyclasses(W) if not 'chartable' in dir(W): W.chartable={'charnames':nnam,'classnames':cw['classnames'], 'classlengths':cw['classlengths'], 'classreps':cw['reps'],'b':nb,'a':na1} if chars: sgn=[(-1)**len(w) for w in cw['reps']] sp=[mat.index([l[j]*sgn[j] for j in range(len(cw['reps']))]) for l in mat] if len(W.cartantype)>1: prf=False else: prefl=[(mat[i][0],nb[i]) for i in range(len(nb))] if len(W.rank)==0: prf=1 else: prf=prefl.index((len(W.rank),1)) W.chartable['irreducibles']=mat W.chartable['position_id']=mat.index(len(cw['reps'])*[1]) W.chartable['position_sgn']=mat.index(sgn) W.chartable['position_refl']=prf W.chartable['permsgn']=sp return W.chartable #F displaychartable def displaychartable(ti): """displays the character table of a Coxeter group (or of an Iwahori-Hecke algebra) in a user-friendly way, where the labelling of the classes and characters is included. >>> displaychartable(heckechartable(coxeter("G",2),[v,v**2])) -------------------------------------------------------------- ~A_1 A_1 G_2 A_2 A_1+~A_1 -------------------------------------------------------------- phi_{1,0} 1 v**4 v**2 v**6 v**12 v**18 phi_{1,6} 1 -1 -1 1 1 1 phi_{1,3}' 1 v**4 -1 -v**4 v**8 -v**12 phi_{1,3}'' 1 -1 v**2 -v**2 v**4 -v**6 phi_{2,1} 2 -1+v**4 -1+v**2 v**3 -v**6 -2*v**9 phi_{2,2} 2 -1+v**4 -1+v**2 -v**3 -v**6 2*v**9 See also 'displaymat'. """ if not 'charnames' in ti.keys(): rows=['|'.join(c) for c in ti['coxeter'].chartable['charnames']] cols=['|'.join(c) for c in ti['coxeter'].chartable['classnames']] else: rows=['|'.join(c) for c in ti['charnames']] cols=['|'.join(c) for c in ti['classnames']] displaymat(ti['irreducibles'],rows,cols) #F inductiontable def inductiontable(H,W,display=False,invchar=False): """computes the decomposition of the induced characters from the subgroup H into irreducible characters of W. The rows correspond to the characters of W, the columns to those of the subgroup. The result is a dictionary with entries: scalar : the induction table proper charW : names of the characters of W charH : names of the characters of H >>> W=coxeter("B",3) >>> H=reflectionsubgroup(W,[1,2,5]); H.cartantype [['A',[0,1,2]]] # non-parabolic >>> inductiontable(H,W) {'scalar': [[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [1, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1]], 'charH' : [('[1,1,1,1]',), ('[2,1,1]',), ('[2,2]',), ('[3,1]',), ('[4]',)], 'charW' : [('[[1,1,1],[]]',), ('[[1,1],[1]]',), ('[[1],[1,1]]',), ('[[],[1,1,1]]',), ('[[2,1],[]]',), ('[[1],[2]]',), ('[[2],[1]]',), ('[[],[2,1]]',), ('[[3],[]]',), ('[[],[3]]',)]} With the optional argument 'invchar' one can specify a numerical function on the irreducible characters by which the induction will be truncated, for example: >>> inductiontable(H,W, ... invchar=(lambda G:chartable(G, chars=False)['b'])) will yield the Lusztig-Macdonald-Spaltenstein induction. See also 'chartable'. """ ch=chartable(H) tab={'charH':ch['charnames'][:],'charW':chartable(W)['charnames'][:]} f=fusionconjugacyclasses(H,W) m=len(ch['classlengths']) mat=[] for t in chartable(W)['irreducibles']: res=[t[f[k]] for k in range(m)] norm=sum(res[k]*res[k]*ch['classlengths'][k] for k in range(m))//H.order vec=m*[0] norm1=0 j=0 while j>> involutionmodel(coxeter("H",3)) #I Total decomposition: #I [1,1,1,1,1,1,1,1,1,1] {'[0,1,0,1,0,2,1,0,1,0,2,1,0,1,2]':[1,0,0,0,0,0,0,0,0,0], '[0,2]' :[0,0,0,1,1,1,0,0,0,1], '[0]' :[0,0,1,0,0,0,1,1,1,0], '[]' :[0,1,0,0,0,0,0,0,0,0]} See also 'involutions' and 'conjugacyclasses'. """ if type(poids)==type(1): lp=len(W.rank)*[poids] else: lp=poids rr=conjugacyclasses(W)['reps'] ti=chartable(W) cl=[w for w in rr if W.wordtocoxelm(2*w)==tuple(W.rank)] #lprint('# Number of involutions = '+str(sum([len(l) for l in cl]))+'\n') chisigma={} for x in cl: inv=conjugacyclass(W,W.wordtoperm(x)) cinv=[bytes(y[:len(W.rank)]) for y in inv] perms=[] lprint('#I ') for s in W.rank: lprint(str(s)+' ') p=(2*len(inv))*[0] for i in range(len(inv)): i1=cinv.index(bytes(conjgenperm(W,s,inv[i])[:len(W.rank)])) if i==i1 and inv[i][s]>=W.N and lp[s]%2==1: p[i]=len(inv)+i1 p[len(inv)+i]=i1 else: p[i]=i1 p[len(inv)+i]=len(inv)+i1 perms.append(p) char=[] #lprint('\n#I char values: ') for w in rr: # lprint('+') if w==[]: char.append(len(inv)) else: l=[list(range(len(inv)))] l.extend([perms[s] for s in w]) p=reduce(permmult,tuple(l)) c=0 for i in range(len(inv)): if p[i]==i: c+=1 if p[i]==len(inv)+i: c-=1 char.append(c) chisigma[str(x)]=[sum([char[j]*chi[j]*ti['classlengths'][j] for j in range(len(rr))])//W.order for chi in ti['irreducibles']] lprint('\n') lprint('#I Total decomposition:\n#I ') lprint(str([sum([chisigma[chi][i] for chi in chisigma.keys()]) for i in range(len(rr))])) lprint('\n') return chisigma #F symbol2c, symbol2b, symbol2d needed for dimBu def symbol2c(dblpart): a,b=dblpart[0][:],dblpart[1][:] if len(a)>len(b)+1: b.extend((len(a)-len(b)-1)*[0]) elif len(b)+1>len(a): a.extend((len(b)-len(a)+1)*[0]) a.sort() b.sort() for i in range(1,len(a)): a[i]+=2*i b[i-1]+=2*i-1 return [a,b] def symbol2b(dblpart): a,b=dblpart[0][:],dblpart[1][:] if len(a)>len(b)+1: b.extend((len(a)-len(b)-1)*[0]) elif len(b)+1>len(a): a.extend((len(b)-len(a)+1)*[0]) a.sort() b.sort() for i in range(1,len(b)): a[i]+=2*i b[i]+=2*i a[-1]=a[-1]+2*len(b) return [a,b] def symbol2d(dblpart): a,b=dblpart[0][:],dblpart[1][:] if len(a)>len(b): b.extend((len(a)-len(b))*[0]) elif len(b)>len(a): a.extend((len(b)-len(a))*[0]) a.sort() b.sort() for i in range(1,len(b)): a[i]+=2*i b[i]+=2*i return [a,b] #F dimBu def dimBu(W): """returns, for every irreducible character of a finite Weyl group, the dimension of the variety of Borel subgroups containing an element in the unipotent class corresponding to that character via the Springer correspondence (where the underlying algebraic group is defined over a field of characteristic p where either p=0 or p is a good prime.) >>> W=coxeter("G",2) >>> dimBu(W); chartable(W)['a']; chartable(W)['b'] [0, 6, 1, 3, 1, 2] [0, 6, 1, 1, 1, 1] [0, 6, 3, 3, 1, 2] This uses the explicit knowledge of the Springer correspondence in all cases, as determined by Springer (1977) for A_n,G_2; Shoji (1979) and Lusztig (1984) for B_n,C_n,D_n; Shoji (1980) for F_4; Alvis- Lusztig (1982) and Spaltenstein (1982) for E_6,E_7,E_8. Note that the function yields different results for B_n and C_n: >>> dimBu(coxeter("B",3)) [4, 3, 4, 9, 1, 2, 1, 5, 0, 2] >>> dimBu(coxeter("C",3)) [6, 3, 4, 9, 2, 2, 1, 4, 0, 1] See also 'chartable' and 'inductiontable'. """ db=[] for ct in W.cartantype: if ct[0]=='A': db.append(chartable(coxeter('A',len(ct[1])),chars=False)['a']) if (ct[0]=='B' or ct[0]=='C') and len(ct[1])==2: db.append([2,1,4,0,1]) if ct[0]=='B' and len(ct[1])>2: d=[] for param in partitiontuples(len(ct[1]),2): symb=symbol2b(param) x=0 for i in range(len(symb[0])): for j in range(len(symb[0])): if i2: d=[] for param in partitiontuples(len(ct[1]),2): symb=symbol2c(param) x=0 for i in range(len(symb[0])): for j in range(len(symb[0])): if i>> W=coxeter("B",3) >>> ainvariants(W,1) # all weights equal to 1 [4,3,4,9,1,2,1,4,0,1] >>> ainvariants(W,[3,2,2,2) # weights [3,2,2,2] [7,6,10,21,2,5,3,11,0,4] >>> ainvariants(W,[0,1,1,1) # weights [0,1,1,1] [6,3,3,6,2,1,1,2,0,0] """ cw=conjugacyclasses(W)['reps'] if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL if all(p==0 for p in poids): return len(cw)*[0] if len(set(poids))==1: return [poids[0]*i for i in chartable(W,chars=False)['a']] gens=[j for j in range(len(cw)) if len(cw[j])==1] ti=chartable(W) irr=ti['irreducibles'] omega=[sum(ti['classlengths'][s]*irr[t][s]*poids[cw[s][0]] for s in gens)//irr[t][0] for t in range(len(cw))] tainv=len(irr)*[0] for s in W.rank: J=list(W.rank[:]) J.remove(s) H=reflectionsubgroup(W,J) t=inductiontable(H,W) ainvH=ainvariants(H,[poids[j] for j in J]) for i in range(len(t['charW'])): for j in range(len(t['charH'])): if t['scalar'][i][j]!=0 and ainvH[j]>tainv[i]: tainv[i]=ainvH[j] ainv=len(irr)*[0] for t in range(len(irr)): if tainv[ti['permsgn'][t]]-tainv[t]<=omega[t]: ainv[t]=tainv[t] else: ainv[t]=tainv[ti['permsgn'][t]]-omega[t] return ainv #F niceprintconstructible def niceprintconstructible(const,chn): if len(const)==1: lprint('# one constructible character:\n') else: lprint('# '+str(len(const))+' constructible characters:\n') for x in const: lprint('# ') z=0 for i in range(len(x)): if x[i]!=0: if z==1: lprint(' + ') if x[i]!=1: lprint(str(x[i])+'*') if len(chn[i])==1: lprint(chn[i][0]) else: lprint(str(chn[i])) z=1 lprint('\n') #F constructible def constructible(W,weightL=1,display=True): """computes the constructible characters of W as defined by G. Lusztig, A class of irreducible representations of a finite Weyl group II, Indag. Math. 44 (1982), 219-226. The result is a dictionary with components: 'constructibles' : the matrix containing the multiplicities of the various irreducible characters in the constructible characters. 'families' : the partition of Irr(W) into families. The constructible characters are computed by a recursive procedure, using induction of characters from parabolic subgroups, truncation by a-invariants and tensoring with the sign character. In the equal parameter case (where weightL=1, default value), these are known to be equal to the characters afforded by the various Kazhdan-Lusztig left cells of W. The definition and the algorithm also make sense for general weightL, but then the connection with cells is still conjectural. If the optional argument 'display' is set to False, then the step of printing a formatted list of the constructible characters is skipped. See also 'ainvariants' and 'lusztigfamilies'. >>> W=coxeter("B",2) >>> constructible(W) # equal parameter case # 4 constructible characters: # [[2], []] # [[1], [1]] + [[], [2]] # [[1, 1], []] + [[1], [1]] # [[], [1, 1]] {'constructibles': [[0, 0, 0, 1, 0], [0, 1, 0, 0, 1], [1, 1, 0, 0, 0], [0, 0, 1, 0, 0]], 'families': [[3], [0, 1, 4], [2]]} >>> constructible(W,[1,2]) # unequal parameters # 5 constructible characters: # [[2], []] # [[1, 1], []] # [[1], [1]] # [[], [2]] # [[], [1, 1]] {'constructibles': [[0, 0, 0, 1, 0], [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1], [0, 0, 1, 0, 0]], 'families': [[3], [0], [1], [4], [2]]} >>> constructible(W,[1,0]) # unequal parameters # 3 constructible characters: # [[2], []] + [[], [2]] # [[1], [1]] # [[1, 1], []] + [[], [1, 1]] {'constructibles': [[0, 0, 0, 1, 1], [0, 1, 0, 0, 0], [1, 0, 1, 0, 0]], 'families': [[3, 4], [1], [0, 2]]} >>> c=constructible(coxeter("F",4)) # 17 constructible characters: # 1_1 # 2_3 + 4_2 # 2_1 + 4_2 # 9_1 # 8_1 # 8_3 # 9_3 + 6_1 + 12 + 4_4 + 16 # 9_2 + 6_1 + 12 + 4_3 + 16 # 4_1 + 9_2 + 9_3 + 6_2 + 12 + 2*16 # 1_3 + 2*9_3 + 6_2 + 12 + 4_4 + 16 # 1_2 + 2*9_2 + 6_2 + 12 + 4_3 + 16 # 8_2 # 8_4 # 9_4 # 2_4 + 4_5 # 2_2 + 4_5 # 1_4 """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL if len(W.rank)==0: return {'constructibles':[[1]],'families':[[0]]} #if len(W.rank)==1: # if poids[0]==0: # return [[1,1]] # else: # return [[1,0],[0,1]] ti=chartable(W) ag=ainvariants(W,poids) const=[] for s in W.rank: J=list(W.rank[:]) J.remove(s) H=reflectionsubgroup(W,J) tr=chartable(H) it=inductiontable(H,W)['scalar'] poidsJ=[poids[j] for j in J] au=ainvariants(H,poidsJ) for i in range(len(au)): for j in range(len(ag)): if ag[j]!=au[i]: it[j][i]=0 for x in matmult(constructible(H,poidsJ, display=False)['constructibles'],transposemat(it)): if not x in const: const.append(x[:]) nx=len(x)*[0] for i in range(len(nx)): nx[ti['permsgn'][i]]=x[i] if not nx in const: const.append(nx[:]) const.sort() rest=list(range(len(ag))) f=[] while rest!=[]: o=[rest[0]] for chi in o: for cp in const: for psi in range(len(ag)): if cp[chi]!=0 and cp[psi]!=0 and not psi in o: o.append(psi) o.sort() f.append(o[:]) for x in o: rest.remove(x) f.sort(key=(lambda x:ag[x[0]])) fam=[] for c in const: i=0 while c[i]==0: i+=1 j=0 while not i in f[j]: j+=1 fam.append(j) nconst=[] for i in range(len(f)): for c in range(len(const)): if fam[c]==i: nconst.append(const[c]) if display: niceprintconstructible(nconst,ti['charnames']) return {'constructibles':nconst,'families':f} #F lusztigpreceq def lusztigpreceq(W,poids,display=False): """returns the incidence matrix needed for determining families and the pre-order relation on them; see 'lusztigfamilies' for further details. """ ti=chartable(W) if len(W.rank)==0: return [[True]] if len(W.rank)==1: if poids==[0]: return [[True,True],[True,True]] else: if ti['position_id']>> lusztigfamilies(coxeter("H",3)) 'families': {[[1], [6,7], [3], [8,9], [2], [4,5], [0]], 'names' : [[('1_r',)], [("3_s'",), ("overline{3}_s'",)], [('5_r',)], [("4_r'",), ('4_r',)], [("5_r'",)], [('3_s',), ('overline{3}_s',)], [("1_r'",)]], 'ainv' : [0, 1, 2, 3, 5, 6, 15], 'hasse' : [[0,1], [1,2], [2,3], [3,4], [4,5], [5,6]]} Thus, there are 7 families in this case, and the partial order is a linear order. >>> W=coxeter("B",2) >>> lusztigfamilies(W) # equal parameters 'families': {[[3], [0,1,4], [2]], 'names' : [[('[[2], []]',)], [('[[1,1], []]',), ('[[1], [1]]',), ('[[], [2]]',)], [('[[], [1, 1]]',)]], 'ainv' : [0, 1, 4], 'hasse' : [[0, 1], [1, 2]]} >>> lusztigfamilies(W,[2,1]) 'families': [[3], [0], [1], [4], [2]], 'names' : [[('[[2], []]',)], [('[[1, 1], []]',)], [('[[1], [1]]',)], [('[[], [2]]',)], [('[[], [1, 1]]',)]]} 'ainv' : [0, 1, 2, 3, 6], 'hasse' : [[0, 1], [1, 2], [2, 3], [3, 4]], """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL ti=chartable(W) if len(W.rank)==0: return {'hasse':[],'ainv':[0],'families':[[0]],'names':[['[1]']]} if len(W.rank)==1: if poids[0]==0: return {'hasse':[],'ainv':[0],'families':[[0,1]], 'names':[['[1,1]','[2]']]} else: return {'hasse':[[0,1]],'ainv':[0,1],'families':[[1],[0]], 'names':[['[2]'],['[1,1]']]} ag=ainvariants(W,poids) lprint('#I ') fam=lusztigpreceq(W,poids,display=True) check=True fam1=[] rest=list(range(len(ag))) while rest!=[]: chi=rest[0] orb=[chi] for chi1 in range(1,len(rest)): if fam[chi][rest[chi1]] and fam[rest[chi1]][chi]: orb.append(rest[chi1]) fam1.append(orb[:]) if len(set([ag[x] for x in orb]))>1: check=False for o in orb: rest.remove(o) fam1.sort(key=(lambda i:ag[i[0]])) rest=[] for chi1 in fam1: for chi2 in fam1: if chi1!=chi2 and any(any(fam[psi1][psi2] for psi2 in chi2) for psi1 in chi1): rest.append([fam1.index(chi1),fam1.index(chi2)]) hasse=[] for chi1 in range(len(fam1)): for chi2 in range(len(fam1)): if chi1!=chi2 and [chi1,chi2] in rest: if not any(chi!=chi1 and chi!=chi2 and [chi1,chi] in rest and [chi,chi2] in rest for chi in range(len(fam1))): hasse.append([chi1,chi2]) for h in hasse: if ag[fam1[h[0]][0]]>ag[fam1[h[1]][0]]: check=False if len(fam1)==1: lprint(' '+str(len(fam1))+' family; ') else: lprint(' '+str(len(fam1))+' families; ') lprint(' monotony of a-function is '+str(check)+'\n') return {'families':fam1, 'ainv':[ag[x[0]] for x in fam1],'hasse':hasse, 'names':[[ti['charnames'][i] for i in f] for f in fam1]} #F poincarepol def poincarepol(W,paramL): """returns the Poincare polynomial of a finite Coxeter group with respect to a list of parameters. This is defined by P_W = sum_{w in W} ind(w) where ind(w)=paramL[s_1]* ... *paramL[s_k] and w=s_1...s_k is a reduced expression for w. The parameters are a list of elements in a base ring, one for each simple reflection in W, such that two parameters are equal whenever the corresponding reflections are conjugate in W. For example, a typical parameter list is given by [u^a,u^b,...] where [a,b,...] form a list of weights as in 'ainvariants' and u is some indeterminate. It is allowed that the argument paramL is just one element, in which case all parameters will be set equal to that element; the default value is 1, in which case the function returns the group order. >>> W=coxeter("G",2) >>> v=lpol([1],1,'v') >>> p=poincarepol(W,v); p 1+2*v+2*v**2+2*v**3+2*v**4+2*v**5+v**6 >>> cycldec(p) # factorise into cyclotomic polynomials [1, 0, [[2, 2], [3, 1], [6, 1]]] See also 'heckechartable'. (Note that the parameters used there are square roots of the parameters used here.) """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] if len(W.rank)==0: return 1 else: W1=reflectionsubgroup(W,list(W.rank[:-1])) po=poincarepol(W1,vs[:-1]) rp=0 for w in redrightcosetreps(W,W1): p=1 for s in W.coxelmtoword(w): p*=vs[s] rp+=p return po*rp #F classpolynomials def classpolynomials(W,paramL,pw): """computes the class polynomials of an element (given as a full permutation on the roots) with respect to a set of parameters. This is done by a recursive algorithm (see [Ge-Pf, 8.2.3, 8.2.7]) based on 'testcyclishift'. More precisely, let f_{w,C} be the class polynomial for an element w in W and a conjugacy class C of W. Then f_{w,C} = 1 if w has minimal length in C; f_{w,C'} = 0 if w has minimal length in a class C' not equal to C; f_{w,C} = f_{w',C} if w,w' are conjugate by cyclic shift; f_{w,C} = paramL[s] f_{sws,C} + (paramL[s]-1) f_{sw,C} where s in S is such that l(sws)>> W=coxeter("A",3) >>> R.=QQ['u'] >>> classpolynomials(W,[u,u,u],longestperm(W)) [0, 0, u^2, u^3-2*u^2+u, u^3-u^2+u-1] See also 'allclasspolynomials', especially if you need the class polynomials for several elements, or elements of relatively big length. """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] t=testcyclicshift(W,pw) if t==True: cp=len(conjugacyclasses(W)['reps'])*[0] cp[identifyclasses(W,[W.permtoword(pw)],minrep=True)[0]]=1 else: cp1=classpolynomials(W,vs,t[0]) cp2=classpolynomials(W,vs,[t[0][i] for i in W.permgens[t[1]]]) cp=[vs[t[1]]*cp1[i]+(vs[t[1]]-1)*cp2[i] for i in range(len(cp1))] return cp #F testcyclicshift1 def testcyclicshift1(W,w,pw): """returns the cyclic shift orbit of an element together with some additional information, for us in 'allclasspolynomials'. """ bahn=[pw[:]] cbahn=set([bytes(pw[:len(W.rank)])]) l=len([i for i in pw[:W.N] if i>=W.N]) minr=1 for b in bahn: for s in W.rank: nw=tuple([W.permgens[s][b[W.permgens[s][r]]] for r in range(2*W.N)]) lnw=len([i for i in nw[:W.N] if i>=W.N]) cnw=bytes(nw[:len(W.rank)]) if minr==1 and lnw>> W=coxeter("I5",2) >>> c=allclasspolynomials(W,v); c {(0, 1): [1, 0, 0, 0], (4, 7): [0, 0, 1, 0], (7, 0): [0, 0, 0, 1], (2, 9): [0, v, -1+v, 0], (9, 3): [0, v, -1+v, 0], (1, 8): [0, 0, 0, 1], (3, 6): [0, 1, 0, 0], (5, 2): [0, 1, 0, 0], (6, 5): [0, v**2, -v+v**2, -1+v], (8, 4): [0, 0, 1, 0]} >>> [W.coxelmtoword(w) for w in c.keys()] [[], [1, 0], [0, 1, 0, 1], [1, 0, 1], [0, 1, 0], [1, 0, 1, 0], [1], [0], [0, 1, 0, 1, 0], [0, 1]] See also 'classpolynomials' and 'heckecharvalues'. """ if maxl==-1: maxlen=W.N else: maxlen=min(maxl,W.N) if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] poin=poincarepol(W,v).coeffs cl=conjugacyclasses(W)['reps'] cpmat=[] cp=len(cl)*[0] cp[0]=1 cpmat.append({tuple(W.rank):cp}) cps={} for s in W.rank: cp=len(cl)*[0] cp[identifyclasses(W,[[s]],minrep=True)[0]]=1 cps[W.permgens[s][:len(W.rank)]]=cp cpmat.append(cps) if maxlen>20: lprint('#I 0-1-') for l in range(1,maxlen): if maxlen>20: lprint(str(l+1)) nl=set([]) ol=set(cpmat[l-1].keys()) for w in cpmat[l].keys(): for s in W.permgens: nw=tuple([s[i] for i in w]) if not nw in ol: nl.add(nw) if len(nl)==poin[l+1]: break cps={} if maxlen>20: lprint('-') while nl!=set([]): cw=next(iter(nl)) cw1=W.coxelmtoword(cw) pc1=W.wordtoperm(cw1) t=testcyclicshift1(W,cw1,pc1) if t[0]==1: cp=len(cl)*[0] cp[t[2]]=1 else: cp1=cpmat[l-1][t[2]] cp2=cpmat[l][tuple([W.permgens[t[3]][i] for i in t[2]])] cp=[vs[t[3]]*cp1[i]+(vs[t[3]]-1)*cp2[i] for i in range(len(cl))] for x in t[1]: cx=x[:len(W.rank)] cps[cx]=cp nl.remove(cx) if not perminverse(pc1) in t[1]: for x in t[1]: cx=perminverse(x)[:len(W.rank)] cps[cx]=cp nl.remove(cx) cpmat.append(cps) if maxlen>20: lprint('\n') res={} for l in cpmat: for x in l.keys(): res[x]=l[x] return res #F starkey def starkey(n,u): """returns the matrix required by Starkey's Rule (see [Ge-Pf, 9.2.11]) to produce the character table of the Iwahori-Hecke algebra of type A. """ W=coxeter("A",n-1) pt=partitions(n) cl=[W.order//centraliserpartition(n,p) for p in pt] stk=[] for mu in range(len(pt)): J=list(range(n)) l=0 for i in pt[mu]: l+=i J.remove(l-1) H=reflectionsubgroup(W,J) ch=conjugacyclasses(H) nam=[partitions(len(t[1])+1) for t in H.cartantype] fus=[] # more efficient than fusionconjugacyclasses for st in cartesian(nam): p=flatlist(st) p.sort(reverse=True) while sum(p)>> p=partitiontuples(n,2) >>> [[heckevalueB(n,q,Q,a,b) for b in p] for a in p] will yield the complete character table (with the same ordering of the rows and columns as in 'heckechartable'). If q,Q are set equal to 1, then 'heckevalueB' yields an individual entry of the ordinary character table of W(B_n). >>> heckevalueB(20,v**2,v**3,[[8,6,2],[2,2]],[[4,4,4],[4,4]]) -30*v**24+324*v**26-1620*v**28+4500*v**30-7719*v**32+8499*v**34 -5940*v**36+2778*v**38-1608*v**40+1590*v**42-1050*v**44 +210*v**46+141*v**48-93*v**50+18*v**52 >>> heckevalueB(20,1,1,[[8,6,2],[2,2]],[[4,4,4],[4,4]]) 0 (The whole table for n=20 has 24842 rows and columns.) See also 'chartableB' and 'heckechartable'. """ if n==0: return q**0 val=0*q if pi[0]!=[]: k=pi[0][0] for alpha in partitiontuples(n-k,2): dif=[differencepartitions(gamma[0],alpha[0]), differencepartitions(gamma[1],alpha[1])] if dif[0]!=False and dif[1]!=False: dif={'cc':dif[0]['cc']+dif[1]['cc'],'ll':dif[0]['ll']+dif[1]['ll']} val+=(q-1)**(dif['cc']-1)*(-1)**(dif['ll'])*q**(k-dif['ll']- dif['cc'])*heckevalueB(n-k,q,Q,alpha, [[pi[0][i] for i in range(1,len(pi[0]))],pi[1]]) else: k=pi[1][0] nn=sum(gamma[0]) if nn>=k: for alpha in partitions(nn-k): dif=differencepartitions(gamma[0],alpha) if dif!=False and dif['cc']==1: val+=Q*(-1)**(dif['ll'])*q**(n+dif['d'])*heckevalueB(n-k,q,Q, [alpha,gamma[1]],[pi[0],[pi[1][i] for i in range(1,len(pi[1]))]]) nn=sum(gamma[1]) if nn>=k: for alpha in partitions(nn-k): dif=differencepartitions(gamma[1],alpha) if dif!=False and dif['cc']==1: val+=(-1)**(dif['ll']+1)*q**(n+dif['d'])*heckevalueB(n-k,q,Q, [gamma[0],alpha],[pi[0],[pi[1][i] for i in range(1,len(pi[1]))]]) return val def heckeB(n,q,Q): p=partitiontuples(n,2) return [[heckevalueB(n,q,Q,a,b) for b in p] for a in p] def heckeD(n,v): W1=coxeter("B",n) r1=reflections(W1) W=reflectionsubgroup(W1,list(range(1,n))+ [r1.index(W1.wordtoperm([0,1,0]))]) cw=conjugacyclasses(W)['reps'] cc=[i for i in range(len(cw)) if set(cw[i])==set(list(range(n)))] cw1=[W.reducedword(cw[i],W1) for i in cc] if all(len(conjtomin(W1,W1.wordtoperm(x)))==len(x) for x in cw1): fus=identifyclasses(W1,cw1,minrep=True) else: print("Mist!") return False pt=partitiontuples(n,2) t1=[] # table of restrictions for i in range(len(pt)): mu=pt[i] if mu[0]==mu[1]: vals=[divmod(heckevalueB(n,v**2,1,mu,pt[j]),2)[0] for j in fus] t1.append(vals) t1.append(vals) elif mu[0]=1: #v=paramL[0] #cl=[centraliserpartition(n+1,p) for p in partitions(n+1)] #sti=starkey(n+1,paramL[0]) #cc=list(range(len(cl))) #ch=cc[:] #t=chartable(coxeter("A",n))['irreducibles'] #t1=[[sum(t[i][k]*sti[k][j] for k in range(len(cl)))//cl[0] # for j in range(len(cl))] for i in range(len(cl))] cc=[] ch=[] t1=[[]] if typ[0]=='B' and n==2: v=paramL[0]**2 u=paramL[1]**2 # v == u cc=[2,4] ch=[0,1,2,3,4] t1=[[u**2,-u],[-2*v*u,0],[1,1],[v**2*u**2,v*u],[v**2,-v]] if typ[0]=='C' and n==2: # u == v v=paramL[0]**2 u=paramL[1]**2 # u == v cc=[2,4] ch=[0,1,2,3,4] t1=[[v**2,-v],[-2*v*u,0],[1,1],[v**2*u**2,v*u],[u**2,-u]] if (typ[0]=='B' or typ[0]=='C') and n>=3: v=paramL[0]**2 u=paramL[1]**2 # v == u -- u -- ... --- u p=partitiontuples(n,2) cc=[i for i in range(len(p)) if p[i][0]==[]] ch=list(range(len(p))) t1=[[heckevalueB(n,u,v,a,p[b]) for b in cc] for a in p] if typ[0]=='D' and n>=3: v=paramL[0] W1=coxeter("B",n) r1=reflections(W1) W=reflectionsubgroup(W1,list(range(1,n))+ [r1.index(W1.wordtoperm([0,1,0]))]) cw=conjugacyclasses(W)['reps'] cc=[i for i in range(len(cw)) if set(cw[i])==set(list(range(n)))] cw1=[W.reducedword(cw[i],W1) for i in cc] if n<11 or all(len(conjtomin(W1,W1.wordtoperm(x)))==len(x) for x in cw1): fus=identifyclasses(W1,cw1,minrep=True) else: print("Mist!") return False pt=partitiontuples(n,2) t1=[] # table of restrictions for i in range(len(pt)): mu=pt[i] if mu[0]==mu[1]: vals=[divmod(heckevalueB(n,v**2,1,mu,pt[j]),2)[0] for j in fus] t1.append(vals) t1.append(vals) elif mu[0]>> heckechartable(coxeter("B",2),[v**3,v**2]) {'irreducibles': [[1, v**4, v**8, -1, -v**4], [2, -1+v**4, -2*v**10, -1+v**6, 0], [1, -1, 1, -1, 1], [1, v**4, v**20, v**6, v**10], [1, -1, v**12, v**6, -v**6]], 'coxeter': coxeter('B',2), 'params': [v**3, v**2]} See also 'classpolynomials', 'heckecharvalues', 'schurelms' and 'displaychartable'. """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] ti=chartable(W) if all(x==x**0 for x in vs): return ti nti={'params':vs,'coxeter':W} ct=W.cartantype[0] if len(W.cartantype)>1: # can use induction nti['irreducibles']=heckechartable(coxeter(ct[0],len(ct[1])), [vs[s] for s in ct[1]])['irreducibles'] for t in W.cartantype[1:]: nt=heckechartable(coxeter(t[0],len(t[1])),[vs[s] for s in t[1]]) nti['irreducibles']=kroneckerproduct(nti['irreducibles'], nt['irreducibles']) else: # now build table from heckeirrdata cc,ch,matv=heckeirrdata(ct[0],len(ct[1]),[vs[s] for s in ct[1]]) cl=conjugacyclasses(W)['reps'] lc=[len(c) for c in cl] cind=[] for w in cl: x=1 for i in w: x*=vs[i] cind.append(x) if 0ch[i]: matv.append([(-1)**lc[cc[j]]*cind[cc[j]]**2*tt1[i][j] for j in range(len(cc))]) nch.append(i1) matv=[matv[nch.index(i)] for i in range(len(nch))] if len(cc)==len(cl): nti['irreducibles']=matv else: tr=transposemat(matv) onegood=[c for c in range(len(cl)) if set(cl[c])==set(W.rank) and not c in cc] if list(onegood)!=[]: # add 1-good classes for c in onegood: xv=[] for x in ti['irreducibles']: if x[c]==0: xv.append(0) else: xv.append(x[c]*cind[c]* vs[ct[1][0]]**((len(cl[c])*x[lc.index(1)])//x[0])) tr.append(xv[:]) cc.append(c) s=len(W.rank) while len(cc)>> W=coxeter("B",2) >>> a=allwords(W); a [[], [0], [1], [0, 1], [1, 0], [0, 1, 0], [1, 0, 1], [0, 1, 0, 1]] >>> heckecharvalues(W,[v**3,v**2],a) [[ 1, 2, 1, 1, 1], [ -1, -1+v**6, -1, v**6, v**6], [ v**4, -1+v**4, -1, v**4, -1], [-v**4, 0, 1, v**10, -v**6], [-v**4, 0, 1, v**10, -v**6], [ v**4, -v**6+v**10, -1, v**16, -v**12], [-v**8, -v**4+v**10, -1, v**14, v**6], [ v**8, -2*v**10, 1, v**20, v**12]] See also 'heckechartable' and 'classpolynomials'. """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] ti=heckechartable(W,vs)['irreducibles'] maxl=max([len(w) for w in lw]) #elms=allwords(W,maxl) if clpols==[]: cpmat=allclasspolynomials(W,[p**2 for p in vs],maxl) else: cpmat=clpols lc=[] for w in lw: #cp=cpmat[elms.index(w)] cp=cpmat[W.wordcoxelm(w)] lc.append([sum(cp[j]*irr[j] for j in range(len(ti))) for irr in ti]) return lc #F heckecentraltable def heckecentraltable(W,paramL): """returns the matrix of central character values on the standard basis of the centre of a generic Iwahori-Hecke algebra. This matrix is uniquely determined by the condition that its product with the transpose of the character table of the algebra is the diagonal matrix with diagonal entries given by the Schur elements. (The current implementation uses class polynomials and, hence, will only work for W of order at most around 50000. I intend to add a more efficient version, which will also be meant to work for W of type E_8, in a later version.) >>> W=coxeter("B",2) >>> v=lpol([1],1,'v') >>> ti=heckechartable(W,[v**2,v]) >>> ct=heckecentraltable(W,[v**2,v]); ct [[1, v**(-4)+1, v**(-8),-v**(-4)-v**(-2), -v**(-8)-v**(-2)], [1, -v**(-2)+1,-v**(-6), -v**(-4)+1,v**(-6)-v**(-4)-v**(-2)+1], [1,-v**(-6)-v**(-2),v**(-12),-v**(-6)-v**(-4), v**(-10)+v**(-8)], [1, 1+v**4, 1, 1+v**2, v**2+v**4], [1, -v**(-2)-v**2,v**(-4), v**(-2)+1, -v**(-4)-v**2]] >>> matmult(ti['irreducibles'],transposemat(ct)) [[v**(-6)+2*v**(-4)+2*v**(-2)+2+v**2, 0, 0, 0, 0], [0, v**(-4)+v**(-2)+v**2+v**4, 0, 0, 0], [0, 0, v**(-12)+v**(-10)+v**(-8)+2*v**(-6)+v**(-4)+v**(-2)+1, 0, 0], [0, 0, 0, 1+v**2+v**4+2*v**6+v**8+v**10+v**12, 0], [0, 0, 0, 0, v**(-2)+2+2*v**2+2*v**4+v**6]] """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] indw=[] aw=allwords(W) ivs=[] for s in W.rank: if vs[s]==1 or vs[s]==-1: ivs.append(vs[s]) else: ivs.append(vs[s]**(-1)) for w in aw: cind=1 for i in w: cind*=ivs[i] indw.append(cind) cpmat=allclasspolynomials(W,[p**2 for p in vs]) cpmat=[cpmat[W.wordtocoxelm(w)] for w in aw] cpmat=[[indw[i]*cpmat[i][j] for j in range(len(cpmat[0]))] for i in range(len(cpmat))] cpmat=matmult(heckechartable(W,vs)['irreducibles'], matmult(transposemat(cpmat),cpmat)) x1=[chi[0] for chi in chartable(W)['irreducibles']] return [[divmod(z,x1[i])[0] for z in cpmat[i]] for i in range(len(cpmat[0]))] #F schurelmA def schurelmA(alpha,u): """returns the Schur element corresponding to the partition alpha. (Taken from the gap-chevie library.) """ l=len(alpha) lbd=[i+alpha[::-1][i] for i in range(l)] if u==1 or u==-1: u1=u else: u1=u**(-1) res=u**((l*(l-1)*(l-2))//6) for i in lbd: for j in range(i): if j in lbd: res*=u1**j else: res*=sum(u**e for e in range(i-j)) return res #F schurelmB def schurelmB(bip,v,u): """returns the Schur element corresponding to the bipartition bip. (Taken from the gap-chevie library.) """ if u==1 or u==-1: u1=u else: u1=u**(-1) if v==1 or v==-1: v1=v else: v1=v**(-1) la,mu=redlusztigsymbolB(1,1,bip); m=len(mu) if m==0: res=1 elif m==1: res=u1*(u+v) else: res=u**(((2*m+1)*m*(m-2))//3)*v**((m*(m-1))//2)*(u+v)**m for i in la: for j in range(i): if j in la: if j in mu: res*=u1**(2*j) else: if i-2*j>=1: res*=u**(i-2*j-1)*v+(u1**j) else: res*=(u1**(2*j+1-i))*v+(u1**j) else: if j in mu: res*=sum(u**e for e in range(i-j))*(u1**j) else: res*=sum(u**e for e in range(i-j))*(u**(i-j-1)*v+1) for i in mu: for j in range(i): if j in mu: if j in la: if j==0: res*=u*v1 else: res*=u1**(2*j-1)*v1 else: if i-2*j+1>=0: res*=u**(i-2*j+1)*v1+u1**j else: res*=(u1**(2*j-1-i))*v1+u1**j else: if j in la: if j==0: res*=sum(u**e for e in range(i))*u*v1 else: res*=sum(u**e for e in range(i-j))*(u1**(j-1))*v1 else: res*=sum(u**e for e in range(i-j))*(u**(i-j+1)*v1+1) if i in la: if i==0: res=divmod(res,u1*v+1)[0] else: res=divmod(res,u**(i-1)*v+u**i)[0] return res #F schurelmdata def schurelmdata(typ,n,vs): """returns the Schur elements of the Iwahori-Hecke algebra of a given type and rank with respect to a list of parameters. The data are taken from the corresponding files in gap-chevie. """ if typ[0]=='A': return [schurelmA(alpha,vs[0]) for alpha in partitions(n+1)] if typ[0]=='B' and n==2: return [schurelmB(mu,vs[1],vs[0]) for mu in partitiontuples(2,2)] if typ[0]=='C' and n==2: return [schurelmB(mu,vs[0],vs[1]) for mu in partitiontuples(2,2)] if (typ[0]=='B' or typ[0]=='C') and n>=3: return [schurelmB(mu,vs[0],vs[1]) for mu in partitiontuples(n,2)] if typ[0]=='D': vcyc=[] for mu in partitiontuples(n,2): s=schurelmB(mu,vs[0]**0,vs[0]) if mu[0]==mu[1]: vcyc.append(s) vcyc.append(s) elif mu[0]>> W=coxeter("B",2) >>> v=lpol([1],1,'v') # the built-in Lauent polynomials >>> schurelms(W,v) # equal parameters [2*v**(-2)+4+2*v**2, 2*v**(-2)+2*v**2, v**(-8)+2*v**(-6)+2*v**(-4)+2*v**(-2)+1, 1+2*v**2+2*v**4+2*v**6+v**8, 2*v**(-2)+4+2*v**2] >>> schurelms(W,[v**3,v**2]) # unequal parameters [v**(-8)+v**(-6)+v**(-4)+2*v**(-2)+1+v**2+v**4, v**(-6)+v**(-4)+v**4+v**6, v**(-20)+v**(-16)+v**(-14)+2*v**(-10)+v**(-6)+v**(-4)+1, 1+v**4+v**6+2*v**10+v**14+v**16+v**20, v**(-4)+v**(-2)+1+2*v**2+v**4+v**6+v**8] Typically, the parameters will be powers of an indeterminate (and the program has been tested for this case particularly). But it is also possible to take any non-zero and invertible elements in a commutative ring. In such a case, it may happen that the function returns an error (because it is running in difficulties with some divisions). To get around this, first compute the Schur elements generically, and then evaluate the resulting polynomials at the desired values. >>> [s.value(rootof1(6)) for s in schurelms(coxeter("B",4),v)] [0, 0, -4+4*E(6), 0, 0, 2, 1, 0, 0, -2, 4*E(6), 0, -4*E(6), 0, 4-4*E(6), 0, 0, 2, 0, 0] """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] ct=W.cartantype[0] if ct[0]=='G': schur=schurelmdata(ct[0],len(ct[1]),[vs[ct[1][0]]**2,vs[ct[1][1]]**2, vs[ct[1][0]]*vs[ct[1][1]]]) elif ct[0][0]=='I': schur=schurelmdata(ct[0],len(ct[1]),[vs[ct[1][0]],vs[ct[1][1]]]) else: schur=schurelmdata(ct[0],len(ct[1]),[vs[s]**2 for s in ct[1]]) for ct in W.cartantype[1:]: if ct[0]=='G': s1=schurelmdata(ct[0],len(ct[1]),[vs[ct[1][0]]**2,vs[ct[1][1]]**2, vs[ct[1][0]]*vs[ct[1][1]]]) elif ct[0][0]=='I': s1=schurelmdata(ct[0],len(ct[1]),[vs[ct[1][0]],vs[ct[1][1]]]) else: s1=schurelmdata(ct[0],len(ct[1]),[vs[s]**2 for s in ct[1]]) schur=flatlist([[x*sh for sh in s1] for x in schur]) return schur #F lcmschurelms def lcmschurelms(W,paramL): """returns the least common multiple of all Schur elements of the generic Iwahori-Hecke algebra associated with a finite Coxeter group and a list of parameters. In the equal parameter case, it is well-known that this is just given by the Poincare polynomial multiplied by a constant which is divisible by bad primes only. It is also known that a specialised Iwahori-Hecke algebra is semisimple if and only if this least common multiple remains non-zero under the specialisation. >>> W=coxeter("B",2) >>> v=lpol([1],1,'v') # the built-in Lauent polynomials >>> lcmschurelms(W,v) # equal parameters 2+4*v**2+4*v**4+4*v**6+2*v**8 >>> lcmschurelms(W,[v**2,v]) # unequal parameters 1+v**2+v**4+2*v**6+v**8+v**10+v**12 See also 'schurelms' and 'poincarepol'. """ if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] lcms=[] for ct in W.cartantype: equ=True vs1=[vs[s] for s in ct[1]] for p in vs1[1:]: if p!=vs1[0]: equ=False if equ==False: if ct[0][0]=='I': lprint('#I Just taking product of Schur elements\n') sh=schurelms(coxeter(ct[0],len(ct[1])),vs1) p=1 for se in sh[4:]: p*=se p=lcmcyclpol([p,sh[0],sh[1],sh[2],sh[3]]) else: p=lcmcyclpol(schurelms(coxeter(ct[0],len(ct[1])),vs1)) else: p=poincarepol(coxeter(ct[0],len(ct[1])),vs1[0]**2) if ct[0]=='B' or ct[0]=='C': d=0 while d*(d+1)<=len(ct[1]): d+=1 p=2**(d-1)*p if ct[0]=='D': d=0 while d*d<=len(ct[1]): d+=1 p=2**(d-2)*p if ct[0]=='G': p=6*p if ct[0]=='F': p=24*p if ct[0]=='E' and len(ct[1])==6: p=6*p if ct[0]=='E' and len(ct[1])==7: p=6*p if ct[0]=='E' and len(ct[1])==8: p=120*p if ct[0]=='H' and len(ct[1])==3: p=10*p if ct[0]=='H' and len(ct[1])==4: p=120*p if ct[0][0]=='I': p=int(ct[0][1:])*p lcms.append(p) return lcmcyclpol(lcms) #F cocharpol def cocharpol(W,u): c=conjugacyclasses(W) p=(u-1)**len(W.rank)*poincarepol(W,u) cp=[] for i in range(len(c['reps'])): m=[list(row) for row in W.wordtomat(c['reps'][i])] for k in W.rank: for l in W.rank: if k==l: m[k][k]=u-m[k][k]*u**0 else: m[k][l]=-m[k][l]*u**0 cp.append(divmod(p,determinantmat(m))[0]) return [(-1)**len(c['reps'][i])*c['classlengths'][i]*cp[i] for i in range(len(cp))] #F fakedegree def fakedegree(W,u,chars): """returns the fake degrees of characters of a finite Coxeter group. Given an arbitrary class function f, the fake degree is defined by (-1)^l(w) f(w) R(f)=(1/|W|) * P (u-1)^r * sum_{w in W} -------------- det(u.id - w) where r denotes the rank of W and P is the Poincare polynomial. We always have that R(f) is a polynomial in u, where the coefficients are integers if f is a character. If f is an irreducible character of W, then the b-invariant of f is defined to be the largest integer b such that u^b divides R(f). The b-invariants are contained in the result of 'chartable(W)'. >>> W=coxeter("G",2) >>> v=lpol([1],1,'v') >>> fakedegree(W,v,chartable(W)['irreducibles']) [1, v**6, v**3, v**3, v+v**5, v**2+v**4] >>> chartable(W)['b'] [0, 6, 3, 3, 1, 2] """ cp=cocharpol(W,u) return [divmod(sum(char[i]*cp[i] for i in range(len(cp))),W.order)[0] for char in chars] #F fakeomega def fakeomega(W,u): """returns the matrix of all terms u^W.N * R( chi tensor chi' tensor sign) where chi,chi' run over all irreducible characters of W; see also 'fakedegree''. The entries of this matrix are polynomials with integer coefficients; furthermore, all principal minors of this matrix are non-zero; see M. Geck and G. Malle, On special pieces in the unipotent variety. Experimental Math. 8 (1999), 281--290. One application of this matrix is the (conjectural) algorithm for computing the sizes of special pieces in the above article. It is also used in the algorithm for computing the Green functions of a finite (split) reductive group with Weyl group W. Note that the function also works for W of non-crystallographic type. """ ti=chartable(W) cp=cocharpol(W,u) om=[len(ti['b'])*[0] for i in ti['b']] for i in range(len(ti['b'])): for j in range(i,len(ti['b'])): char=[ti['irreducibles'][ti['position_sgn']][k]* ti['irreducibles'][i][k]*ti['irreducibles'][j][k] for k in range(len(ti['b']))] om[i][j]=u**W.N*divmod(sum(char[i]*cp[i] for i in range(len(cp))), W.order)[0] if i!=j: om[j][i]=om[i][j] return om #F some mod p functions needed for greenalgo def repintp(n,p): m=n%p if m>(p-1)//2: return m-p else: return m def powp(x,n,p): y=1 for i in range(n): y=(y*x)%p return y def matsubp(a,b,p): return [[(a[i][j]-b[i][j])%p for j in range(len(a[0]))] for i in range(len(a))] def matmultp(a,b,p): return [[(sum((row[k]*b[k][j])%p for k in range(len(b))))%p for j in range(len(b[0]))] for row in a] def valuep(f,x,p): if f.coeffs==[]: return 0 y=0 for i in range(len(f.coeffs)): y=((x*y)%p+f.coeffs[-i-1])%p for i in range(f.val): y=(y*x)%p return y def applychinrem(mat1,mat2,m1,m2): """apply Chinese Remainder to a matrix of lists. """ m=m1*m2 g=gcdex(m1,m2) for i in range(len(mat1)): for j in range(len(mat1)): for k in range(len(mat1[i][j])): x=(mat1[i][j][k]*g['coeff2']*m2+mat2[i][j][k]*g['coeff1']*m1)%m; if x>(m-1)//2: mat1[i][j][k]=x-m else: mat1[i][j][k]=x #F blockLR def blockLR(mat,bl,diag,p): """returns the block LR decomposition of a symmetric matrix with integer coefficients modulo a prime: P * L * P^{tr} = mat (mod p) where L is a block diagonal matrix (with blocks specified by 'bl') and P is block lower triangular with diagonal blocks consisting of scalar matrices (with scalars specified by 'diag'). If some principal minor of mat is zero modulo p, the function returns 'False'. """ P=[[[] for b in bl] for c in bl] L=[[] for b in bl] fbl=flatlist(bl) for j in range(len(bl)): if diag[j]%p==0: return False d1=1 while (d1*diag[j])%p!=1: d1+=1 Lj=[[mat[r][s] for s in bl[j]] for r in bl[j]] for k in range(j): Lj=matsubp(Lj,matmultp(P[j][k],matmultp(L[k], transposemat(P[j][k]),p),p),p) L[j]=[[(((x*d1)%p)*d1)%p for x in r] for r in Lj] invj=inversematp(L[j],p) if invj==False: return False for i in range(len(bl)): if i0: i=-i else: i=1-i lprint('\n') lprint('#I Now interpolating ') vm=inversematp([[powp(x,i,p) for x in l] for i in range(len(l))],p) if vm!=False: P,L=idmat(fbl,0),idmat(fbl,0) for i in range(len(fbl)): if i%5==0: lprint('.') for j in range(len(fbl)): if j<=i and any(m[i][j]!=0 for m in Ps): coeffs0=matmultp([[m[i][j] for m in Ps]],vm,p)[0] P[i][j]=lpol([repintp(c,p) for c in coeffs0],0,v.vname) if any(m[i][j]!=0 for m in Ls): coeffs1=matmultp([[m[i][j] for m in Ls]],vm,p)[0] L[i][j]=lpol([repintp(c,p) for c in coeffs1],0,v.vname) fertig=True lprint('\n#I Checking: ') i=0 while fertig and i>> W=coxeter("H",3) >>> v=lpol([1],1,'v') # built-in polynomials >>> specialpieces(W,v) #I Total size of all special pieces: v**30 [[("1_r'",), 1], [('3_s',), -1-v**4-v**8+v**10+v**14+v**18], [("5_r'",), v**4-v**10-v**14+v**20], [("4_r'",), v**8-v**14-v**18+v**24], [('5_r',), v**10-v**16-v**20+v**26], [("3_s'",), -v**10+v**12+v**16-v**18+v**20-v**22-v**26+v**28], [('1_r',), -v**12+v**14+v**18-v**20+v**22-v**24-v**28+v**30]] """ ti=chartable(W) a1=list(set(ti['a'])) a1.sort(reverse=True) g=greenalgo(W,v,[list(filter(lambda i:ti['a'][i]==x, range(len(ti['a'])))) for x in a1],a1) spec=[] tot=0 for i in range(len(g[2])): ch=g[2][i] if ti['a'][ch]==ti['b'][ch]: spec.append([ti['charnames'][ch],g[1][i][i]]) tot+=g[1][i][i] lprint('#I Total size of all special pieces: ') lprint(repr(tot)) lprint('\n') return spec ########################################################################## ## #Y Section 4: Kazhdan-Lusztig cells ## # class-wgraph class wgraph: """creates a W-graph (as a python `class') for a finite Coxeter group W with respect to a weight function. This consists of the following data: * a set X together with a map I which assigns to each x in X a subset I(x) of S (the set of generators of W); * a collection of elements {m_{x,y}^s} in A, where x,y in X and s in S are such that s has weight >0, s in I(x) and s is not in I(y); * a bijection s:X -> X for every s in S with weight 0. (Here, A is the ring of Laurent polynomials in one variable, v say.) These data are subject to the following requirements: we require that * v^L(s)m_{x,y}^s is an actual polynomial with constant term 0; * and m_{x,y}(v^(-1)) = m_{x,y}^s for all relevant x,y,s. Furthermore, let V be a free A-module with a basis {e_y|y in X}. For s in S, define an A-linear map rho_s: V -> V by e_y -> -v^{-L(s)} e_y if s in I(y) and s has weight >0, e_y -> v^L(s) e_y + sum_{x in X:s in I(x)} m_{x,y}^s e_{x} if s not in I(y) and s has weight >0, e_y -> e_{s.y} if s has weight 0. Then we require that the map T_s->rho_s defines a representation of the generic Iwahori-Hecke algebra associated with W,L. Recall that the quadratic relations in this algebra are given by T_s^2 = T_1 + (v^L(s) - v^(-L(s))) T_s for all s in S. The result of 'wgraph' is a class with the following components: W the underlying group W var the parameter (typically a variable v) X the base set (given as reduced expression in the case where the W-graph arises from a left cell) Isets the sets I(x) for x in X mpols the list of all possible m-values mmat a dictionary with keys given by pairs (y,x) where x,y in X and m_{x,y}^s is not 0 for at least some s. If (y,x) is such a pair, then the value will be a string pointing to the appropriate value in mpols. Xrep a hashable set in bijection with X The input to 'wgraph' can take several forms: For example, one can specify explicitly the above components. There are further possibilities; see 'relklpols' for some examples. >>> W=coxeter("G",2) >>> k=klcells(W,1,v); k #I 4 left cells >>> [wgraph(coxeter('G',2),[1,1], [[]]), wgraph(coxeter('G',2),[1,1],[[1],[0,1],[1,0,1],[0,1,0,1], [1,0,1,0,1]]), wgraph(coxeter('G',2),[1,1],[[0],[1,0],[0,1,0],[1,0,1,0], [0,1,0,1,0]]), wgraph(coxeter('G',2),[1,1],[[0,1,0,1,0,1]])] >>> [l.matrices(True) for l in k] # the correseponding left #I defining relations are true # cell representations #I defining relations are true #I defining relations are true #I defining relations are true [[[[v**2]], [[v**2]]], [[[v**2, v, 0, 0, 0], [ 0, -1, 0, 0, 0], [ 0, v, v**2, v, 0], [ 0, 0, 0, -1, 0], [ 0, 0, 0, v, v**2]], [[-1, 0, 0, 0, 0], [ v, v**2, v, 0, 0], [ 0, 0, -1, 0, 0], [ 0, 0, v, v**2, v], [ 0, 0, 0, 0, -1]]], [[[-1, 0, 0, 0, 0], [ v, v**2, v, 0, 0], [ 0, 0, -1, 0, 0], [ 0, 0, v, v**2, v], [ 0, 0, 0, 0, -1]], [[v**2, v, 0, 0, 0], [ 0, -1, 0, 0, 0], [ 0, v, v**2, v, 0], [ 0, 0, 0, -1, 0], [ 0, 0, 0, v, v**2]]], [[[-1]], [[-1]]]] See also 'reflectionwgraph', 'klcells' and 'wgraphstarorbit'. """ def __init__(self,W,weightL,xset,v,isets=[],mmat=[],mues=[],xrep=[]): if type(weightL)==type(0): self.weights=len(W.rank)*[weightL] else: self.weights=weightL if all(i==1 for i in self.weights): uneq=False else: uneq=True self.W=W self.X=xset self.var=v if isets!=[]: self.Isets=isets self.mpols=mues self.mmat=mmat self.Xrep=xrep else: ap=[W.wordtoperm(w) for w in xset['elms']] ll=[W.permlength(p) for p in ap] self.Isets=[W.leftdescentsetperm(p) for p in ap] nmues=[[0,1] for s in W.rank] mm={} for y in range(len(ap)): for x in range(y): if xset['klmat'][y][x][0]=='c': ms=xset['klmat'][y][x].split('c')[2:] mstr='' if uneq==False: for s in W.rank: if s in self.Isets[x] and not s in self.Isets[y]: if len(ms)==len(W.rank): if ms[s]!='' and ms[s]!='0': m=-(-1)**(ll[y]+ll[x])*xset['mpols'][s][int(ms[s])] if m in nmues[s]: mstr+='c'+str(nmues[s].index(m)) else: mstr+='c'+str(len(nmues[s])) nmues[s].append(m) else: mstr+='c0' else: if ms[0]!='' and ms[0]!='0': m=-(-1)**(ll[y]+ll[x])*xset['mpols'][int(ms[0])] if m in nmues[s]: mstr+='c'+str(nmues[s].index(m)) else: mstr+='c'+str(len(nmues[s])) nmues[s].append(m) else: mstr+='c0' else: mstr+='c' else: for s in W.rank: if self.weights[s]>0: if ms[s]!='' and ms[s]!='0': m=-(-1)**(ll[y]+ll[x])*xset['mpols'][s][int(ms[s])] if m in nmues[s]: mstr+='c'+str(nmues[s].index(m)) else: mstr+='c'+str(len(nmues[s])) nmues[s].append(m) else: mstr+='c0' else: # self.weights[s]=0: sy=tuple([ap[y][i] for i in W.permgens[s]]) if sy in ap and ap[x]==sy: mstr+='c1' else: mstr+='c0' if any(i!='0' and i!='c' for i in mstr): mm[(y,x)]=mstr for s in W.rank: if not s in self.Isets[y]: sy=tuple([ap[y][i] for i in W.permgens[s]]) if sy in ap: syi=ap.index(sy) mm[(y,syi)]='' for t in W.rank: if t==s: mm[(y,syi)]+='c1' else: mm[(y,syi)]+='c0' #self.X=[W.permtoword(p) for p in ap] self.X=[w[:] for w in xset['elms']] self.Xrep=[p[:len(W.rank)] for p in ap] self.mpols=nmues self.mmat=mm def __eq__(self,wgr): return self.Xrep==wgr.Xrep def __repr__(self): return 'wgraph('+repr(self.W)+', '+str(self.weights)+', '+str(self.X)+')' def normalise(self): """returns a wgraph (for the same representation) where the base set has been sorted. If the base set consists of lists, then the lists will be sorted by increasing length. Otherwise, a generic 'sort' will be applied. """ lx=self.X[:] if type(self.X[0])==type([]): lx.sort(key=(lambda x:len(x))) else: lx.sort() if lx==self.X: return self else: l=[self.X.index(x) for x in lx] l1=[l.index(i) for i in range(len(l))] x1r=[self.Xrep[i] for i in l] i1=[self.Isets[i] for i in l] m1={} for k in self.mmat.keys(): m1[(l1[k[0]],l1[k[1]])]=self.mmat[k] return wgraph(self.W,self.weights,lx,self.var,i1,m1,self.mpols,x1r) def wgraphtoklmat(self): """returns a dictionary which can be used as input to the function 'relklpols'. If G is a W-graph, then we have G=wgraph(W, 1, G.wgraphtoklmat(), v) For examples of the use of this function, see 'relklpols'. """ mat=[] for j in range(len(self.X)): mat.append(['f' for i in range(j+1)]) mues=[[0,1] for s in self.W.rank] for j in range(len(self.X)): for i in range(j): if (j,i) in self.mmat.keys(): mstr='c0' # exact value will not be used anywhere eps=-(-1)**(len(self.X[i])+len(self.X[j])) rk=self.mmat[(j,i)].split('c')[1:] for s in self.W.rank: if rk[s]!='': m=eps*self.mpols[s][int(rk[s])] if m in mues[s]: mstr+='c'+str(mues[s].index(m)) else: mstr+='c'+str(len(mues[s])) mues[s].append(m) else: mstr+='c0' mat[j][i]=mstr return {'elms':self.X,'mpols':mues,'klmat':mat} def decompose(self): """checks if a W-graph is indecomposable and, if not, returns the list of W-graphs of the indecomposable composants. """ pp0=[[w] for w in range(len(self.X))] for p in self.mmat.keys(): pp0[p[0]].append(p[1]) pp1=[p[:] for p in pp0] for z in pp1: for w in z: for y in pp0[w]: if not y in z: z.append(y) lcells=[] rest=list(range(len(self.X))) while rest!=[]: l=[x for x in pp1[rest[0]] if rest[0] in pp1[x]] l.sort() lcells.append(l) for w in l: rest.remove(w) neu=[] for l in lcells: x1=[self.X[i] for i in l] x1r=[self.Xrep[i] for i in l] i1=[self.Isets[i] for i in l] m1={} for k in self.mmat.keys(): if k[0] in l and k[1] in l: m1[(l.index(k[0]),l.index(k[1]))]=self.mmat[k] neu.append(wgraph(self.W,self.weights,x1,self.var,i1,m1,self.mpols,x1r)) return neu def matrices(self,check=False,param='generic'): """returns the representing matrices for a W-graph. Note that, here, the matrices corresponding to the elements v**weightL[s] T_s are returned. (The advantage of this convention is that no inverses of elements in the base ring are required.) If the optional argument 'check' is set to 'True', then the defining relations will be checked. There is a further optional argument 'param'. It can be used to specialise the base parameter. For example, setting param=1 yields representing matrices for W itself. """ if param=='generic': v=self.var else: v=param mats=[idmat(self.X,0) for s in self.W.rank] for s in self.W.rank: for y in range(len(self.X)): if self.weights[s]>0 and s in self.Isets[y]: mats[s][y][y]=-1 else: if self.weights[s]>0: mats[s][y][y]=v**(2*self.weights[s]) for x in range(len(self.X)): if self.weights[s]==0 or s in self.Isets[x]: if (y,x) in self.mmat.keys(): mats[s][y][x]=v**(self.weights[s])*self.mpols[s][ int(self.mmat[y,x].split('c')[s+1])] if check!=False: lprint('#I defining relations are ') for s in self.W.rank: sq=v**(2*self.weights[s]) if matmult(mats[s],mats[s])!=matadd(idmat(self.X,sq), scalmatmult(sq-1,mats[s])): lprint('Mist1!\n'); return 'False1' for t in range(s+1,len(self.W.rank)): a=matmult(mats[s],mats[t]) b=matmult(mats[t],mats[s]) m=self.W.coxetermat[s][t] if m%2==0: a1,b1=a,b for i in range(m//2-1): a1,b1=matmult(a1,a),matmult(b1,b) else: a1,b1=a,b for i in range((m-1)//2-1): a1,b1=matmult(a1,a),matmult(b1,b) a1,b1=matmult(a1,mats[s]),matmult(b1,mats[t]) if a1!=b1: lprint('Mist2!\n') return 'False2' lprint('true\n') return mats def character(self,v=1): """returns the character of the underlying Coxeter group afforded by the W-graph representation. The values are on representatives of minimal length in the conjugacy classes of W, as returned by 'conjugacyclasses'. The result will be added as component 'char' to the wgraph class. >>> W=coxeter("A",3) >>> [l.character() for l in klcells(w,1,v)] [[1,1,1,1,1], [3,1,-1,0,-1], [3,1,-1,0,-1], [2,0,2,-1,0], [3,-1,-1,0,1], [3,1,-1,0,-1], [2,0,2,-1,0], [3,-1,-1,0,1], [3,-1,-1,0,1], [1,-1,1,1,-1]] Thus, the left cells 0, 1, 3, 4, 9 have pairwise different characters and, hence, yield a full set of irreducible representations of W. (It is known that, in type A, all left cell representations are irreducible.) """ m=self.matrices(param=v) c=[len(m[0])] for w in conjugacyclasses(self.W)['reps'][1:]: c.append(sum([reduce(matmult,[m[s] for s in w])[i][i] for i in range(c[0])])) self.char=c return c # end of definition of class wgraph # reflectionwgraph def reflectionwgraph(W,weightL,v): """returns the W-graph corresponding to the reflection representation of a Coxeter group W with respect to a weight function. >>> v=lpol([1],1,[v]) >>> reflectionwgraph(coxeter("A",2),1,v).matrices(True) [[[-v**(-1), 0], [1, v]], [[v, 1], [0, -v**(-1)]]] >>> reflectionwgraph(coxeter("G",2),[7,1],v).matrices(True) [[[-v**(-7), 0], [v**(-6)+1+v**6, v**7]], [[v, 1], [0, -v**(-1)]]] (The optional argument 'True' forces the function to check the defining relations for the representing matrices.) For the conventions regarding the argument specifying the weights, see the help of 'ainvariants' for further explanation. See also 'wgraph'. """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL mues=[[0,1] for s in W.rank] mmat={} for y in W.rank: for x in W.rank: if W.coxetermat[x][y]!=2 and (poids[x]0: return f elif len(f.coeffs)>-f.val: return lpol([f.coeffs[i] for i in range(-f.val+1,len(f.coeffs))], 1,f.vname) else: return 0 def nonnegpart(f): if type(f)==type(0): return f elif f.val>=0: return f elif len(f.coeffs)>=-f.val: return lpol([f.coeffs[i] for i in range(-f.val,len(f.coeffs))], 0,f.vname) else: return 0 def zeropart(f): if type(f)==type(0): return f elif f.val<=0 and len(f.coeffs)>-f.val: return f.coeffs[-f.val] else: return 0 def barpart(f): if type(f)==type(0): return f else: return lpol(f.coeffs[::-1],-f.degree,f.vname) # klpolynomials def klpolynomials(W,weightL,v): """returns the matrix of all Kazhdan-Lusztig polynomials, and further information on the corresponding left cells, with respect to a given weight function. The result is a dictionary with components: elms : all elements of W (as reduced words, in increasing order) klpols : the Kazhdan-Lusztig polynomials mpols : the mue-polynomials klmat : a matrix indexed by pairs of elements of W, whose entries are strings encoding information on Kazhdan-Lusztig and mue polynomials. If y<=w (Bruhat-Chevalley order), then klmat[w][y] is of the form 'c

cc ...' where

refers to a polynomial in 'klpols' and , refer to the polynomials in 'mpols' for the generators labelled by 0,1,... Otherwise, klmat[w][y] equals 'f'. arrows : a complete list of all pairs (w,y) where y,w in W are such that C_y occurs in C_sC_w for some simple reflection s. lcells : the partition of W into left cells duflo : the corresponding distinguished involutions, together with their a-invariants and the sign n_d. lorder : the partial order on left cells (given as an incidence matrix) lcells : the partition of W into left cells rcells : the partition of W into right cells tcells : the partition of W into two-sided cells As in 'ainvariants', a weight function is given by a sequence of non-negative integers corresponding to the simple reflections of W, where weights for simple reflections which are conjugate in W have to be equal. This gives rise to a weight function L from W to the integers in the sense of Lusztig; given w in W, we have L(w) = weightL[s_1] + weightL[s_2] + ... + weightL[s_k] where w=(s_1,...,s_k) is a reduced expression for w. It is allowed that weightL is just an integer, in which case all weights will be set equal to that integer. >>> W=coxeter("B",2) >>> kl=klpolynomials(W,[2,1],v) #I Number of elements = 8 #I Initialising (Bruhat-Chevalley order etc.) .... #I Computing KL polynomials for elements of length: #I 1 2 3 4 #I 10 arrows >> 6 left cells >> checks are True >>> kl['klpols'] [1, 1-v**2, 1+v**2] # negative coefficients do occur! >>> kl['lcells'] [[0], [1, 4], [2], [3, 6], [5], [7]] # elements represented by # their index in 'elms' >>> [[kl['elms'][w] for w in c] for c in kl['lcells']] [[[]], [[0], [1, 0]], [[1]], [[0, 1], [1, 0, 1]], [[0, 1, 0]], [[0, 1, 0, 1]]] >>> kl['elms'][5] [0, 1, 0] >>> kl['elms'][0] [] >>> kl['klmat'][5][0] 'c1cc' >>> kl['klpols'][int(kl['klmat'][5][0][1])] 1-v**2 (Thus, we have P_{[],[0,1,0]}=1-v**2.) In general, P_{elms[y],elms[w]} is given by kl['klpols'][int(kl['klmat'][w][y].split('c')[1])] If weightL[s]>0 and sy>> kl['duflo'] [[0, 0, 1], [1, 2, 1], [2, 1, 1], [6, 2, 1], [5, 3, -1], [7, 6, 1]] Here, each triple consists of d,a(d),n_d where d is the index of the distinguished involution in the list 'elms', a(d) is the degree of the Kazhdan-Lusztig polynomial P_{1,d} and n_d the coefficient of the highest power of v in P_{1,d}. In the course of the computation, it is checked if n_d is 1 or -1, and also if the function w -> a(w) reaches its mimumum at exactly one element of a left cell (which is supposed to be the involution d). -- No counter examples are known! If one is merely interested in the partition of W into left cells (and not in knowing all the Kazhdan-Lusztig polynomials), then it is much more efficient to use the function 'klcells'. See also 'klcells', 'relklpols' and 'wgraph'. """ if type(weightL)==type([]): poids=weightL else: poids=len(W.rank)*[weightL] if all(i==1 for i in poids): uneq=False else: uneq=True ap=allwords(W) Lw=[sum([poids[s] for s in w]) for w in ap] lw=[len(w) for w in ap] a=[W.wordtocoxelm(c) for c in ap] inva=[a.index(W.wordtocoxelm(c[::-1])) for c in ap] inva1=[a[c] for c in inva] w0=longestperm(W) aw0=[a.index(tuple([w0[i] for i in p])) for p in a] lprint('#I Initialising (Bruhat-Chevalley order etc.) ') lft=[[inva1.index(tuple([s[i] for i in p])) for s in W.permgens] for p in inva1] mat=[['c0'+len(W.rank)*'c']] for w in range(1,len(a)): if lw[w]>lw[w-1]: lprint('.') s=0 while lft[w][s]>w: s+=1 b=['c'] for y in range(1,w): if lw[y]==lw[w]: b.append('f') elif lw[w]+lw[y]>W.N: b.append(mat[aw0[y]][aw0[w]]) else: if (lft[y][s]y and y<=lft[w][s] and mat[lft[w][s]][y]=='c'): b.append('c') else: b.append('f') b.append('c') mat.append(b[:]) lprint('\n#I Computing KL polynomials for elements of length:\n') lprint('#I ') klpol=[1] klstar=[1] mues=[[0] for s in W.rank] for w in range(1,len(a)): if lw[w]>lw[w-1]: lprint(str(lw[w])+' ') for y in range(w,-1,-1): if mat[w][y][0]=='c': if y==w: h=1 elif inva[w]y): h=klpol[int(mat[inva[w]][inva[y]].split('c')[1])] else: s=0 while sw): s+=1 if siw): s+=1 if sw: s+=1 if uneq: for t in W.rank: if lft[w][t]0 and lft[y][s]w: if lw[y]+lw[w]>W.N: if (lw[w]-lw[y])%2==0: m=-mues[s][int(mat[aw0[y]][aw0[w]].split('c')[s+2])] else: m=mues[s][int(mat[aw0[y]][aw0[w]].split('c')[s+2])] elif poids[s]==1: m=zeropart(v**(1+Lw[y]-Lw[w])*h) else: m=nonnegpart(v**(poids[s]+Lw[y]-Lw[w])*h) for z in range(w-1,y,-1): if lft[z][s]w: if not m in mues[s]: mat[w][y]+='c'+str(len(mues[s])) mues[s].append(m) else: mat[w][y]+='c'+str(mues[s].index(m)) else: mat[w][y]+='c' lprint('\n#I ') pp=[] for w in range(len(a)): for s in W.rank: if poids[s]==0 or (lft[w][s]>w and poids[s]>0): pp.append((w,lft[w][s])) for y in range(w): if mat[w][y][0]=='c': if any(poids[s]>0 and lft[y][s]w and mues[s][int(mat[w][y].split('c')[s+2])]!=0 for s in W.rank): pp.append((w,y)) lprint(str(len(pp))+' arrows ') adelta=[] ndelta=[] for w in range(len(a)): p=v**(-Lw[w])*klpol[int(mat[w][0].split('c')[1])] if p==0: adelta.append(-1) ndelta.append(0) else: adelta.append(-p.degree) ndelta.append(p.coeffs[-1]) lprint('>') pp0=[[w] for w in range(len(a))] for p in pp: pp0[p[0]].append(p[1]) pp1=[p[:] for p in pp0] for z in pp1: for w in z: for y in pp0[w]: if not y in z: z.append(y) z.sort() lprint('>') rest=list(range(len(a))) lcells=[] duflo=[] checks=True while rest!=[]: l=[x for x in pp1[rest[0]] if rest[0] in pp1[x]] i0=0 while ndelta[l[i0]]==0: i0+=1 d=l[i0] for w in l[i0:]: if ndelta[w]!=0 and adelta[w]1: checks=False if not (ndelta[d]==1 or ndelta[d]==-1): checks=False duflo.append([d,adelta[d],ndelta[d]]) lcells.append(l) for w in l: rest.remove(w) lprint(' '+str(len(lcells))+' left cells ') lorder=[[d2[0] in pp1[d1[0]] for d2 in duflo] for d1 in duflo] for c1 in range(len(lcells)): for c2 in range(len(lcells)): if c1!=c2 and lorder[c1][c2]==True and duflo[c1][1]>=duflo[c2][1]: checks=False lprint('>') rcells=[[inva[w] for w in l] for l in lcells] il,ir=[],[] for w in range(len(a)): i=0 while not w in lcells[i]: i+=1 il.append(i) i=0 while not w in rcells[i]: i+=1 ir.append(i) rest=list(range(len(a))) tcells=[] while rest!=[]: t=[rest[0]] for w in t: for y in rest: if (not y in t) and (il[w]==il[y] or ir[w]==ir[y]): t.append(y) t.sort() tcells.append(t) for w in t: rest.remove(w) lprint('> checks are '+str(checks)+'\n') return {'elms':ap,'klpols':klpol,'mpols':mues,'klmat':mat,'arrows':pp, 'lcells':lcells,'duflo':duflo,'lorder':lorder, 'rcells':rcells, 'tcells':tcells,'klstar':klstar} def klpoly1(W,weightL,v): """returns the left cells in a form which can be used as input to the function 'wgraph'. See also 'klpolynomials' and 'wgraph'. """ k=klpolynomials(W,weightL,v) return [{'elms':[k['elms'][x] for x in c], 'mpols':k['mpols'],'klpols':k['klpols'], 'klmat':[[k['klmat'][c[w]][c[y]] for y in range(w+1)] for w in range(len(c))]} for c in k['lcells']] def relmue(lw,ly,p): if p==0: return 0 elif type(p)==type(0): if lw-ly==1: return p else: return 0 elif p.degree==lw-ly-1: return p.coeffs[-1] else: return 0 #F relklpols def relklpols(W,W1,cell1,weightL,q): """returns the matrix of relative Kazhdan-Lusztig polynomials with respect to a left cell in a parabolic subgroup, following M. Geck, On the induction of Kazhdan--Lusztig cells, Bull. London Math. Soc. 35 (2003), 608--614. (This version is for equal parameters only.) More precisely, let W be a Coxeter group with generating S. Let J be a subset of S and W1 be the parabolic subgroup generated by J. Let X be the set of minimal left coset representatives of W1 in W. For y in X and v in W1, we can write uniquely C_{yv}' = T_yC_v' + sum_{x,u} p_{xu,yv}^* T_xC_u' where the sum runs over all x in X and u in W1 such that x>> W=coxeter("A",3); W1=reflectionsubgroup(W,[0,1]) >>> k1=klcells(W1,1,v); k1 [wgraph(coxeter('A',2), [1, 1], [[]]), wgraph(coxeter('A',2), [1, 1], [[1], [0, 1]]), wgraph(coxeter('A',2), [1, 1], [[0], [1, 0]]), wgraph(coxeter('A',2), [1, 1], [[0, 1, 0]])] (Thus, W1 of type A2 has 4 left cells: {}, {1,01}, {0,10}, {010}.) We induce the first left cell to W and decompose the associated W-graph into its indecomposable components: >>> r=relklpols(W,W1,k1[0].wgraphtoklmat(),1,v) >>> G=wgraph(W,1,r,v).decompose() [wgraph(coxeter('A',3), [1, 1, 1], [[]]), wgraph(coxeter('A',3), [1, 1, 1], [[2], [1, 2], [0, 1, 2]])] (Thus, the induced graph has 2 components.) See also 'klpolynomials', 'klcells', 'wgraph' and 'allrelklpols'. """ if type(weightL)==type([]): poids=weightL else: poids=len(W.rank)*[weightL] if all(i==1 for i in poids): uneq=False else: uneq=True J=W1.fusions[W.cartanname]['subJ'] X1w=[W.coxelmtoword(c) for c in redleftcosetreps(W,J)] X1=[W.wordtoperm(w) for w in X1w] Lw=[sum([poids[s] for s in w]) for w in X1w] lft=[] for s in W.rank: ls=[] for w in X1: sw=tuple([w[i] for i in W.permgens[s]]) if sw in X1: ls.append(X1.index(sw)) else: t=0 while tuple([W.permgens[t][i] for i in w])!=sw: t+=1 ls.append(-t-1) lft.append(ls) Lw1=[sum([poids[J[s]] for s in w]) for w in cell1['elms']] p1=[W1.wordtoperm(w) for w in cell1['elms']] lft1={} for t in W1.rank: l=[] for w in p1: w1=tuple([w[i] for i in W1.permgens[t]]) if w1 in p1: l.append(p1.index(w1)) else: if w[t]>=W1.N: # tww l.append(len(p1)) lft1[J[t]]=l bruhatX=[] for y in range(len(X1)): bruhatX.append([bruhatperm(W,X1[x],X1[y],lx=Lw[x],ly=Lw[y]) for x in range(y+1)]) mat={} mues=[0,1] for y in range(len(X1)): for x in range(y): if bruhatX[y][x]: mat[y,x]=[len(p1)*['f'] for i in range(len(p1))] for v in range(len(p1)): for u in range(len(p1)): if (x==y and u==v) or Lw[x]+Lw1[u]x] fs1=[s1 for s1 in ldy if 0<=lft[s1][x]0: # case syx and sx in X s=fs[0] for v in range(len(p1)): for u in range(len(p1)): if mat[y,x][v][u][0]=='c': if bruhatX[y][lft[s][x]] and mat[y,lft[s][x]][v][u][0]=='c': mat[y,x][v][u]+=mat[y,lft[s][x]][v][u].split('c')[1] rk=mat[y,x][v][u].split('c')[1] if rk!='0': m=relmue(Lw[y]+Lw1[v],Lw[x]+Lw1[u],rklpols[int(rk)]) if m in mues: mat[y,x][v][u]+='c'+str(mues.index(m)) else: mat[y,x][v][u]+='c'+str(len(mues)) mues.append(m) else: mat[y,x][v][u]+='c0' else: mat[y,x][v][u]+='0c0' else: for u in range(len(p1)): if any(lft[s1][x]<0 and u0: s=fs1[0] #lprint('!') else: s=ldy[0] sx,sy=lft[s][x],lft[s][y] for v in range(len(p1)): if mat[y,x][v][u][0]=='c': h=0 for z in range(x,sy): sz=lft[s][z] if sz=0 or lft1[-1-sz][w]w and (mat[sy,x][v][w][0]=='c' and cell1['klmat'][w][u][0]=='c'): m=mues[int(mat[0,0][w][u].split('c')[2])] if m!=0: rk=mat[sy,x][v][w].split('c')[1] if rk!='0': h+=q**(Lw1[w]-Lw1[u]+1)*rklpols[int(rk)]*m else: # case sx=W1.N: # tww l.append(len(p1)) lft1[J[t]]=l bruhatX=[] for y in range(len(X1)): bruhatX.append([bruhatperm(W,X1[x],X1[y],lx=len(X1w[x]), ly=len(X1w[y])) for x in range(y+1)]) mues=[[0,1] for s in W.rank] rklpols=[0,1] mat={} for y in range(len(X1)): for x in range(y): if bruhatX[y][x]: mat[y,x]=[len(p1)*['f'] for i in range(len(p1))] for v in range(len(p1)): for u in range(len(p1)): if len(X1w[x])+lw1[u]0 and lft[s][y]<0: t=-1-lft[s][y] if lft1[t][i]>i and lft1[t][j]x and poids[s]>0] fs1=[s1 for s1 in ldy if 0<=lft[s1][x]0] if len(fs0)>0: # case sy=0: if sx<=sy and bruhatX[sy][sx] and mat[sy,sx][v][u][0]=='c': mat[y,x][v][u]+=mat[sy,sx][v][u].split('c')[1] else: mat[y,x][v][u]+='0' else: tu=lft1[-1-sx][u] if 0<=tu0: # case syx and sx in X s=fs[0] for v in range(len(p1)): for u in range(len(p1)): if mat[y,x][v][u][0]=='c': if bruhatX[y][lft[s][x]] and mat[y,lft[s][x]][v][u][0]=='c': mat[y,x][v][u]+=mat[y,lft[s][x]][v][u].split('c')[1] else: mat[y,x][v][u]+='0' else: for u in range(len(p1)): if any(lft[s1][x]<0 and u0: s=fs1[0] #lprint('!') else: s=ldy[0] sx,sy=lft[s][x],lft[s][y] for v in range(len(p1)): if mat[y,x][v][u][0]=='c': h=0 for z in range(x,sy): sz=lft[s][z] if sz=0 or lft1[-1-sz][w]w and (mat[sy,x][v][w][0]=='c' and cell1['klmat'][w][u][0]=='c'): m1=cell1['mpols'][J.index(t)][int(cell1['klmat'][ w][u].split('c')[J.index(t)+2])] if m1!=0: rk=mat[sy,x][v][w].split('c')[1] if rk!='0': h+=q**(Lw1[w]-Lw1[u]+poids[t])*rklpols[int(rk)]*m1 else: # case sx0 and (lft[r][y]>y or (lft[r][y]<0 and v=0 and mat[y,x][v][lft1[t][u]][0]=='c': # rk=mat[y,x][v][lft1[t][u]].split('c')[1] # if rk!='0': # pis-=nonnegpart(q**(Lw1[u]+Lw[x]-Lw1[v]-Lw[y]- # poids[t])*rklpols[int(rk)]) for w in range(u+1,len(p1)): if w>> W=coxeter("B",2) >>> relklpols(W,[0],1,v) {'allelms': [[],[0],[1],[0,1],[1,0],[0,1,0],[1,0,1],[0,1,0,1]], 'elmsX': [[],[1],[0,1],[1,0,1]], 'elmsJ': [[],[0]], 'klpols': [1], 'rklpols': [0, 1], 'mues': [0, 1], 'relklmat': {(0, 0): [['c1c0', 'f' ], ['c0c1', 'c1c0']], (1, 0): [['c1c1', 'f' ], ['c0c0', 'c1c1']], (1, 1): [['c1c0', 'f' ], ['c0c1', 'c1c0']], (2, 0): [['c0c0', 'c1c1'], ['c0c0', 'c1c0']], (2, 1): [['c1c1', 'f' ], ['c0c0', 'c1c1']], (2, 2): [['c1c0', 'f' ], ['c0c1', 'c1c0']], (3, 0): [['c0c0', 'c1c0'], ['c0c0', 'c1c0']], (3, 1): [['c0c0', 'c1c1'], ['c0c0', 'c1c0']], (3, 2): [['c1c1', 'f' ], ['c0c0', 'c1c1']], (3, 3): [['c1c0', 'f' ], ['c0c1', 'c1c0']], 'arrows': [(0,1), (0,2), (1,4), (2,3), (4,5), (4,1), (3,6), (3,2), (5,7), (5,4), (6,7), (6,3)]} (Conventions in relklmat similar to those in 'klpolynomials'. See also 'klpolynomials' and 'klcells'. """ if type(weightL)==type([]): poids=weightL else: poids=len(W.rank)*[weightL] if all(i==1 for i in poids): uneq=False else: uneq=True ap=allwords(W) W1=reflectionsubgroup(W,J) m1=klpolynomials(W1,[poids[s] for s in J],q) wa1=[[J[s] for s in c] for c in m1['elms']] a1=[W.wordtoperm(w) for w in wa1] X1=[W.coxelmtoperm(c) for c in redleftcosetreps(W,J)] Lw1=[sum([poids[s] for s in w]) for w in wa1] X1w=[W.permtoword(p) for p in X1] Lw=[sum([poids[s] for s in w]) for w in X1w] lft=[] for s in W.rank: ls=[] for w in X1: sw=tuple([w[i] for i in W.permgens[s]]) if sw in X1: ls.append(X1.index(sw)) else: t=0 while tuple([W.permgens[t][i] for i in w])!=sw: t+=1 ls.append(-t-1) lft.append(ls) lft1={} for t in J: lft1[t]=[a1.index(tuple([w[i] for i in W.permgens[t]])) for w in a1] mat={} lprint('#I Initialising ') mues=[0] for y in range(len(X1)): if y%100==0: lprint('.') for x in range(y): if bruhatperm(W,X1[x],X1[y]): #if x==y or Lw[x]x] fs1=[s1 for s1 in ldy if 0<=lft[s1][x]0: # case syx and sx in X s=fs[0] for v in range(len(a1)): for u in range(len(a1)): if mat[y,x][v][u][0]=='c': if mat[y,lft[s][x]][v][u][0]=='c': mat[y,x][v][u]+=mat[y,lft[s][x]][v][u].split('c')[1] rk=mat[y,x][v][u].split('c')[1] if rk!='0': m=relmue(Lw[y]+Lw1[v],Lw[x]+Lw1[u],rklpols[int(rk)]) if m in mues: mat[y,x][v][u]+='c'+str(mues.index(m)) else: mat[y,x][v][u]+='c'+str(len(mues)) mues.append(m) else: mat[y,x][v][u]+='c0' else: mat[y,x][v][u]+='0c0' else: for u in range(len(a1)): if any(lft[s1][x]<0 and lft1[-1-lft[s1][x]][u]>u for s1 in ldy): for v in range(len(a1)): if mat[y,x][v][u][0]=='c': mat[y,x][v][u]+='0c0' else: #fs1=list(filter(lambda s2:lft[s2][x]<0 and # lft1[-1-lft[s2][x]][u]0: s=fs1[0] #lprint('!') else: s=ldy[0] sx,sy=lft[s][x],lft[s][y] for v in range(len(a1)): if mat[y,x][v][u][0]=='c': h=0 for z in range(x,sy): if lft[s][z]=0 or lft1[-1-lft[s][z]][w]w and (mat[sy,x][v][w][0]=='c' and m1['klmat'][w][u][0]=='c'): m=mues[int(mat[0,0][w][u].split('c')[2])] if m!=0: rk=mat[sy,x][v][w].split('c')[1] if rk!='0': h+=q**(Lw1[w]-Lw1[u]+1)*rklpols[int(rk)]*m else: # case sxy: pp.append((bij[y,v],bij[lft[s][y],v])) elif lft[s][y]<0 and lft1[-1-lft[s][y]][v]>v: pp.append((bij[y,v],bij[y,lft1[-1-lft[s][y]][v]])) for x in range(y+1): for u in range(len(a1)): rk=mat[y,x][v][u] if rk[0]=='c' and rk.split('c')[2]!='0': if any((0<=lft[s][x]y or (lft[s][y]<0 and lft1[-1-lft[s][y]][v]>v)) for s in W.rank): pp.append((bij[y,v],bij[x,u])) lprint(str(len(pp))+' arrows \n'); klpols=[] for y in range(len(X1)): for x in range(y+1): for v in range(len(a1)): for u in range(len(a1)): if x==y and u<=v and m1['klmat'][v][u][0]=='c': h=m1['klpols'][int(m1['klmat'][v][u].split('c')[1])] else: rk=mat[y,x][v][u] if rk[0]=='c': h=rklpols[int(rk.split('c')[1])] for w in range(u+1,len(a1)): rk1=m1['klmat'][w][u] if rk1[0]=='c': rk2=mat[y,x][v][w] if rk2[0]=='c' and rk2[1]!='0': h+=m1['klpols'][int(rk1.split('c')[1])]*rklpols[ int(rk2.split('c')[1])] if h!=0 and not h in klpols: klpols.append(h) return {'allelms':ap,'elmsJ':wa1,'elmsX':X1w,'rklpols':rklpols, 'mues':mues,'relklmat':mat,'klpols':klpols,'arrows':pp} #F klstaroperation def klstaroperation(W,s,t,pcell): """returns the list containing the elements w^* for w in cell, where w^* is obtained by the Kazhdan-Lusztig star operation with respect to the generators s,t in S such that st has order 3. Here, cell is assumed to be a list of elements in W which all have the same right descent set (and which are given as full permutations). The function returns 'False' if the star operation with respect to s,t is not defined for the elements in the given set. >>> W=coxeter("D",4); W.coxetermat [[1, 2, 3, 2], [2, 1, 3, 2], [3, 3, 1, 3], [2, 2, 3, 1]] >>> k=klcells(W,1,v);k[0][2] >>> k[0][2] # example of a left cell c[[3], [2, 3], [0, 2, 3], [1, 2, 3]] >>> klstaroperation(W,0,2,[W.wordtoperm(p) for p in k[0][2]]); False >>> klstaroperation(W,1,2,[W.wordtoperm(p) for p in k[0][2]]) False >>> st=klstaroperation(W,2,3,[W.wordtoperm(p) for p in k[0][2]]) >>> st==False False >>> [W.permtoword(p) for p in st] [[3, 2], [2], [0, 2], [1, 2]] See also 'klstarorbit' and 'wgraphstarorbit'. """ pw1=perminverse(pcell[0]) if (pw1[s]>=W.N and pw1[t]>=W.N) or (pw1[s]=W.N and wsi[t]=W.N and wsi[s]>> W=coxeter("A",3);k=klcells(W,1,v) >>> k[1] wgraph(coxeter('A',3), [1, 1, 1], [[2], [1, 2], [0, 1, 2]]) >>> klstarorbit(W,k[1].X) [[[2], [1, 2], [0, 1, 2]], [[2, 1], [1], [0, 1]], [[2, 1, 0], [1, 0], [0]]] See also 'klstaroperation' and 'klcells'. """ if gens=='each': gens=list(W.rank) orb=[[W.wordtoperm(x) for x in l]] for cell in orb: for s in range(len(gens)): for t in range(s): if W.coxetermat[gens[s]][gens[t]]==3: nc=klstaroperation(W,gens[s],gens[t],cell) if nc!=False and not any(nc[0] in c for c in orb): orb.append(nc) return [[W.permtoword(p) for p in o] for o in orb] #F klstarorbitperm def klstarorbitperm(W,l,gens='each'): """same as klstarorbit but the function returns the elements as full permutations. """ if gens=='each': gens=list(W.rank) orb=[[W.wordtoperm(x) for x in l]] for cell in orb: for s in range(len(gens)): for t in range(s): if W.coxetermat[gens[s]][gens[t]]==3: nc=klstaroperation(W,gens[s],gens[t],cell) if nc!=False and not any(nc[0] in c for c in orb): orb.append(nc) return orb #F leftklstar def leftklstar(W,pw,s,t): """applies the left star operation with respect to generators s and t to an element pw; here, it is already assumed that the product st has order 3 and that the star operation is known to apply to pw. """ if pw[s]>=W.N and pw[t]=W.N: return sw else: return tuple([pw[i] for i in W.permgens[t]]) else: sw=tuple([pw[i] for i in W.permgens[t]]) if sw[s]>=W.N: return sw else: return tuple([pw[i] for i in W.permgens[s]]) #F generalisedtau def generalisedtau(W,pw,maxd=10): """returns Vogan's generalised tau-invariant of an element of a finite Coxeter group. It is known that two elements which belong to the same left cell (in the equal parameter case) must have the same generalised tau-invariant. The optional argument 'maxd' can be used to set to specify the depth to which the star operations will be applied (default 10). """ #pw=W.wordtoperm(w) orb=[pw] o=0 while o1: return [[l[i] for i in range(len(l)) if rnd[i]==s] for s in srnd] elif not any(nc[0] in c for c in orb): orb.append(nc) return [l] def gentauorbits(W,startset=[],pr=True): """returns the partition of a set of elements into equivalence classes under the relation given by Vogan's generalised tau-invariant (which amounts to repeated application of the star operations). If the startset is not specified, then all elements of W will be taken. >>> W=coxeter("E",6) >>> g=gentauorobits(W) >>> len(g) 652 (In this case, the result is precisely the partition of W into left cells. The same also happens for W of type A.) See also 'klstaroperation', 'klstarorbit' and 'precells'. """ if startset==[]: pset=[W.wordtoperm(w) for w in allwords(W)] else: if type(startset[0])==type(W.permgens[0]) and len(startset[0])==2*W.N: pset=startset else: pset=[W.wordtoperm(w) for w in startset] rd=[tuple(W.rightdescentsetperm(pw)) for pw in pset] rest=[[pset[i] for i in range(len(pset)) if rd[i]==s] for s in set(rd)] res=[] weiter=True if pr: lprint('#I tau-ells: ') while weiter: cg=[gentauorbit2(W,i) for i in rest] weiter=False rest=[] for i in range(len(cg)): if len(cg[i])==1: #res.append([W.permtoword(w) for w in cg[i][0]]) res.append(cg[i][0]) else: rest.extend(cg[i]) weiter=True if pr: lprint(str(len(res))+' ') if pr: lprint('\n') return res # klcellw0 def klcellw0(W,wgr): """returns the W-graph of a left cell multiplied by the longest element. (For the time being, only for equal parameters.) """ w0=longestperm(W) pc=[W.wordtoperm(w) for w in wgr.X] np=[permmult(p,w0) for p in pc] if np[0] in pc: return wgr else: ni=[W.leftdescentsetperm(p) for p in np] nmat={} for k in wgr.mmat.keys(): nmat[(k[1],k[0])]=wgr.mmat[k] return wgraph(W,wgr.weights,[W.permtoword(p) for p in np],wgr.var, ni,nmat,wgr.mpols,[p[:len(W.rank)] for p in np]).normalise() # wgraphstarorbit def wgraphstarorbit(W,wgr,gens='each'): """returns the orbit of a W-graph under the relation generated by the Kazhdan-Lusztig star operation. (Only works in the case of equal parameters.) >>> W=coxeter("A",2); k=klcells(W,1,v); k #I 4 left cells (3 non-equivalent) [[[[]], [[0, 1, 0]], [[1], [0, 1]], [[1, 0], [0]]], [wgraph(coxeter('A',2), [1, 1], [[]]), wgraph(coxeter('A',2), [1, 1], [[0, 1, 0]]), wgraph(coxeter('A',2), [1, 1], [[1], [0, 1]])]] >>> flatlist([wgraphstarorbit(W,g) for g in k[1]]) [wgraph(coxeter('A',2), [1, 1], [[]]), wgraph(coxeter('A',2), [1, 1], [[0, 1, 0]]), wgraph(coxeter('A',2), [1, 1], [[1], [0, 1]]), wgraph(coxeter('A',2), [1, 1], [[0], [1, 0]])] See also 'klstaroperation', 'wgraph' and 'klcells'. """ return [wgraph(W,wgr.weights,[W.permtoword(p) for p in l],wgr.var,wgr.Isets, wgr.mmat,wgr.mpols,[p[:len(W.rank)] for p in l]).normalise() for l in klstarorbitperm(W,wgr.X,gens)] #F klcellsun def klcellsun(W,weightL,v,pr=True): if type(weightL)==type([]): poids=weightL else: poids=len(W.rank)*[weightL] if len(W.rank)==0: cr1=[wgraph(W,poids,[[]],v,[[]],{},[],[()])] else: J=list(W.rank) if W.cartantype[0][0]=='E': J.remove(0) else: J.remove(len(W.rank)-1) W1=reflectionsubgroup(W,J) kk=klcellsun(W1,[poids[s] for s in J],v,pr=False) if len(W.rank)>0: lprint('#I ') lprint('('+str(len(kk))+') ') cr1=[] allmues=[[] for s in W.rank] for i in kk: lprint('+') rk=relklpolsuneq(W,W1,i.wgraphtoklmat(),poids,v) for s in W.rank: for m in rk['mpols'][s]: if m!=0 and not m in allmues[s]: allmues[s].append(m) ind=wgraph(W,poids,rk,v).decompose() lprint(str(len(ind))) for ii in ind: cr1.append(ii) lprint('\n') if pr==True and len(W.rank)>0: lprint('#I '+str(len(cr1))+' left cells, mues: ') for s in W.rank: lprint('['+', '.join([repr(i) for i in allmues[s]])+'] ') lprint('\n') cr1.sort(key=(lambda c:len(c.X))) return cr1 #F klcells def klcells(W,weightL,v,allcells=True,pr=True): """returns the partition of a finite Coxeter group into left cells together with the corresponding W-graphs. In the equal parameter case (where all weights are equal to 1), the function returns a pair [l,l1] where l is a list describing the partition of W into left cells and l1 is a list containing the W-graphs for a set of a representatives of the left cells under the equivalence relation given by the star operation. (It is known that star equivalent left cells give rise to the same W-graphs.) The computation is done recursively, using induction of left cells from proper parabolic subgroups (see the function 'relklpols'). This works very efficiently for groups of rank up to 6, including types H4 and E6. If one is willing to wait for a few hours, then type E7 is also possible. If the optional argument 'allcells' is set to 'False', then for each left cell the function only returns those elements whose inverses also lie in that left cell. In the case of unequal parameters, we just return the W-graphs corresponding to all the left cells of W. See also 'klpolynomials', 'wgraphstarorbit', 'reklpols', 'wgraph' and 'precells'. >>> klcells(coxeter("B",3),[2,1,1],v) # unequal parameters #I Number of elements = 48 #I Initialising (Bruhat-Chevalley order etc.) ......... #I Computing KL polynomials for elements of length: #I 1 2 3 4 5 6 7 8 9 #I 120 arrows >> 16 left cells >> checks are True [wgraph(coxeter('B',3),[2,1,1],[[]]), wgraph(coxeter('B',3),[2,1,1],[[0],[1,0],[2,1,0]]), wgraph(coxeter('B',3),[2,1,1],[[1],[2,1]]), wgraph(coxeter('B',3),[2,1,1],[[2],[1,2]]), wgraph(coxeter('B',3),[2,1,1],[[0,1],[1,0,1],[2,1,0,1]]), wgraph(coxeter('B',3),[2,1,1],[[0,2],[1,0,2],[0,1,0,2], [1,2,1,0],[0,1,2,1,0],[1,0,1,2,1,0]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0],[0,2,1,0],[1,0,2,1,0], [0,1,0,2,1,0]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,2],[1,0,1,2],[2,1,0,1,2]]), wgraph(coxeter('B',3),[2,1,1],[[0,2,1],[1,0,2,1],[0,1,0,2,1], [1,2,1,0,1],[0,1,2,1,0,1],[1,0,1,2,1,0,1]]), wgraph(coxeter('B',3),[2,1,1],[[1,2,1],[0,1,2,1],[1,0,1,2,1], [1,2,1,0,1,2]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0,1],[0,2,1,0,1], [1,0,2,1,0,1]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0,1,2],[0,2,1,0,1,2], [1,0,2,1,0,1,2]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0,1,2,1],[0,1,2,1,0,1,2], [1,0,1,2,1,0,1,2]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0,1,2,1,0],[0,1,0,2,1,0,1,2]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0,2,1,0,1],[0,1,0,1,2,1,0,1]]), wgraph(coxeter('B',3),[2,1,1],[[0,1,0,1,2,1,0,1,2]])] Here are some examples for equal parameters, where the more efficient algorithm based on 'relklpols' is used. >>> klcells(coxeter("I5",2),1,v) #I 4 left cells (4 non-equivalent), mues: 1 [[[[]], [[0,1,0,1,0]], [[1], [0,1], [1,0,1], [0,1,0,1]], [[0], [1,0], [0,1,0], [1,0,1,0]]], [wgraph(coxeter('I5',2), [1,1], [[]]), wgraph(coxeter('I5',2), [1,1], [[0,1,0,1,0]]), wgraph(coxeter('I5',2), [1,1], [[1], [0,1], [1,0,1], [0,1,0,1]]), wgraph(coxeter('I5',2), [1,1], [[0], [1,0], [0,1,0], [1,0,1,0]])]] >>> k=klcells(coxeter("H",4),1,v) # takes < 7 minutes #I 206 left cells (90 non-equivalent), mues: 1,2,3 >>> set([len(c) for c in k[0]]) set([32,1,36,326,8,392,18,436,25]) (Thus, W has left cells of size 1,8,18,25,33,36,326,392,436.) The left cells in type H4 were first determined by D. Alvis: The left cells of the Coxeter group of type H_4, J. Algebra 107 (1987), 160-168; see also http://mypage.iusb.edu/~alvis/h4data (I have checked that the result of 'klcells' indeed coincides with Alvis' tables. Note that Alvis can actually compute all the Kazhdan-Lusztig polynomials in type H4, which would take a very long time with 'klpolynomials'. If one is interested in reproducing this information, then it is better to use DuCloux's Coxeter programe. Alternatively, one can also build all Kazhdan-Lusztig polynomials from the relative polynomials returned by 'allrelklpols'; this takes about 1 day cpu time.) >>> k=klcells(coxeter("E",6),1,v) # takes about 45 seconds #I 652 left cells (21 non-equivalent), mues: 1 >>> set([len(c) for c in k[0]]) set([64,1,230,6,45,81,20,150,24,60,280]) (I have checked that the result of 'klcells' coincides with the result for E6 produced by DuCloux's Coxeter programme.) >>> k=klcells(coxeter("D",7),1,v) # takes < 4 minutes #I 2416 left cells (49 non-equivalent), mues: 1 >>> set([len(c) for c in k[0]]) set([1,98,35,6,7,105,231,140,15,112,49,210,147,20,21,56,84, 154,175,63]) >>> k=klcells(coxeter("E",7),1,v) # takes about 4 hours #I 6364 left cells (56 non-equivalent), mues: 1,2,3,4,6,5 >>> set([len(c) for c in k[0]]) set([1024,1,27,7,168,105,756,135,77,910,621,504,210,594,21, 225,665,378,91,189,875]) >>> k=klcells(coxeter("D",8),1,v) # takes about 4 hours #I 11504 left cells (90 non-equivalent), mues: 1,2 set([1,7,8,140,21,1302,280,28,35,168,176,392,434,56,315,700, 448,68,714,76,336,184,980,728,350,230,616,490,364,238, 112,504,250,252]) >>> k=klcells(coxeter("A",9),1,v) # takes about 50 minutes #I 9496 left cells (42 non-equivalent), mues: 1 >>> set([len(c) for c in k[0]]) set([768,1,9,525,160,35,36,42,300,567,315,448,288,450,75,210, 84,90,350,225,252,126]) The program essentially works in all cases where one can afford to keep a complete list of all elements of W in the main memory. Thus, type B8 with its 10,321,920 elements is about the limit: it takes some 58 hours and 9GB memory to compute the 15304 left cells and the corresponding W-graphs. """ if type(weightL)==type([]): poids=weightL else: poids=len(W.rank)*[weightL] if any(i<0 for i in poids): print('#W All parameters must be non-negative') return False if all(i==1 for i in poids): #if pr==True and len(W.rank)>0: # lprint('#I') if len(W.rank)==0: nc=[[[]]] cr1=[wgraph(W,poids,[[]],v,[[]],{},[],[()])] creps=[()] else: allmues=[] J=list(W.rank) if W.cartantype[0][0]=='E': J.remove(0) else: J.remove(len(W.rank)-1) W1=reflectionsubgroup(W,J) X1p=[W.coxelmtoword(x1) for x1 in redleftcosetreps(W,J)] kk=klcells(W1,[poids[s] for s in J],v,pr=False,allcells=False) if len(W.rank)>0: lprint('#I ') lprint('('+str(len(kk[0]))+':'+str(len(kk[1]))+') ') nc,cr1,creps=[],[],[] celms=set([]) i,tot=0,0 while tot300: if len(rk['perm'])>1500: rht=[generalisedtau(W,p,maxd=3*len(W.rank)) for p in rk['perm']] else: rht=[tuple(W.rightdescentsetperm(p)) for p in rk['perm']] srht=list(set(rht)) ind1=wgraph(W,poids,rk,v) lprint(str(len(srht))+'!') ind=[] for rh in srht: l=list(filter(lambda x:rht[x]==rh,range(len(rht)))) x1=[ind1.X[ih] for ih in l] x1r=[ind1.Xrep[ih] for ih in l] i1=[ind1.Isets[ih] for ih in l] m1={} for kh in ind1.mmat.keys(): if kh[0] in l and kh[1] in l: m1[(l.index(kh[0]),l.index(kh[1]))]=ind1.mmat[kh] ind.extend(wgraph(ind1.W,ind1.weights,x1,ind1.var,i1, m1,ind1.mpols,x1r).decompose()) else: ind=wgraph(W,poids,rk,v).decompose() lprint(str(len(ind))) for ii in ind: if tot0: lprint('#I '+str(len(nc))+' left cells (') lprint(str(len(creps))+' non-equivalent), ') lprint('mues: '+','.join([str(i) for i in allmues])+'\n') #nc.sort(key=(lambda c:len(c))) ct=chartable(W) if allcells==True and len(nc)!=sum([ct['irreducibles'][i][0] for i in range(len(ct['a'])) if ct['a'][i]==ct['b'][i]]): print("Mist!") return False cr1.sort(key=(lambda c:len(c.X))) return [nc,cr1] else: return klcellsun(W,weightL,v,pr=True) #k=klpolynomials(W,weightL,v) #return [wgraph(W,poids,{'elms':[k['elms'][x] for x in c], # 'mpols':k['mpols'],'klpols':k['klpols'], # 'klmat':[[k['klmat'][c[w]][c[y]] for y in range(w+1)] # for w in range(len(c))]},v) for c in k['lcells']] def zeroterm(p): if type(p)==type(0): return p else: if p.val>0 or p.coeffs==[]: return 0 else: return p.coeffs[0] #def zeroterm(p): # if type(p)==type(0): # return p # else: # return p.value(0) #F leadingcoefficients def leadingcoefficients(W,weightL,lw,clpols=[]): """returns the leading coefficients (as defined by Lusztig) of the character values of the generic Iwahori-Hecke algebra associated with W and given list of weights. For an irreducible representation E and w in W, the coefficient c_{w,E} is defined by Tr(T_w,E) = c_{w,E} v^(-a_E) + higher powers of v, where a_E is the a-invariant of E (see 'ainvariants'). The argument lw contains the list of elements, given as reduced expressions, for which the leading values are to be computed. The weights are specified as described in 'ainvariants'. The computations use the character table of the Iwahori--Hecke algebra (see 'heckechartable') and the class polynomials (see 'heckecharvalues'). (Note that Lusztig actually further multiplies c_{w,E} by (-1)^l(w), but we omit this sign here.) The argument weightL specifies a weight function as explained in 'ainvariants'; in particular, the programme also works for unequal parameters. >>> W=coxeter("B",2) >>> a=allwords(W); a; [[], [0], [1], [0, 1], [1, 0], [0, 1, 0], [1, 0, 1], [0, 1, 0, 1]] >>> leadingcoefficients(W,[1,1],a) [[ 0, 0, 0, 1, 0], [-1,-1, 0, 0, 0], [ 0,-1, 0, 0,-1], [ 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0], [ 1,-1, 0, 0, 0], [ 0,-1, 0, 0,-1], [ 0, 0, 1, 0, 0]] See also 'leftcellleadingcoeffs'. """ v=lpol([1],1,'v') if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL[:] ti=heckechartable(W,[v**i for i in poids])['irreducibles'] ainv=ainvariants(W,poids) maxl=max([len(w) for w in lw]) if clpols==[]: cpmat=allclasspolynomials(W,[v**(2*p) for p in poids],maxl) else: cpmat=clpols lc=[] for w in lw: cind=0 for i in w: cind+=poids[i] cp=cpmat[W.wordtocoxelm(w)] #lc.append([zeroterm((-1)**len(w)*v**(ainv[i]-cind)*sum(cp[j]*ti[i][j] # for j in range(len(ainv)))) for i in range(len(ainv))]) lc.append([zeroterm(v**(ainv[i]-cind)*sum(cp[j]*ti[i][j] for j in range(len(ainv)))) for i in range(len(ainv))]) return lc #F leftcellleadingcoeffs def leftcellleadingcoeffs(W,weightL,v,cell,clpols=[]): """returns a dictionary with information concerning the leading coefficients in a given left cell. The components are: elms : the list of all w in the cell such that w^(-1) also lies in the cell; ti : the associated character table; distinv : the distinguished involution in the cell; nd : the corresponding sign; special : the character for which all values are positive; char : decomposition into irreducible characters of W. More precisely, let C be a left cell. Then we consider the subalgebra of the asymptotic algebra J which is spanned by all basis elements t_w where both w and its inverse lie in C. The associated character table is the table of values ((-1)^l(d) n_d c_{w,E})_{E,w} where w runs over all w in C such that w^{-1} in C, and E runs over all E in Irr(W) such that E occurs in the left cell module given by C. Here, c_{w,E} are the leading coefficients of the character values of the corresponding generic Iwahori-Hecke algebra as defined by G. Lusztig, Leading coefficients of character values of Hecke algebras, Proc. Symp. Pure Math. 47, AMS, 1987, pp. 235-262. (This article also contains a detailed study of the character tables (c_{w,E}) in the equal parameter case.) The distinguished involution d and the corresponding sign 'nd' are uniquely determined by the condition that nd*t_d is the identity element of the above subalgebra. It is known that nd=1 in the equal parameter case; in the general unequal parameter case, these properties are conjectural. (In fact, the function checks if such a distinguished involution exists.) The argument weightL specifies a weight function as explained in 'ainvariants'; in particular, the programme also works for unequal parameters. >>> v=lpol([1],1,'v') >>> W=coxeter("B",2) >>> [leftcellleadingcoeffs(W,1,v,l) for l in klcells(W,1,v)[0]] # equal parameters #I 4 left cells (4 non-equivalent), mues: 1 [{'elms': [[]], 'nd': 1, 'special': ('[[2], []]',), 'distinv': [], 'ti': [[('[[2], []]',), [1]]]}, {'elms': [[0, 1, 0, 1]], 'nd': 1, 'special': ('[[], [1, 1]]',), 'distinv': [0, 1, 0, 1], 'ti': [[('[[], [1, 1]]',), [1]]]}, {'elms': [[1], [1, 0, 1]], 'nd': 1, 'special': ('[[1], [1]]',), 'distinv': [1], 'ti': [[('[[1], [1]]',), [1, 1]], [('[[], [2]]',), [1, -1]]]}, {'elms': [[0], [0, 1, 0]], 'nd': 1, 'special': ('[[1], [1]]',), 'distinv': [0], 'ti': [[('[[1, 1], []]',), [1, -1]], [('[[1], [1]]',), [1, 1]]]}] >>> leftcellleadingcoeffs(W,[2,1],v,l.X) for l in klcells(W,[2,1],v)] # unequal parameters #I 10 arrows >> 6 left cells >> checks are True [{'elms': [[]], 'nd': 1, 'special': ('[[2], []]',), 'distinv': [], 'ti': [[('[[2], []]',), [1]]]}, {'elms': [[0]], 'nd': 1, 'special': ('[[1], [1]]',), 'distinv': [0], 'ti': [[('[[1], [1]]',), [1]]]}, {'elms': [[1]], 'nd': 1, 'special': ('[[], [2]]',), 'distinv': [1], 'ti': [[('[[], [2]]',), [1]]]}, {'elms': [[1, 0, 1]], 'nd': 1, 'special': ('[[1], [1]]',), 'distinv': [1, 0, 1], 'ti': [[('[[1], [1]]',), [1]]]}, {'elms': [[0, 1, 0]], 'nd': -1, 'special': ('[[1, 1], []]',), 'distinv': [0, 1, 0], 'ti': [[('[[1, 1], []]',), [1]]]}, {'elms': [[0, 1, 0, 1]], 'nd': 1, 'special': ('[[], [1, 1]]',), 'distinv': [0, 1, 0, 1], 'ti': [[('[[], [1, 1]]',), [1]]]}] (Note the negative value for nd.) Remark: The normalisiation by the sign (-1)^l(d)*n_d has the effect that the above table has a row in which all entries are strictly positive numbers. (There can be at most one row with this property.) It is known that, in the equal parameter case, this row is labelled by the unique special character (in the sense of Lusztig) which appears in the left cell representation carried by C. >>> W=coxeter("F",4); k=klcells(W,1,v) >>> l=leftcellleadingcoeffs(W,1,v,k[0][64]); l['ti'] [[('4_1',), [1,-1,-1, 1, 1, 0,-1,-1, 1]], [('9_2',), [1, 1,-1,-1,-1, 0,-1, 1, 1]], [('9_3',), [1,-1, 1,-1,-1, 0, 1,-1, 1]], [('6_2',), [1, 1, 1, 1, 1,-2, 1, 1, 1]], [('12',), [1, 1, 1, 1, 1, 4, 1, 1, 1]], [('16',), [2, 0, 0, 0, 0, 0, 0, 0,-2]]]} >>> l['special'] ('12',) >>> t=chartable(W); t['charnames'].index('12') 15 >>> t['a'][15]; t['b'][15] 4 4 (Thus, indeed, the character labelled by '12' is special in the sense originally defined by Lusztig.) See also 'chartable', 'leadingcoeffients', 'klcells', 'allcellsleadingcoeffs' and 'distinguishedinvolutions'. """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL[:] ch=chartable(W,chars=False)['charnames'] fshi=[s.coeffs[0] for s in schurelms(W,[v**p for p in poids])] pcell=[W.wordtoperm(w) for w in cell] lw=[cell[i] for i in range(len(cell)) if perminverse(pcell[i]) in pcell] if clpols==[]: cpmat=allclasspolynomials(W,[v**(2*p) for p in poids], max([len(w) for w in lw])) else: cpmat=clpols lc=transposemat(leadingcoefficients(W,weightL,lw,cpmat)) ii=list(filter(lambda i:any(x!=0 for x in lc[i]),range(len(fshi)))) ftot=1 for i in ii: ftot*=fshi[i] cof=[] for i in ii: cf=1 for j in ii: if i!=j: cf*=fshi[j] cof.append(cf) nd=[(-1)**len(lw[w])*sum(cof[i]*lc[ii[i]][w] for i in range(len(ii)))//ftot for w in range(len(lw))] #nd1=[(-1)**len(lw[w])*sum((W.order*lc[i][w])//fshi[i] # for i in range(len(fshi)))//W.order for w in range(len(lw))] #if nd1!=nd: # print('mist!') # return False if nd.count(0)!=len(nd)-1: print("no distinguished involution!") return nd i=0 while nd[i]==0: i+=1 di=i if nd[di]**2!=1: print("no distinguished involution!!") return [di,nd,nd[di]**2] if nd[di]==-1: lc=[[-i for i in l] for l in lc] if len(lw[di])%2==1: lc=[[-i for i in l] for l in lc] #if set([(len(x)-len(lw[di]))%2 for x in lw])!=set([0]): # lprint('#W odd lengths ') sp=0 while sp0 for x in lc[ii[sp]]): sp+=1 if sp==len(ii): print('no special character!') chi=[] for i in range(len(fshi)): if i in ii: chi.append(lc[i][di]) else: chi.append(0) return {'ti':[[ch[i],lc[i]] for i in ii],'distinv':lw[di], 'nd':nd[di],'elms':lw,'special':ch[ii[sp]],'char':chi} #F poltostr def poltostr(f): if type(f)==type(0): return ('0.'+str(f)) elif f.coeffs==[]: return ('0.0') else: return str(f.val)+'.'+'.'.join([str(i) for i in f.coeffs]) #F strtopol def strtopol(sp,vnam): spl=sp.split('.') return lpol([int(i) for i in spl[1:]],val=int(spl[0]),vname=vnam) #F distinguishedinvolutions def distinguishedinvolutions(W,weightL,distonly=True): """returns the list of distinguished involutions with respect to a weight function, plus some additional information (as explained below). Here, we use the following definition: An element w in W is called distinguished if n_w := (-1)^l(w) * sum_{E in Irr(W)} f_E^(-1) c_{w,E} is non-zero, where c_{w,E} are the leading coefficients of the character values of the generic Iwahori-Hecke algebra and f_E are defined in terms of the corresponding Schur elements. This definition appeared in M. Geck, Leading coefficients and cellular bases of Hecke algebras, Proc. Edinburgh Math. Soc. 52 (2009), 653--677. (This is equivalent to Lusztig's original definition in the case of equal parameters and also in types I_2(m), F_4 for any choice of parameters; it is conjectured that this equivalence holds in general.) One expects that all distinguished elements are involutions and that every left cell with respect to the given weight function contains exactly one distinguished element. (This is known to be true in the equal parameter case where it is also known that n_d=1 for all distinguished d in W.) The function returns a list whose first component is the list of distinguished involutions and the second component is a list of pairs (E_i, c_{d,E_i}) where E_i in Irr(W) and c_{d,E_i}<>0. If not all n_d are equal to 1, then there is a third component containing the values n_d. >>> W=coxeter("I8",2) >>> distinguishedinvolutions(W,[1,2]) # unequal parameters #I Number of distinguished involutions = 6 (4) [[[], [1], [0], [0,1,0], [1,0,1,0,1,0,1], [0,1,0,1,0,1,0,1]], [[[('phi_{1,0}',),1]], [[('phi_{2,3}',),-1],[('phi_{2,2}',),-1], [('phi_{2,1}',),-1]], [[("phi_{1,4}''",),-1]], [[('phi_{2,3}',),-1],[('phi_{2,2}',),-1], [('phi_{2,1}',),-1]], [[("phi_{1,4}'",),1]], [[('phi_{1,8}',),1]]], [1, 1, 1, 1, -1, 1]] >>> W=coxeter("G",2) >>> distinguishedinvolutions(W,1) # equal parameters #I Number of distinguished involutions = 4 (4) [[[], [1], [0], [0,1,0,1,0,1]], [[[('phi_{1,0}',),1]], [[('phi_{2,2}',),-1], [('phi_{2,1}',),-1], [("phi_{1,3}''",),-1]], [[('phi_{2,2}',),-1], [('phi_{2,1}',),-1], [("phi_{1,3}'",),-1]], [[('phi_{1,6}',),1]]]] (Here, all signs n_d are known to be equal to 1.) This function even works for W of large rank: For type H4, it takes about 25s; for type E6, about 45s; for type E7, about 40min; for type E8, about 18 days (and 22GB main memory). See also 'distinguishedinvolutions_eq' (optimised for equal parameters), 'libdistinv', 'leadingcoefficients', 'schurelms' and 'leftcellleadingcoeffs'. Final remark: There is an optional argument 'distonly'. If this is set to 'False', then the function returns similar lists as described above, but now the first list contains all elements w such that c_{w,E} is non-zero for some E in Irr(W). Hence, this yields the complete table of all leading coefficients c_{w,E}. """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL[:] v=lpol([1],1,'v') poin=poincarepol(W,v).coeffs w0=longestperm(W) ct=chartable(W) lcl=[len(w) for w in conjugacyclasses(W)['reps']] vs=[v**p for p in poids] ti=heckechartable(W,vs)['irreducibles'] schur=schurelms(W,vs) fshi=[s.coeffs[0] for s in schur] ainv=[-s.val//2 for s in schur] tup=[[i,ainv[i]] for i in range(len(ainv))] tup.sort(key=(lambda i:i[1]),reverse=True) ti=[ti[tup[i][0]] for i in range(len(ainv))] fshi=[fshi[tup[i][0]] for i in range(len(ainv))] ainv=[ainv[tup[i][0]] for i in range(len(ainv))] chn=[ct['charnames'][tup[i][0]] for i in range(len(ainv))] ti1=[ct['irreducibles'][tup[i][0]] for i in range(len(ainv))] signchar=[(-1)**lw for lw in lcl] signp=[ti1.index([l[j]*signchar[j] for j in range(len(lcl))]) for l in ti1] maxl=W.N if W.N in lcl: iw0=lcl.index(W.N) eps=[x[iw0]//x[0] for x in ct['irreducibles']] eps=[eps[tup[i][0]] for i in range(len(ainv))] maxl=W.N//2 distinv,nvec=[[]],[1] lc=[zeroterm(v**(ainv[i])*ti[i][0]) for i in range(len(ainv))] char=[] for i in range(len(ainv)): if lc[i]!=0: char.append([chn[i],lc[i]]) #if lc[i]>0: # char.append([chn[i],lc[i]]) #elif lc[i]<0: # char.append([chn[i],-lc[i]]) charvec=[char[::-1]] if maxl==W.N or all(p==0 for p in poids): distinv0,nvec0,charvec0=[],[],[] else: distinv0,nvec0=[W.permtoword(w0)],[1] Lw0=ainv[ti1.index(signchar)] lc0=[zeroterm(v**(ainv[i]-Lw0)*ti[i][iw0]) for i in range(len(ainv))] char0=[] for i in range(len(ainv)): if lc0[i]!=0: char0.append([chn[i],lc0[i]]) #if lc0[i]>0: # char0.append([chn[i],lc0[i]]) #elif lc0[i]<0: # char0.append([chn[i],-lc0[i]]) charvec0=[char0[::-1]] cpmat=[] cp=len(ainv)*[0] cp[0]=1 cpmat.append({mybytes(W.rank):';'.join([poltostr(f) for f in cp])}) cps={} nega=False zael=0 lprint('#I length 0 - 1 element, 1 class polynomial; 1 distinv\n') for l in range(maxl): if l<9: lprint('#I length '+str(l+1)) else: lprint('#I length '+str(l+1)) nl=set([]) if l==0: ol=[] else: ol=set(cpmat[l-1].keys()) for w in cpmat[l].keys(): for s in W.permgens: nw=mybytes([s[i] for i in w]) if not nw in ol: nl.add(nw) if len(nl)==poin[l+1]: break cps={} lprint(' - ') ll=len(nl) spols=[] while nl!=set([]): w=W.coxelmtoword(next(iter(nl))) pw=W.wordtoperm(w) pw1=perminverse(pw) t=testcyclicshift1(W,w,pw) if t[0]==1: cp=len(ainv)*[0] cp[t[2]]=1 else: cp1=[strtopol(f,'v') for f in cpmat[l-1][mybytes(t[2])].split(';')] cp2=[strtopol(f,'v') for f in cpmat[l][mybytes([W.permgens[t[3]][i] for i in t[2]])].split(';')] if poids[t[3]]==1: cp=[v*cp1[i]+(v-1)*cp2[i] for i in range(len(ainv))] else: cp=[v**(poids[t[3]])*cp1[i]+(v**(poids[t[3]])-1)*cp2[i] for i in range(len(ainv))] strcp=';'.join([poltostr(f) for f in cp]) ic=len(spols)-1 while ic>=0: if strcp==spols[ic]: break ic-=1 if ic==-1: spols.append(strcp) for x in t[1]: cx=x[:len(W.rank)] cps[mybytes(cx)]=spols[ic] nl.remove(mybytes(cx)) if not pw1 in t[1]: for x in t[1]: cx=perminverse(x)[:len(W.rank)] cps[mybytes(cx)]=spols[ic] nl.remove(mybytes(cx)) if len(t[1])==1 and (any(p!=1 for p in poids) or W.permorder(pw)==2 or generalisedtau(W,pw,maxd=len(W.rank)**2)== generalisedtau(W,pw1,maxd=len(W.rank)**2)): zael+=1 cpv=[] for j in range(len(ainv)): if type(cp[j])==type(0): cpv.append(cp[j]) elif cp[j].coeffs==[]: cpv.append(0) else: cpv.append(cp[j].value(v**2)) cind=0 for i in w: cind+=poids[i] lc=[] nonz=-1 for i in range(len(ainv)): if ainv[i]>cind or (nonz!=-1 and nonz!=ainv[i]): lcc=0 else: lcc=zeroterm(v**(ainv[i]-cind)*sum(cpv[j]*ti[i][j] for j in range(len(ainv)) if cpv[j]!=0)) if nonz==-1 and lcc!=0: nonz=ainv[i] lc.append(lcc) ii=[i for i in range(len(ainv)) if lc[i]!=0] if ii!=[]: ftot=1 for i in ii: ftot*=fshi[i] cof=[] for i in ii: cf=1 for j in ii: if i!=j: cf*=fshi[j] cof.append(cf) nd=(-1)**len(w)*sum(cof[i]*lc[ii[i]] for i in range(len(ii))) if nd%ftot==0: nd=nd//ftot else: print("Mist!") return False if nd!=0 or distonly!=True: distinv.append(w) nvec.append(nd) char=[] for i in range(len(ainv)): if lc[i]!=0: char.append([chn[i],lc[i]]) #if lc[i]>0: # char.append([chn[i],lc[i]]) #elif lc[i]<0: # char.append([chn[i],-lc[i]]) charvec.append(char[::-1]) if nega==False and nd!=1: nega=True if maxl==W.N//2 and (W.N%2==1 or l0: # char0.append([signp[i],lc[i]]) #elif lc[i]<0: # char0.append([signp[i],-lc[i]]) char0.sort(key=(lambda i:i[0]),reverse=True) charvec0.append([[chn[i[0]],i[1]] for i in char0]) if nega==False and nd!=1: nega=True lprint(str(ll)+' elements, '+str(len(spols))+' class polynomials; ') lprint(str(len(distinv)+len(distinv0))+' distinvs\n') if l>0: cpmat[l-1]=0 cpmat.append(cps) distinv=distinv+distinv0[::-1] nvec=nvec+nvec0[::-1] charvec=charvec+charvec0[::-1] #d1=distinguishedinvolutions(W,poids) #d1=libdistinv(W,poids) #if not (len(distinv)==len(d1) and all(i in d1 for i in distinv)): # print("Mist!") # return [distinv,d1] #else: # lprint('True') lprint('#I Number of distinguished involutions = '+str(len(distinv))) lprint(' ('+str(zael)+')\n') if nega==True: return [distinv,charvec,nvec] else: return [distinv,charvec] #F starorbitinv (for use in distinguishedinvolutions_eq and libdistinv) def starorbitinv(W,pw): """returns double star orbit of an involution. """ orb=[pw[:]] for d in orb: for s in W.rank: for t in range(s): if W.coxetermat[s][t]==3: if (d[s]>=W.N and d[t]=W.N): d1=leftklstar(W,perminverse(leftklstar(W,d,s,t)),s,t) if not d1 in orb: orb.append(d1) if W.permorder(d1)!=2: print("Mist!") return False return orb def starorbitinv1(W,distinv): """decomposes list of distinguished involutions into orbits under the star operation. """ rest=[W.wordtoperm(w) for w in distinv] reps=[] while rest!=[]: orb=starorbitinv(W,rest[0]) for x in orb: rest.remove(x) l=W.permlength(orb[0]) r=orb[0] for x in orb[1:]: lx=W.permlength(x) if lx=0: if strcp==spols[ic]: break ic-=1 if ic==-1: spols.append(strcp) for x in t[1]: cx=x[:len(W.rank)] cps[mybytes(cx)]=spols[ic] nl.remove(mybytes(cx)) if not pw1 in t[1]: for x in t[1]: cx=perminverse(x)[:len(W.rank)] cps[mybytes(cx)]=spols[ic] nl.remove(mybytes(cx)) if W.permorder(pw)==2 and pw in repsinv and not pw in distinv: cpv=[] for j in range(len(ainv)): if type(cp[j])==type(0): cpv.append(cp[j]) elif cp[j].coeffs==[]: cpv.append(0) else: cpv.append(cp[j].value(v**2)) lc=[] nonz=-1 for i in range(len(ainv)): if ainv[i]>len(w) or (nonz!=-1 and nonz!=ainv[i]): lcc=0 else: lcc=zeroterm(v**(ainv[i]-len(w))*sum(cpv[j]*ti[i][j] for j in range(len(ainv)) if cpv[j]!=0)) if nonz==-1 and lcc!=0: nonz=ainv[i] lc.append(lcc) ii=[i for i in range(len(ainv)) if lc[i]!=0] ftot=1 for i in ii: ftot*=fshi[i] cof=[] for i in ii: cf=1 for j in ii: if i!=j: cf*=fshi[j] cof.append(cf) nd=(-1)**len(w)*sum(cof[i]*lc[ii[i]] for i in range(len(ii))) if nd%ftot==0: nd=nd//ftot else: print("Mist!") return False #nd=(-1)**len(w)*sum((W.order*lc[i])//fshi[i] # for i in range(len(fshi)))//W.order if nd!=0: sto=starorbitinv(W,pw) distinv.extend(sto) char=[] for i in range(len(ainv)): if lc[i]!=0: char.append([chn[i],lc[i]]) #if lc[i]>0: # char.append([chn[i],lc[i]]) #elif lc[i]<0: # char.append([chn[i],-lc[i]]) char=char[::-1] for i in sto: charvec.append(char) pw0=permmult(pw,w0) if all(f%2==0 for f in W.degrees) and not pw0 in distinv: nd=sum(eps[ii[i]]*cof[i]*lc[ii[i]] for i in range(len(ii))) if nd%ftot==0: nd=nd//ftot else: print("Mist!") return False if nd!=0: sto0=starorbitinv(W,pw0) distinv.extend(sto0) char0=[] for i in range(len(ainv)): if lc[i]!=0: char0.append([signp[i],(-1)**(W.N+len(w))*eps[i]*lc[i]]) #if lc[i]>0: # char0.append([signp[i],lc[i]]) #elif lc[i]<0: # char0.append([signp[i],-lc[i]]) char0.sort(key=(lambda i:i[0]),reverse=True) char0=[[chn[i[0]],i[1]] for i in char0] for i in sto0: charvec.append(char0) lprint(str(ll)+' elements, '+str(len(spols))+' class polynomials; ') lprint(str(len(distinv))+' distinvs\n') if l>0: cpmat[l-1]=0 cpmat.append(cps) if len(distinv)==maxn: break res=[[W.permtoword(distinv[i]),charvec[i]] for i in range(len(distinv))] res.sort(key=(lambda x:len(x[0]))) #d1=distinguishedinvolutions(W,poids) #d1=libdistinv(W,1) #if not (len(distinv)==len(d1) and all(i in d1 for i in distinv)): # print("Grosser Mist!") # return [distinv,d1] #else: # lprint('True') lprint('#I Number of distinguished involutions = '+str(len(res))+'\n') return [list(l) for l in zip(*res)] #F allcellsleadingcoeffs def allcellsleadingcoeffs(W,weightL,v): """returns a list which contains the pairwise different results of 'leftcellleadingcoeffs' as we run over all left cells of W with at least two irreducible components. >>> allcellsleadingcoeffs(coxeter("H",3),1,v) [[[[("3_s'",), 1, ir5], [("overline{3}_s'",), 1, 1-ir5]], [[('3_s',), ir5, 1], [('overline{3}_s',), 1-ir5, 1]], [[("4_r'",), 1, -1], [('4_r',), 1, 1]]], [[[], 1], # all distinguished involutions with signs [[0, 1, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 0, 1, 2], 1], [[2, 1, 0, 1, 0, 2, 1, 0, 1, 2], 1], [[0, 1, 0, 1, 0], 1], [[1, 0, 2, 1], 1], [[0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 0], 1], [[1, 0, 2, 1, 0, 1, 0, 2, 1], 1], [[0, 1, 0, 2, 1, 0], 1], [[2], 1], [[0, 1, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 0, 1], 1], [[1, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 0, 1, 2], 1], [[0], 1], [[1, 2, 1], 1], [[0, 1, 0, 1, 2, 1, 0, 1, 0], 1], [[1, 0, 1, 2, 1, 0, 1], 1]], [("5_r'",), ("3_s'",), ("1_r'",), ('3_s',), # special characters ('1_r',), ('5_r',), ("4_r'",)]] >>>allcellsleadingcoeffs(coxeter("B",3),[2,1,1],v) [[[[('[[1, 1], [1]]',), 1, 1], [('[[1], [2]]',), 1, -1]], [[('[[1], [2]]',), 1, -1], [('[[], [3]]',), 1, 1]], [[('[[1, 1, 1], []]',), 1, -1], [('[[1, 1], [1]]',), 1, 1]]], [[[], 1], [[0], 1], [[1], 1], [[2], 1], [[1, 0, 1], 1], [[0, 2], 1], [[0, 1, 0], -1], [[2, 1, 0, 1, 2], 1], [[1, 0, 2, 1], 1], [[1, 2, 1], 1], [[0, 1, 0, 1], 1], [[0, 2, 1, 0, 1, 2], 1], [[1, 0, 1, 2, 1, 0, 1, 2], 1], [[0, 1, 0, 2, 1, 0, 1, 2], -1], [[0, 1, 0, 1, 2, 1, 0, 1], -1], [[0, 1, 0, 1, 2, 1, 0, 1, 2], 1]] [('[[1], [2]]',), ('[[1], [1, 1]]',), ('[[1, 1, 1], []]',), ('[[2], [1]]',), ('[[], [2, 1]]',), ('[[], [1, 1, 1]]',), ('[[3], []]',), ('[[2, 1], []]',)]] See also 'klcells' and 'leftcellleadingcoeffs'. """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL[:] if all(p==1 for p in poids): kl=[c.X for c in klcells(W,poids,v,allcells=False)[1]] else: kl=[c.X for c in klcellsun(W,poids,v)] cp=allclasspolynomials(W,[v**(2*i) for i in poids]); ch=chartable(W,chars=False)['charnames'] nlc,dlc=[],[] slc=set([]) lprint('#I ') for l1 in kl: l=leftcellleadingcoeffs(W,poids,v,l1,clpols=cp) lprint(str(l['nd'])) dlc.append([l['distinv'],l['nd']]) slc.add(ch.index(l['special'])) if len(l['ti'])>1: tl=transposemat([flatlist(i) for i in l['ti']]) ok=True for m in nlc: if len(m)==len(tl) and all(m.count(r)==tl.count(r) for r in m): ok=False if ok: nlc.append(tl) lprint('\n') slc=list(slc) slc.sort() slc=[ch[i] for i in slc] for l in nlc: #if len(list(filter(lambda i:i in slc,l[0])))!=1: if len([i for i in l[0] if i in slc])!=1: print('multiple special in cell') return l return [[transposemat(l) for l in nlc],dlc,slc] #F libdistinv def libdistinv(W,weightL,unpack=True): """returns a pre-computed and explicitly stored list of distinguished involutions. Among other properties, these elements form a set of representatives for the left cells of W with respect to the given weight function. This function only works for W of type I_2(m), F_4 (any weight function) and H_3, H_4, E_6, E_7, E8 (equal parameters) and only if all the weights are strictly positive. One can reproduce these data using 'distinguishedinvolutions' but this will take some time. Also, some further arguments are needed to deal with all choices of unequal parameters for I_2(m) and F_4. Here, we rely on the results and techniques in M. Geck, Computing Kazhdan-Lusztig cells for unequal parameters, J. Algebra 281 (2004), 342--365. For the large groups of exceptional type, we only store a set of representatives under N. Xi's 'double' star operation; this shows that, if d is distinguished and the star operation is defined for d (with respect to generators s,t such that st has order 3), then the element ((d^*)^{-1})^* is also distinguished. -- So it will take a couple of minutes or so to unpack these data. For example, for W of type E8 there are 101796 distinguished involutions but only 106 orbits under the 'double' star operation. The 'unpacking' is done by the function 'starorbitinv'. >>> W=coxeter('H',4); >>> libdistinv(W,1)==distinguishedinvolutions(W,1) True >>> W=coxeter("E",8); >>> d=timer(libdistinv,W,1) 188.06 >>> len(d) 101796 See also 'distinguishedinvolutions'. """ if type(weightL)==type(0): poids=len(W.rank)*[weightL] else: poids=weightL[:] if len(W.cartantype)>1: print('#W Sorry, only irreducible W of exceptional type') return False if all(p==0 for p in poids): return [[]] typ=W.cartantype[0][0] rk=list(W.cartantype[0][1]) if typ=='H' and rk==[0,1,2]: l=['', '0', '1', '2', '02', '121', '1021', '01010', '01210', '010210', '1012101', '0210102', '10102101', '010121010', '102101021', '2101021012', '01021010210', '1010210102101', '01010210102101', '01012101021012', '10102101021012', '010102101021012'] chrs0=['1.1', '7.1c6.1', '7.1c6.1', '7.1c6.1', '3.1', '9.1c8.1', '3.1', '2.1', '9.1c8.1', '3.1', '9.1c8.1', '2.1', '3.1', '9.1c8.1', '2.1', '3.1', '2.1', '2.1', '5.1c4.1', '5.1c4.1', '5.1c4.1', '0.1'] ch=chartable(W,chars=False)['charnames'] chars=[[[ch[int(k.split('.')[0])],int(k.split('.')[1])] for k in i.split('c')] for i in chrs0] if unpack==True: return [[int(s) for s in i] for i in l] else: return [[[int(s) for s in l[i]],chars[i]] for i in range(len(l))] elif typ=='H' and rk==[0,1,2,3]: l=['', '0', '1', '2', '3', '02', '03', '13', '121', '232', '1021', '0232', '2132', '01010', '01210', '12321', '010210', '010103', '102321', '121321', '1012101', '0210102', '0123210', '10102101', '02101032', '12101321', '01023210', '01213210', '010121010', '102101021', '101232101', '032101023', '2101021012', '1021010321', '0121013210', '1010232101', '1012132101', '0232101023', '01021010210', '01012321010', '21012321012', '10321010213', '010210103210', '101210132101', '010121321010', '121012321012', '210102321012', '102321010213', '321010210123', '1010210102101', '0210123210102', '0103210102103', '2103210102132', '01010210102101', '01012101021012', '10102101021012', '10102101032101', '01012101321010', '12101021321012', '01210123210102', '01023210102103', '23210102101232', '010102101021012', '102101232101021', '101032101021013', '021032101021032', '0121010213210102', '1012101232101021', '1010232101021013', '0101321010210123', '1010321010210123', '1232101021012321', '01021012321010210', '10210321010210321', '01010321010210123', '21010321010210132', '101210102132101021', '010121012321010210', '210102321010210132', '210103210102101232', '012321010210123210', '1010210123210102101', '1210103210102101321', '0102103210102103210', '0210103210102101232', '01012101021321010210', '10123210102101232101', '210102101232101021012', '012101032101021013210', '102101032101021012321', '101021032101021032101', '2101232101021012321012', '0101232101021012321010', '21010210321010210321012', '01021010321010210123210', '10121010321010210132101', '32101021012321010210123', '021012321010210123210102', '1010210103210102101232101', '1210102103210102101321012', '0101210103210102101321010', '2321010210123210102101232', '01010210102101232101021012', '10210123210102101232101021', '121010210132101021012321012', '012101021032101021013210102', '010102101032101021012321010', '123210102101232101021012321', '0102101232101021012321010210', '0101032101021012321010210123', '01210102101321010210123210102', '10121010210321010210132101021', '01232101021012321010210123210', '12132101021013210102103210123', '101021012321010210123210102101', '021010321010210123210102101232', '0101210102103210102101321010210', '1012101021013210102101232101021', '1012321010210123210102101232101', '0121321010210123210102103210123', '21010210123210102101232101021012', '10210103210102101232101021012321', '010121010210132101021012321010210', '210123210102101232101021012321012', '010123210102101232101021012321010', '101213210102101232101021013210123', '0101021010210123210102101321010210', '0102101032101021012321010210123210', '3210102101232101021012321010210123', '01010210102101321010210123210102101', '12101232101021012321010210123210123', '02101232101021012321010210123210102', '01012132101021012321010210132101023', '101021010321010210123210102101232101', '010102321010210123210102101321010213', '0101021321010210123210102101321010213', '0121012321010210123210102101232101023', '1021012321010210123210102101232101021', '12101021013210102101232101021012321012', '01010210103210102101232101021012321010', '02101023210102101232101021013210102132', '021010213210102101232101021013210102132', '101210123210102101232101021012321010213', '010210123210102101232101021012321010210', '0121010210132101021012321010210123210102', '1021010232101021012321010210123210102132', '1210321010210123210102101232101021012321', '10210102132101021012321010210123210102132', '10102101232101021012321010210123210102101', '01012101232101021012321010210123210102103', '101210102101321010210123210102101232101021', '010210102321010210123210102101232101021032', '012103210102101232101021012321010210123210', '0102101021321010210123210102101232101021032', '0101021012321010210123210102101232101021013', '2101021012321010210123210102101232101021012', '01012101021013210102101232101021012321010210', '10102101023210102101232101021012321010210321', '10121032101021012321010210123210102101232101', '021010210123210102101232101021012321010210132', '101021010213210102101232101021012321010210321', '321010210123210102101232101021012321010210123', '0101021010210132101021012321010210123210102101', '0101021010232101021012321010210123210102103210', '1210102101232101021012321010210123210102101321', '0101210321010210123210102101232101021012321010', '1210102321010210123210102101232101021012321012', '01010210102132101021012321010210123210102103210', '10210102101232101021012321010210123210102101321', '02321010210123210102101232101021012321010210123', '010102101021012321010210123210102101232101021012', '012101021012321010210123210102101232101021013210', '010102101321010210123210102101232101021012321010', '012101023210102101232101021012321010210123210102', '121321010210123210102101232101021012321010210123', '0102101021012321010210123210102101232101021013210', '1021321010210123210102101232101021012321010210123', '10121010210123210102101232101021012321010210132101', '02101021013210102101232101021012321010210123210102', '10121010232101021012321010210123210102101232101021', '01010321010210123210102101232101021012321010210123', '01210321010210123210102101232101021012321010210123', '101021010210123210102101232101021012321010210132101', '010210321010210123210102101232101021012321010210123', '0101021010210132101021012321010210123210102101321010', '0101210102101232101021012321010210123210102101321010', '1021010210132101021012321010210123210102101232101021', '0101210102321010210123210102101232101021012321010210', '1012101321010210123210102101232101021012321010210123', '0210102321010210123210102101232101021012321010210123', '10102101321010210123210102101232101021012321010210123', '010210102101321010210123210102101232101021012321010210', '010121010321010210123210102101232101021012321010210123', '102101021321010210123210102101232101021012321010210123', '2101021012321010210123210102101232101021012321010210123', '01010210102103210102101232101021012321010210123210102101', '10102101021013210102101232101021012321010210123210102101', '01021010210321010210123210102101232101021012321010210123', '0101210102101232101021012321010210123210102101232101021012', '1010210102101232101021012321010210123210102101232101021012', '1010210102101321010210123210102101232101021012321010210123', '01010210102101232101021012321010210123210102101232101021012', '01010210102101321010210123210102101232101021012321010210123', '01012101021012321010210123210102101232101021012321010210123', '10102101021012321010210123210102101232101021012321010210123', '010102101021012321010210123210102101232101021012321010210123'] chrs0=['0.1', '4.1c2.1', '4.1c2.1', '4.1c2.1', '4.1c2.1', '12.1c10.1', '12.1c10.1', '12.1c10.1', '19.1c17.1', '19.1c17.1', '12.1c10.1', '26.1', '12.1c10.1', '30.1', '19.1c17.1', '19.1c17.1', '12.1c10.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '19.1c17.1', '30.1', '19.1c17.1', '12.1c10.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '26.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '19.1c17.1', '30.1', '19.1c17.1', '30.1', '12.1c10.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '26.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '30.1', '19.1c17.1', '19.1c17.1', '30.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '26.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '12.1c10.1', '30.1', '19.1c17.1', '30.1', '30.1', '33.2c32.2c29.2c28.2c25.1c24.1c23.1c22.1c16.1c15.1c7.1c6.1', '33.2c32.2c29.2c28.2c25.1c24.1c23.1c22.1c16.1c15.1c7.1c6.1', '33.2c32.2c29.2c28.2c25.1c24.1c23.1c22.1c16.1c15.1c7.1c6.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '26.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '26.1', '31.1', '19.1c17.1', '30.1', '30.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '33.2c32.2c29.2c28.2c25.1c24.1c23.1c22.1c16.1c15.1c7.1c6.1', '33.2c32.2c29.2c28.2c25.1c24.1c23.1c22.1c16.1c15.1c7.1c6.1', '26.1', '19.1c17.1', '30.1', '31.1', '30.1', '26.1', '33.2c32.1c29.1c28.1c25.1c24.1c23.1c22.1c21.1c9.1c8.1', '33.2c32.2c29.1c28.1c25.1c24.1c23.1c22.1c21.1c16.1c15.1c14.1', '33.2c32.2c29.2c28.2c25.1c24.1c23.1c22.1c16.1c15.1c7.1c6.1', '26.1', '19.1c17.1', '30.1', '30.1', '31.1', '26.1', '26.1', '19.1c17.1', '30.1', '31.1', '30.1', '26.1', '26.1', '30.1', '31.1', '30.1', '19.1c17.1', '26.1', '31.1', '30.1', '30.1', '30.1', '27.1', '26.1', '31.1', '30.1', '31.1', '30.1', '26.1', '27.1', '31.1', '30.1', '30.1', '31.1', '26.1', '27.1', '30.1', '31.1', '30.1', '31.1', '26.1', '27.1', '31.1', '30.1', '30.1', '31.1', '20.1c18.1', '27.1', '26.1', '31.1', '31.1', '30.1', '31.1', '27.1', '20.1c18.1', '31.1', '31.1', '30.1', '27.1', '27.1', '20.1c18.1', '31.1', '31.1', '30.1', '27.1', '20.1c18.1', '27.1', '31.1', '30.1', '31.1', '27.1', '20.1c18.1', '27.1', '31.1', '31.1', '30.1', '27.1', '20.1c18.1', '27.1', '31.1', '31.1', '30.1', '27.1', '20.1c18.1', '20.1c18.1', '27.1', '27.1', '31.1', '31.1', '31.1', '13.1c11.1', '20.1c18.1', '27.1', '27.1', '20.1c18.1', '31.1', '31.1', '20.1c18.1', '27.1', '27.1', '13.1c11.1', '20.1c18.1', '31.1', '31.1', '20.1c18.1', '20.1c18.1', '27.1', '27.1', '20.1c18.1', '13.1c11.1', '31.1', '27.1', '20.1c18.1', '13.1c11.1', '31.1', '13.1c11.1', '27.1', '13.1c11.1', '13.1c11.1', '13.1c11.1', '13.1c11.1', '5.1c3.1', '5.1c3.1', '5.1c3.1', '5.1c3.1', '1.1'] ch=chartable(W,chars=False)['charnames'] chars=[[[ch[int(k.split('.')[0])],int(k.split('.')[1])] for k in i.split('c')] for i in chrs0] if unpack==True: return [[int(s) for s in i] for i in l] else: return [[[int(s) for s in l[i]],chars[i]] for i in range(len(l))] elif typ=='F' and rk==[0,1,2,3]: l1111=['', '0', '1', '2', '3', '02', '03', '13', '010', '232', '1021', '1212', '0103', '0232', '2132', '02102', '12321', '012102', '210212', '021032', '102321', '121321', '132123', '1021021', '0123210', '2123212', '0321023', '01021021', '10210321', '01213210', '21023212', '21232123', '01321023', '32102123', '010210212', '121232123', '021232102', '103210213', '01212321023', '10212321021', '21032102132', '01032102123', '2102123210212', '0102123210213', '0210321021232', '1210321021321', '021021232102132', '012103210213210', '102103210212321', '321021232102123', '0102102123210212', '1210232102123212', '10210212321021321', '01021032102123210', '12102132102123212', '02321021232102123', '012102321021232102', '010321021232102123', '0102102123210213210', '0121021321021232102', '1021321021232102123', '1212321021232102123', '01021023210212321021', '02102321021232102123', '010210213210212321021', '010210321021232102123', '012102321021232102123', '210212321021232102123', '0121021232102123210212', '1021021232102123210212', '1021021321021232102123', '010210212321021232102123'] l1122=['', '0', '1', '2', '3', '02', '03', '13', '010', '121', '212', '232', '1021', '1212', '0103', '0232', '2132', '01210', '12321', '32123', '012102', '021032', '102321', '121321', '132123', '232123', '0123210', '2123212', '01021021', '01210212', '10210212', '10210321', '01023210', '01213210', '12123212', '21023212', '12132123', '21232123', '01321023', '010210212', '121232123', '021232102', '0102103210', '0121232102', '0210232102', '0121321023', '0212321023', '0103210213', '0132102123', '1032102123', '01212321023', '10212321021', '01032102123', '010212321021', '102102321021', '102123210213', '021032102132', '2102123210212', '0102123210213', '0210321021232', '21021232102132', '10210321021321', '21232102123212', '021021232102132', '102103210212321', '321021232102123', '1210212321021321', '0102103210213210', '0212321021232102', '2321021232102123', '10210212321021321', '01021032102123210', '12102132102123212', '02321021232102123', '012102123210213210', '102123210212321021', '121321021232102123', '212321021232102123', '0102102123210213210', '0121021321021232102', '1021321021232102123', '1212321021232102123', '21021232102123210212', '01210321021232102123', '010210213210212321021', '010210321021232102123', '012102321021232102123', '01021021232102123210212', '01021021321021232102123', '01210212321021232102123', '10210212321021232102123', '010210212321021232102123'] l1133=['', '0', '1', '2', '3', '02', '03', '13', '010', '121', '212', '232', '1021', '1212', '0103', '0232', '2132', '01210', '02102', '12321', '32123', '010210', '012102', '210212', '021032', '102321', '121321', '132123', '232123', '1021021', '0123210', '2123212', '0321023', '01021021', '01210212', '10210212', '10210321', '01023210', '01213210', '12123212', '21023212', '12132123', '21232123', '01321023', '32102123', '010210212', '121232123', '021232102', '103210213', '0102103210', '0121232102', '0210232102', '0121321023', '0212321023', '0103210213', '0132102123', '1032102123', '01212321023', '10212321021', '21032102132', '01032102123', '010212321021', '102102321021', '102123210213', '021032102132', '2102123210212', '0102123210213', '0210321021232', '1210321021321', '21021232102132', '10210321021321', '21232102123212', '021021232102132', '012103210213210', '102103210212321', '321021232102123', '0102102123210212', '1210212321021321', '0102103210213210', '1210232102123212', '0212321021232102', '2321021232102123', '10210212321021321', '01021032102123210', '12102132102123212', '02321021232102123', '012102123210213210', '012102321021232102', '102123210212321021', '010321021232102123', '121321021232102123', '212321021232102123', '0102102123210213210', '0121021321021232102', '1021321021232102123', '1212321021232102123', '01021023210212321021', '21021232102123210212', '01210321021232102123', '02102321021232102123', '010210213210212321021', '010210321021232102123', '012102321021232102123', '210212321021232102123', '0121021232102123210212', '1021021232102123210212', '1021021321021232102123', '01021021232102123210212', '01021021321021232102123', '01210212321021232102123', '10210212321021232102123', '010210212321021232102123'] if poids[0]==poids[2]: return [[int(s) for s in i] for i in l1111] elif poids[0]>0 and poids[2]>poids[0]: if poids[2]==2*poids[0]: return [[int(s) for s in i] for i in l1122] else: return [[int(s) for s in i] for i in l1133] elif poids[2]>0 and poids[0]>poids[2]: J=[3,2,1,0] W=coxeter("F",4) if poids[0]==2*poids[2]: return [W.reducedword([J[int(s)] for s in i],W) for i in l1122] else: return [W.reducedword([J[int(s)] for s in i],W) for i in l1133] else: print('#W Sorry, not yet implemented') return False elif typ[0]=='E' and rk==[0,1,2,3,4,5]: reps=['', '3', '12', '015', '232', '1315', '01454', '020454', '020320', '0131431', '0120454', '1314315431', '23123431234', '01203120325', '123123431234', '020320432054320', '02031203431203243', '01203120324312032431', '020312043120324315431203243', '0120312032431203243154312032431', '012031203243120324315431203243154320'] chrs0=['0.1', '3.1', '10.1', '14.1c8.1', '14.1c6.1', '21.1', '19.1', '12.1', '23.1', '18.1c17.1c16.1', '17.1c16.1c2.1', '24.1', '18.2c17.1c5.1', '20.1', '13.1', '15.1c9.1', '22.1', '11.1', '15.1c7.1', '4.1', '1.1'] ch=chartable(W,chars=False)['charnames'] chars=[[[ch[int(k.split('.')[0])],int(k.split('.')[1])] for k in l.split('c')] for l in chrs0] if unpack==True: f=[W.permtoword(x) for x in flatlist([starorbitinv(W, W.wordtoperm([int(i) for i in r])) for r in reps])] f.sort(key=(lambda x:len(x))) else: f=[[[int(i) for i in reps[r]],chars[r]] for r in range(len(reps))] return f elif typ[0]=='E' and rk==[0,1,2,3,4,5,6]: reps=['', '2', '12', '020', '124', '146', '0120', '1246', '01204', '020320', '020454', '012046', '0131431', '0120454', '1314316', '01314316', '020320565', '0120312032', '0120454654', '12312343123', '01203120325', '123123431234', '123123431236', '1231234312346', '0120312032565', '020320432054320', '231243123565432', '131431543165431', '123431235654312', '0131431543165431', '01203120324312032', '01203435436543120', '012031203243120326', '01203120324312032431', '12312343543126543123', '012031203243120324316', '020320432054320654320', '123120343120543126543123', '232432543123456543123456', '020312043120324315431203243', '1234312032543654312032431546', '12312343123454312345654312345', '123123431234543123456543123456', '0120312032431203243154312032431', '0120343254312036543120324315465', '0131431543120654312032431543654', '0203204320543120345654312032435465', '012031203243120324315431203243154320', '123123431234543120324315432654312345', '123123431203243543265431203243154326', '02032043120543120326543120324315432065432', '123120324312032431543123456543120324315432', '123123431203243154312032431543206543120324315432', '01203120324312032431543120324315432065431203243154320', '01203120324312032431543120324315432065431203243154320654312345', '012031203243120324315431203243154320654312032431543206543123456'] chrs0=['0.1', '3.1', '10.1', '17.1c6.1', '17.1c14.1', '9.1', '28.1c23.1', '28.1c5.1', '35.1', '38.1', '30.1', '24.1', '49.1c46.1c45.1', '49.1c46.1c19.1', '37.1', '54.1c43.1', '53.1', '56.1c51.1', '40.1', '49.1c45.2c13.1', '59.1c58.1', '26.1', '54.1c32.1', '57.1c21.1', '41.1', '55.1c42.1', '53.1', '27.1', '35.1', '48.1c47.1c18.1', '57.1c50.1', '59.1c58.1', '52.1', '36.1', '56.1c20.1', '31.1', '25.1', '40.1', '48.1c47.1c44.1', '55.1c33.1', '24.1', '39.1', '16.1c15.1', '29.1c22.1', '41.1', '25.1', '34.1', '8.1', '34.1', '52.1', '29.1c4.1', '48.1c44.2c12.1', '16.1c7.1', '11.1', '2.1', '1.1'] ch=chartable(W,chars=False)['charnames'] chars=[[[ch[int(k.split('.')[0])],int(k.split('.')[1])] for k in l.split('c')] for l in chrs0] if unpack==True: f=[W.permtoword(x) for x in flatlist([starorbitinv(W, W.wordtoperm([int(i) for i in r])) for r in reps])] f.sort(key=(lambda x:len(x))) else: f=[[[int(i) for i in reps[r]],chars[r]] for r in range(len(reps))] return f elif typ[0]=='E' and rk==[0,1,2,3,4,5,6,7]: reps=['', '7', '12', '020', '146', '0205', '1246', '14547', '232432', '020454', '012046', '0204547', '0565765', '01204676', '12454654', '131431676', '0120454654', '2324325432', '04546547654', '23123431234', '124546547654', '131234312346', '123123431234', '123123565765', '1231234312346', '0204546547654', '12312343123676', '01204546547654', '231243123565432', '343543654376543', '123123431234676', '123431235654312', '0120312032565765', '0131431543165431', '13143123454312345', '01203435436543120', '012031203243120326', '01203120324312032676', '01203120324312032431', '12312343543126543123', '232432543265432765432', '012031203243120324316', '0131431543165431765431', '13124312354312346765431', '12343123543123467654312', '01203120324312032431676', '131431543123456543123456', '124543206543120324315465', '123120343120324354312032431', '012034312354312034676543120', '1312043120324543120324315437', '0131234312032435657654312034', '1234312032543654312032431546', '0203204320543206543207654320', '12312343123454312345654312345', '123123431234543123456543123456', '123123431234546543123765431234', '1231203431203243543120324315432', '0131431543120654312032431543654', '0120343254312036543120324315465', '01203120324312032431543120324317', '13143154316543123456765431234567', '0203204320543120345654312032435465', '0131431543165431234567654312034567', '012031203243120324315431203243154320', '123123431234543120324315432654312345', '123120345431203243654312032431543265', '012034312354312032431546765431203245', '0120312032431203243154312032431543207', '232431234543123456543123456765431234567', '02032043120543120326543120324315432065432', '12312034543654312034765431203243154326576', '123123431234543123456543123456765431234567', '123120324312032431543123456543120324315432', '0203204312054312032654312032431543207654320', '013143125431236543120324315432065432765431203456', '123123431203243154312032431543206543120324315432', '020320431203254312032431654312032431543207654320', '123123431203454312032435465432076543120324315432', '2312431235431234654312032431543206543176543123456', '0131234312032435436543120347654312032431543206543176', '01203120324312032431543120324315432065431203243154320', '02032043205431206543120327654312032431543206543276543', '012031203243120324315431236543123476543120324315432067', '012034354312032431654312032431543206543176543120324315465', '0120312032431203243154312032431565431234765431203243154320', '0131431235431234654312032435467654312032431543206543176543', '02031203243120324315431203243154320654312032431543206543123456', '012031203243120324315431203243154320654312032431543206543123456', '123120343120324354312032431543265431234576543120324315432654317', '1231203431203243543120324315432654312345676543120324315432654317', '12312343123454312032431565431203243154320654327654312032431543'+\ '265431', '12312343120324354312365431203243154676543120324315432065431237'+\ '6543123', '12312343120345431203243546543120324315432065431234576543120324'+\ '31543265431', '01203120324312032431543120324315432065431234567654312032431543'+\ '206543123457', '02032043205431203265431203243156765431203243154320654312345676'+\ '54312032435465', '12312343123454312345654312032431543206765431203243154320654312'+\ '345676543123456', '01203120324312032431543120324316543120324315432654317654312032'+\ '4315432065431234', '12312034312032435431203243154326543120324315432065431234576543'+\ '12032431543265431', '01203120324312032431543120324315432065431203243154326543176543'+\ '1203243154320654312345', '12312032431203243154312032431543206543120324315432076543120324'+\ '315432065431234576543120324315432', '12312343123454312032431565431203243154320654327654312032431543'+\ '2065431234567654312032431543265431', '12312343120324315431203243154320654312032431543206543123456765'+\ '43120324315432065431234576543120324315432', '01203120324312032431543120324315432065431203243154320654312345'+\ '6765431203243154320654312345765431203243154320', '01203120324312032431543120324315432065431203243154320654312345'+\ '676543120324315432065431234567654312032431543206543123456', '01203120324312032431543120324315432065431203243154320654312345'+\ '6765431203243154320654312345676543120324315432065431234567'] chrs0=['0.1', '67.1', '4.1', '71.1c2.1', '71.1c9.1', '73.1c14.1', '14.1c7.1', '80.1', '23.1', '28.1c17.1', '76.1c28.1', '92.1c78.1c39.1', '92.1c85.1c39.1', '42.1c36.1c12.1', '44.1c42.1c36.1', '99.1', '90.1c50.1', '88.1c53.1', '104.1c61.1', '92.1c85.2c69.1', '101.1c63.1', '44.2c42.1c19.1', '21.1', '63.1c31.1', '96.1c26.1', '108.1', '83.1c50.1', '56.1', '99.1', '110.1c59.1', '106.1', '80.1', '103.1c87.1c82.1c52.1c49.1c46.1c16.1', '103.2c98.1c87.1c58.1c55.1c52.1c49.1c46.1c41.1', '96.1c47.1', '104.1c61.1', '65.1', '103.1c98.1c82.1c58.1c52.1c49.1c46.1c25.1', '38.1', '53.1c34.1', '107.1', '111.1c60.1', '57.1', '106.1', '99.1', '109.1', '103.2c98.2c58.2c55.2c52.1c49.1c41.1c33.1c30.1', '90.1c50.1', '110.1c94.1', '99.1', '103.1c98.2c75.1c58.2c55.1c52.1c46.1c33.1c25.1', '101.1c63.1', '76.1c28.1', '91.1c51.1', '111.1c95.1', '54.1c35.1', '103.1c98.1c82.1c58.1c55.1c52.1c49.2c30.1c11.1', '97.1c48.1', '107.1', '108.1', '105.1c62.1', '102.1c64.1', '66.1', '102.1c64.1', '22.1', '66.1', '65.1', '90.1c50.1', '93.1c79.1c40.1', '100.1', '97.1c27.1', '108.1', '77.1c29.1', '103.1c98.2c75.1c58.3c55.3c52.1c33.3c30.2c6.1', '109.1', '57.1', '89.1c54.1', '105.1c62.1', '91.1c51.1', '109.1', '56.1', '93.1c86.1c40.1', '100.1', '64.1c32.1', '106.1', '84.1c51.1', '91.1c51.1', '24.1', '72.1c10.1', '100.1', '77.1c29.1', '45.1c43.1c37.1', '107.1', '100.1', '29.1c18.1', '43.1c37.1c13.1', '81.1', '45.2c43.1c20.1', '81.1', '74.1c15.1', '93.1c86.2c70.1', '15.1c8.1', '72.1c3.1', '5.1', '68.1', '1.1'] ch=chartable(W,chars=False)['charnames'] chars=[[[ch[int(k.split('.')[0])],int(k.split('.')[1])] for k in l.split('c')] for l in chrs0] if unpack==True: f=[W.permtoword(x) for x in flatlist([starorbitinv(W, W.wordtoperm([int(i) for i in r])) for r in reps])] f.sort(key=(lambda x:len(x))) else: f=[[[int(i) for i in reps[r]],chars[r]] for r in range(len(reps))] return f elif typ[0]=='I' and rk==[0,1]: m=int(typ[1:]) if m%2==1: if poids[0]>0: w0=[0] for i in range((m-1)//2): w0.extend([1,0]) return [[], [0], [1], w0] else: print('#W Sorry, not yet implemented') return False else: w1=[] for i in range((m-2)//2): w1.extend([0,1]) if poids[0]==poids[1] and poids[0]>0: return [[], [0], [1], w1+[0,1]] elif poids[0]>poids[1] and poids[1]>0: return [[],[0],[1],[1,0,1],w1+[0],w1+[0,1]] elif poids[0]0: return [[],[0],[1],[0,1,0],[1]+w1,w1+[0,1]] else: print('#W Sorry, not yet implemented') return False else: return False #F precells def precells(W,lw): """returns the partition of a set of elements into pieces which are unions of left cells (in the equal parameter case) and which are as small as possible with respect to (1) the induction of cells from parabolic subgroups and (2) Vogan's generalised tau-invariant. For example, in type A_n (any n) or E_6, this yields exactly the partition into left cells. >>> W=coxeter('E',6) >>> len(precells(W,involutions(W)) 652 (This is the number of left cells in type E_6.) See also 'klcells', 'distinguishedinvolutions' and 'gentauorbits'. """ p=['' for w in lw] for s in W.rank: J=list(W.rank) J.remove(s) H=reflectionsubgroup(W,J) c1=[[W.wordtocoxelm([J[i] for i in w]) for w in l] for l in klcells(H,1,v)[0]] for i in range(len(lw)): pw=lw[i] h=permmult(redinrightcoset(W,H,perminverse(pw)[:len(W.rank)]),pw) j=0 while not h in c1[j]: j+=1 p[i]+=str(j)+'.' reps=set(p) lprint('#I '+str(len(p))+' : '+str(len(reps))+' ---> ') np=[[lw[i] for i in range(len(lw)) if p[i]==r] for r in reps] if len(W.cartantype)==1 and (W.cartantype[0][1]==list(W.rank) and W.cartantype[0][0] in ['E','F','H','I']): distinv=[W.wordtoperm(x) for x in libdistinv(W,1)] else: distinv=[W.wordtoperm(x) for x in distinguishedinvolutions(W,1)[0]] res,npi=[],[] for l in range(len(np)): if len([x for x in np[l] if x in distinv])==1: res.append(np[l]) npi.append(0) else: npi.append(1) np=[np[l] for l in range(len(np)) if npi[l]==1] lprint(' '+str(len(np))+' ---> ') nonc=0 for l in range(len(np)): if l%100==0: lprint('.') g=gentauorbits(W,np[l],pr=False) res.extend(g) for lg in g: if len([x for x in lg if x in distinv])>1: nonc+=1 lprint(' '+str(len(res))+' ('+str(nonc)+')\n') return [[''.join([str(i) for i in W.permtoword(x)]) for x in l ] for l in res] ########################################################################## ## #Y Section 5: Tests ## #F timer def timer(func,*pargs,**kargs): """returns the result of applying a function and prints the time used in seconds. (Taken from M. Lutz's "Learning Python" book.) >>> t=timer(klcells,coxeter("F",4),1,v) #I 72 left cells (29 non-equivalent) 1.02 """ import time start=time.clock() ret=func(*pargs,**kargs) elapsed=time.clock()-start print(elapsed) return ret #F checksh def checksh(W,paramL): """checks if the relation characterising the Schur elements is true; see the help to 'schurelms'. (This is a good test for various functions: 'heckechartable', 'schurelms', 'lpol', 'zeta5', ...; it does not yet work for type I_2(m), n>7, because some operations for the general cyclotomic arithmetic are not implemented.) """ p=lcmschurelms(W,paramL) if type(paramL)==type([]): vs=paramL[:] else: vs=len(W.rank)*[paramL] gd=[divmod(p,s)[0] for s in schurelms(W,vs)] ti=heckechartable(W,vs)['irreducibles'] res=[sum(gd[i]*ti[i][j] for i in range(len(gd))) for j in range(len(gd))] return res[0]==p and all(i==0 for i in res[1:len(gd)]) #F test def test(): """runs a test suite. """ lprint('# ==> Should finish in less than 1 minute CPU time\n') v=lpol([1],1,'v') somechecks=[] # test all cartan types (finite and affine) W=coxeter('A',1); chartable(W) W=coxeter('A',2); chartable(W) W=coxeter('A',3) chartable(W) somechecks.append(checksh(W,v)) W=coxeter('A',4) t=chartable(W) somechecks.append(ainvariants(W,1)==t['a']) somechecks.append(checksh(W,v)) W=coxeter('B',2) chartable(W) somechecks.append(checksh(W,[v**2,v])) W=coxeter('B',3) chartable(W) somechecks.append(checksh(W,[v**2,v,v])) somechecks.append(checksh(W,[v**2,v**3,v**3])) W=coxeter('B',4) somechecks.append(checksh(W,v)) t=chartable(W) somechecks.append(ainvariants(W,1)==t['a']) lprint(str(t['a'])+'\n') W=coxeter('B',5) chartable(W) W=coxeter('C',2) somechecks.append(checksh(W,[v**2,v])) chartable(W) W=coxeter('C',3) chartable(W) W=coxeter('C',4) t=chartable(W) somechecks.append(checksh(W,v)) somechecks.append(ainvariants(W,1)==t['a']) W=coxeter('C',5) chartable(W) W=coxeter('D',4) somechecks.append(checksh(W,v)) t=chartable(W) somechecks.append(ainvariants(W,1)==t['a']) W=coxeter('D',5) t=chartable(W) #somechecks.append(ainvariants(W,1)==t['a']) W=coxeter('D',6) W=coxeter('D',7) W=coxeter('D',8) W=coxeter('G',2) chartable(W) somechecks.append(checksh(W,[v**2,v])) W=coxeter('I5',2) chartable(W) somechecks.append(checksh(W,v)) somechecks.append(sum(x[1] for x in specialpieces(W,v))==v**(2*W.N)) W=coxeter('F',4) t=chartable(W) somechecks.append(ainvariants(W,1)==t['a']) somechecks.append(checksh(W,[v**2,v**2,v,v])) somechecks.append(sum(x[1] for x in specialpieces(W,v))==v**(2*W.N)) W=coxeter('I5',2) chartable(W) W=coxeter('H',3) chartable(W) somechecks.append(checksh(W,v)) W=coxeter('H',4) chartable(W) W=coxeter('E',6) t=chartable(W) #somechecks.append(ainvariants(W,1)==t['a']) somechecks.append(checksh(W,v)) W=coxeter('E',7) chartable(W) W=coxeter('E',8) chartable(W) W=coxeter(affinecartanmat("A",1)) W=coxeter(affinecartanmat("A",2)) W=coxeter(affinecartanmat("A",3)) W=coxeter(affinecartanmat("A",4)) W=coxeter(affinecartanmat("B",3)) W=coxeter(affinecartanmat("B",4)) W=coxeter(affinecartanmat("B",5)) W=coxeter(affinecartanmat("C",2)) W=coxeter(affinecartanmat("C",3)) W=coxeter(affinecartanmat("C",4)) W=coxeter(affinecartanmat("D",4)) W=coxeter(affinecartanmat("D",5)) W=coxeter(affinecartanmat("D",6)) W=coxeter(affinecartanmat("G",2)) W=coxeter(affinecartanmat("F",4)) W=coxeter(affinecartanmat("E",6)) W=coxeter(affinecartanmat("E",7)) W=coxeter(affinecartanmat("E",8)) # mixed finite and affine type: W=coxeter([[2,0,-3,0,0,0],[0,2,-1,0,0,0],[-1,-1,2,0,0,0], [0,0,0,2,-1,0],[0,0,0,-1,2,-1],[0,0,0,0,-1,2]]) lprint(str(W.cartantype)+'\n') lprint(str(W.cartanname)+'\n') # check all functions on this example: W=coxeter([[2,0,0,0,0,0,0,0,0,0,0,-1,0,0,-2], [0,2,0,0,0,0,0,0,-1,0,0,0,-1,-1,0], [0,0,2,0,0,0,0,0,0,-1,0,0,0,0,0], [0,0,0,2,0,0,0,0,0,0,0,0,0,0,-1], [0,0,0,0,2,0,0,0,0,-1,0,0,0,0,0], [0,0,0,0,0,2,-1,0,0,0,-1,0,0,0,0], [0,0,0,0,0,-1,2,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,2,0,-1,-1,0,0,0,0], [0,-1,0,0,0,0,0,0,2,0,0,0,0,0,0], [0,0,-1,0,-1,0,0,-1,0,2,0,0,0,0,0], [0,0,0,0,0,-1,0,-1,0,0,2,0,0,0,0], [-1,0,0,0,0,0,0,0,0,0,0,2,0,0,0], [0,-1,0,0,0,0,0,0,0,0,0,0,2,0,0], [0,-1,0,0,0,0,0,0,0,0,0,0,0,2,0], [-1,0,0,-1,0,0,0,0,0,0,0,0,0,0,2]]) lprint(str(W.cartantype)+'\n') lprint(str(W.cartanname)+'\n') lprint(str(W.order)+'\n') somechecks.append(W.cartan==cartantypetomat(cartantotype(W.cartan))) c=conjugacyclasses(W); r=reflections(W) l=longestperm(W) a=allelmchain(W) H=reflectionsubgroup(W,[0,1,2,3,4,5,6,7,W.N-1]) lH=H.permtoword(longestperm(H)) lW=H.reducedword(lH,W); W=coxeter([[2,0,-1,0,0,0,0,0,0],[0,2,0,0,-1,0,0,0,0], [-2,0,2,0,0,0,0,0,0],[0,0,0,2,-1,0,-1,0,0],[0,-1,0,-1,2,0,0,-1,0], [0,0,0,0,0,2,0,0,-1],[0,0,0,-1,0,0,2,0,0],[0,0,0,0,-1,0,0,2,0], [0,0,0,0,0,-3,0,0,2]]) lprint(str(W.cartantype)+'\n') lprint(str(W.cartanname)+'\n') lprint(str(W.order)+'\n') somechecks.append(W.cartan==cartantypetomat(cartantotype(W.cartan))) cl=conjugacyclasses(W) H=reflectionsubgroup(W,[0,1,2,3,4,5,6,7,W.N-1]) # test embedding of reflections W=coxeter("F",4) c=coxeterclasses(W) rw=[W.coxelmtoword(r) for r in reflections(W)] for r in range(W.N): H=reflectionsubgroup(W,[r]) somechecks.append(rw[r]==H.reducedword([0],W)) somechecks.append(ainvariants(W,1)==chartable(W)['a']) W=coxeter(affinecartanmat("F",4)) a=[[W.mattoword(m) for m in l] for l in allmats(W,3)] W=coxeter("E",8) c=coxeterclasses(W) e=allelmchain(W) W.coxelmlength(longestperm(W)) H=reflectionsubgroup(W,[0,1,2,3,4,5,6]) f=fusionconjugacyclasses(H,W) t=inductiontable(H,W) lprint('# ==> looks good ... \n') W=coxeter("H",4) f=lusztigfamilies(W,1) somechecks.append(ainvariants(W,1)==chartable(W)['a']) c=coxeterclasses(W) c1=conjugacyclasses(W) e=allelmchain(W) d=redleftcosetreps(W,[0,1,2]) #ca=redleftcosetreps(W) #wa=[W.coxelmtoword(c) for c in ca] #ls=[W.coxelmlength(c) for c in ca] # does not work lprint('# ==> seems ok ! ... \n') #somechecks.append(ca==[W.wordtocoxelm(w) for w in wa]) W=coxeter("B",3) lc=[] for i in range(4): kl=klpolynomials(W,[i,1,1,1],v) lc.append(len(kl['lcells'])) somechecks.append(lc==[10,14,16,20]) k=[i.matrices(True) for i in klcells(W,1,v)[1]] somechecks.append(all([type(m)==type([]) for m in k])==True) k=[i.matrices(True) for i in klcells(W,[3,2,2],v)] somechecks.append(all([type(m)==type([]) for m in k])==True) W=coxeter("I14",2) k=[i.matrices(True) for i in klcells(W,1,v)[1]] somechecks.append(all([type(m)==type([]) for m in k])==True) k=[i.matrices(True) for i in klcells(W,[5,3],v)] somechecks.append(all([type(m)==type([]) for m in k])==True) # test all conversions: W=coxeter("H",3) gh3=[[[]],[[0],[0,1,0]],[[0,1,0,2,1,0]],[[0,1,0,1,2,1,0,1,0],[0,1,0,1,2, 1,0,1,0,2,1,0]],[[0,1,0,1,0]],[[0,1,0,1,0,2,1,0,1,0],[0,1,0,1,0,2,1,0, 1,0,2,1,0,1]],[[0,1,0,1,0,2,1,0,1,0,2,1,0,1,2]],[[0,2]],[[0,1,2,1,0], [0,1,2,1,0,1,0,2]],[[0,2,1,0,1,0,2]],[[0,1,0,2,1,0,1,0,2,1,0]],[[0,1, 2,1,0,1,0,2,1,0,1,2],[0,1,0,1,2,1,0,1,0,2,1,0,1,2]],[[1],[1,0,1]],[[1, 0,2,1]],[[1,0,1,2,1,0,1],[1,0,1,2,1,0,1,0,2,1]],[[1,0,1,0,2,1,0,1]], [[1,0,1,0,2,1,0,1,0,2,1,0,1]],[[1,2,1],[1,2,1,0,1,2]],[[1,0,2,1,0,1,0, 2,1]],[[1,0,2,1,0,1,0,2,1,0,1,2],[1,0,1,0,2,1,0,1,0,2,1,0,1,2]],[[2], [2,1,0,1,2]],[[2,1,0,1,0,2,1,0,1,2]]] cl=allclasspolynomials(W,v**2) l=[] for x in gh3: y=leftcellleadingcoeffs(W,1,v,x,cl) l.append(y['ti']) somechecks.append(l==[[[('1_r',), [1]]], [[("3_s'",), [1, ir5]], [("overline{3}_s'",), [1, 1-ir5]]], [[('5_r',), [1]]], [[("4_r'",), [1, 1]], [('4_r',), [1, -1]]], [[("5_r'",), [1]]], [[('3_s',), [ir5, 1]], [('overline{3}_s',), [1-ir5, 1]]], [[("1_r'",), [1]]], [[('5_r',), [1]]], [[("4_r'",), [1, 1]], [('4_r',), [1, -1]]], [[("5_r'",), [1]]], [[("5_r'",), [1]]], [[('3_s',), [ir5, 1]], [('overline{3}_s',), [1-ir5, 1]]], [[("3_s'",), [1, ir5]], [("overline{3}_s'",), [1, 1-ir5]]], [[('5_r',), [1]]], [[("4_r'",), [1, 1]], [('4_r',), [1, -1]]], [[('5_r',), [1]]], [[("5_r'",), [1]]], [[("4_r'",), [1, 1]], [('4_r',), [1, -1]]], [[("5_r'",), [1]]], [[('3_s',), [ir5, 1]], [('overline{3}_s',), [1-ir5, 1]]], [[("3_s'",), [1, ir5]], [("overline{3}_s'",), [1, 1-ir5]]], [[('5_r',), [1]]]]) c=allcellsleadingcoeffs(W,1,v) c=constructible(W,1) ah=redleftcosetreps(W) ap=[W.coxelmtoperm(c) for c in ah] aw=[W.coxelmtoword(c) for c in ah] am=[W.coxelmtomat(c) for c in ah] alc=[W.coxelmlength(c) for c in ah] alm=[W.matlength(m) for m in am] somechecks.append(alc==[len(w) for w in aw]) somechecks.append(alc==alm) somechecks.append(ah==[c[:len(W.rank)] for c in ap]) somechecks.append(am==[W.coxelmtomat(c) for c in ah]) somechecks.append(aw==[W.coxelmtoword(c) for c in ah]) somechecks.append(aw==[W.permtoword(c) for c in ap]) somechecks.append(ap==[W.coxelmtoperm(c) for c in ah]) somechecks.append(ap==[W.mattoperm(m) for m in am]) somechecks.append(ah==[W.mattocoxelm(m) for m in am]) somechecks.append(aw==[W.mattoword(m) for m in am]) somechecks.append(ah==[W.wordtocoxelm(w) for w in aw]) somechecks.append(am==[W.wordtomat(w) for w in aw]) somechecks.append(ap==[W.wordtoperm(w) for w in aw]) somechecks.append(aw==[W.reducedword(w,W) for w in aw]) aa=allwords(W) somechecks.append(set([w in aa for w in aw])==set([True])) aa=[W.stringtoword(x) for x in allwordstrings(W)] somechecks.append(set([w in aa for w in aw])==set([True])) w=W.wordtoperm([0,1,0,2]); b1=list(filter(lambda x: bruhatcoxelm(W,x,w),ah)) b1a=list(filter(lambda x: bruhat(W,x,w),ah)) elms=[W.coxelmtoperm(c) for c in ah] b2=list(filter(lambda x: bruhatperm(W,x,w),elms)) b2a=list(filter(lambda x: bruhat(W,x,w),elms)) mats=[W.coxelmtomat(c) for c in ah] b3a=list(filter(lambda x: bruhat(W,x,w),elms)) somechecks.append([W.coxelmtoword(p) for p in b2]==[W.coxelmtoword(p) for p in b1]) somechecks.append([W.coxelmtoword(p) for p in b2a]==[W.coxelmtoword(p) for p in b1a]) W=coxeter('F',4) f=lusztigfamilies(W,0) f=lusztigfamilies(W,1) f=lusztigfamilies(W,[3,3,2,2]) f=lusztigfamilies(W,[2,2,1,1]) c=constructible(W,[2,2,1,1]) f=lusztigfamilies(W,[3,3,1,1]) lprint('#I ') p=[3,2,1,0] nc=[[W.cartan[i][j] for j in p] for i in p] somechecks.append(nc==cartantypetomat(cartantotype(nc))) aa=redleftcosetreps(W) w1=[0,2,1,0,2,3,2,1,0,2,1,2,3,2,1,0,2,1,2,3] lb4=[] for l in range(1,21): l1=len(list(filter(lambda x:bruhatperm(W,W.coxelmtoperm(x), W.wordtoperm(w1[:l])),aa))) lb4.append(l1) lprint(str(l1)+' ') lprint('\n#####################################') lprint('\n# ==> ') somechecks.append(lb4==[2,4,8,12,20,40,60,96,132,196,244,272, 396,456,508,680,720,828,912,972]) b5=list(filter(lambda x: W.coxelmlength(x)<=len(w1),aa)) somechecks.append(len(b5)==1122) lprint(str(len(somechecks))+' true/false checks performed\n') lprint('#####################################\n') if False in somechecks: lprint('# ==> !!! There are problems !!!\n') return False else: lprint('# ==> Tests ok\n') return True