/* LA: linear algebra C++ interface library Copyright (C) 2021 Jiri Pittner or 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 . */ #include "vec.h" #include "permutation.h" #include "miscfunc.h" #include #include #include #include "qsort.h" #include "bitvector.h" namespace LA { template void NRPerm::identity() { T n=this->size(); #ifdef DEBUG if(n<0) laerror("invalid permutation size"); #endif if(n==0) return; this->copyonwrite(); for(T i=1; i<=n; ++i) (*this)[i]=i; } template bool NRPerm::is_identity() const { T n=this->size(); #ifdef DEBUG if(n<0) laerror("invalid permutation size"); #endif if(n==0) return 1; for(T i=1; i<=n; ++i) if((*this)[i]!=i) return 0; return 1; } template bool NRPerm::is_valid() const { T n = this->size(); if(n<0) return 0; NRVec_from1 used(n); used.clear(); for(T i=1; i<=n; ++i) { T x= (*this)[i]; if(x<1||x>n) return 0; used[x] += 1; } for(T i=1; i<=n; ++i) if(used[i]!=1) return 0; return 1; } template NRPerm NRPerm::inverse() const { #ifdef DEBUG if(!this->is_valid()) laerror("inverse of invalid permutation"); #endif NRPerm q(this->size()); for(T i=1; i<=this->size(); ++i) q[(*this)[i]]=i; return q; } template NRPerm NRPerm::reverse() const { #ifdef DEBUG if(!this->is_valid()) laerror("reverse of invalid permutation"); #endif NRPerm q(this->size()); for(T i=1; i<=this->size(); ++i) q[i]=(*this)[this->size()-i+1]; return q; } template NRPerm NRPerm::operator&(const NRPerm &q) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("concatenation of invalid permutation"); #endif NRPerm r(size()+q.size()); for(int i=1; i<=size(); ++i) r[i]=(*this)[i]; for(int i=1; i<=q.size(); ++i) r[size()+i]=size()+q[i]; return r; } template NRPerm NRPerm::operator|(const NRPerm &q) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("concatenation of invalid permutation"); #endif NRPerm r(size()+q.size()); for(int i=1; i<=q.size(); ++i) r[i]=size()+q[i]; for(int i=1; i<=size(); ++i) r[q.size()+i]=(*this)[i]; return r; } template NRPerm NRPerm::operator*(const NRPerm &q) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("multiplication of invalid permutation"); #endif T n=this->size(); if(n!=q.size()) laerror("product of incompatible permutations"); NRPerm r(n); for(T i=1; i<=n; ++i) r[i] = (*this)[q[i]]; return r; } template NRPerm NRPerm::multiply(const NRPerm &q, bool inverse) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("multiplication of invalid permutation"); #endif T n=this->size(); if(n!=q.size()) laerror("product of incompatible permutations"); NRPerm r(n); if(inverse) for(T i=1; i<=n; ++i) r[q[i]] = (*this)[i]; else for(T i=1; i<=n; ++i) r[i] = (*this)[q[i]]; return r; } template NRPerm NRPerm::conjugate_by(const NRPerm &q, bool inverse) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("multiplication of invalid permutation"); #endif T n=this->size(); if(n!=q.size()) laerror("product of incompatible permutations"); NRPerm qi=q.inverse(); NRPerm r(n); if(inverse) for(T i=1; i<=n; ++i) r[i] = q[(*this)[qi[i]]]; else for(T i=1; i<=n; ++i) r[i] = qi[(*this)[q[i]]]; return r; } template NRPerm NRPerm::commutator(const NRPerm &q, bool inverse) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("multiplication of invalid permutation"); #endif T n=this->size(); if(n!=q.size()) laerror("product of incompatible permutations"); NRPerm qi=q.inverse(); NRPerm pi=this->inverse(); NRPerm r(n); if(inverse) for(T i=1; i<=n; ++i) r[i] = qi[pi[q[(*this)[i]]]]; else for(T i=1; i<=n; ++i) r[i] = pi[qi[(*this)[q[i]]]]; return r; } template CyclePerm CyclePerm::conjugate_by(const CyclePerm &q) const { #ifdef DEBUG if(!this->is_valid() || !q.is_valid()) laerror("multiplication of invalid permutation"); #endif return q.inverse()*((*this)*q); } //NOTE: for larger permutations it might be more efficient to use cycle decomposition but where exactly the breakeven is? //at the moment we do it the trivial way for small permutations and using sort for larger ones template int NRPerm::parity() const { if(!this->is_valid()) return 0; T n=this->size(); if(n==1) return 1; if(n>=10) //??? { NRPerm tmp(*this); return (tmp.sort()&1) ? -1:1; } T count=0; for(T i=2;i<=n;i++) for(T j=1;j(*this)[i]) count++; return (count&1)? -1:1; } template NRPerm::NRPerm(const CyclePerm &rhs, const int n) { #ifdef DEBUG if(!rhs.is_valid()) laerror("invalid cycle permutation"); #endif int m; if(n) m=n; else m=rhs.max(); this->resize(m); identity(); T ncycles=rhs.size(); for(T j=1; j<=ncycles; ++j) { T length= rhs[j].size(); for(T i=1; i<=length; ++i) (*this)[rhs[j][i]] = rhs[j][i%length+1]; } #ifdef DEBUG if(!is_valid()) laerror("internal error in NRPerm constructor from CyclePerm"); #endif } template void NRPerm::randomize(void) { int n=this->size(); if(n<=0) laerror("cannot randomize empty permutation"); this->copyonwrite(); this->identity(); for(int i=n-1; i>=1; --i) { int j= RANDINT32()%(i+1); T tmp = (*this)[i+1]; (*this)[i+1]=(*this)[j+1]; (*this)[j+1]=tmp; } } template bool NRPerm::next(void) { this->copyonwrite(); int n=this->size(); // Find longest non-increasing suffix and the pivot int i = n; while (i > 1 && (*this)[i-1] >= (*this)[i]) --i; if (i<=1) return false; T piv=(*this)[i-1]; // Find rightmost element greater than the pivot int j = n; while ((*this)[j] <= piv) --j; // Now the value array[j] will become the new pivot, Assertion: j >= i // Swap the pivot with j (*this)[i - 1] = (*this)[j]; (*this)[j] = piv; // Reverse the suffix j = n; while (i < j) { T temp = (*this)[i]; (*this)[i] = (*this)[j]; (*this)[j] = temp; i++; j--; } return true; } //Algorithm L from Knuth's volume 4 template PERM_RANK_TYPE NRPerm::generate_all(void (*callback)(const NRPerm&), int select) { int n=this->size(); NRVec_from1 c((T)0,n); NRVec_from1 d((T)1,n); int j,k,s; T q; T t; this->identity(); PERM_RANK_TYPE sumperm=0; p2: sumperm++; if(callback) {if(!select || (select&1) == (sumperm&1)) {(*callback)(*this); this->copyonwrite();}} j=n; s=0; p4: q=c[j]+d[j]; if(q<0) goto p7; if(q==j) goto p6; t=(*this)[j-c[j]+s]; (*this)[j-c[j]+s]=(*this)[j-q+s]; (*this)[j-q+s]=t; c[j]=q; goto p2; p6: if(j==1) goto end; s++; p7: d[j]= -d[j]; j--; goto p4; end: return select? sumperm/2:sumperm; } template static PermutationAlgebra *list_all_return; static PERM_RANK_TYPE list_all_index; template static void list_all_callback(const NRPerm &p) { (*list_all_return)[list_all_index].weight= (list_all_index&1)?-1:1; (*list_all_return)[list_all_index].perm=p; (*list_all_return)[list_all_index].perm.copyonwrite(); ++list_all_index; } template PermutationAlgebra NRPerm::list_all(int parity_sel) { PERM_RANK_TYPE number = factorial(this->size()); if(parity_sel) number/=2; PermutationAlgebra ret(number); list_all_return = &ret; list_all_index=0; this->generate_all(list_all_callback,parity_sel); return ret; } template bool PermutationAlgebra::operator==(PermutationAlgebra &rhs) { simplify(); rhs.simplify(); if(size()!=rhs.size()) return false; for(int i=0; i PERM_RANK_TYPE NRPerm::generate_all_multi(void (*callback)(const NRPerm&)) { PERM_RANK_TYPE sumperm=0; this->copyonwrite(); myheapsort(this->size(),&(*this)[1],1); while(1) { int j,k,l; T t; sumperm++; if(callback) (*callback)(*this); j=this->size()-1; while(j>0 && (*this)[j]>=(*this)[j+1]) j--; if(j==0) break; /*finished*/ l=this->size(); while((*this)[j]>=(*this)[l]) l--; t=(*this)[j];(*this)[j]=(*this)[l];(*this)[l]=t; k=j+1; l=this->size(); while(k static T _n; template static void (*_callback)(const NRPerm& ); static PERM_RANK_TYPE _sumperm; template static NRPerm *_perm; template static void permg(T n) { if(n<= _n) { for(T i=1;i<= _n;i++) { if(!(*_perm)[i]) //place not occupied { (*_perm)[i]=n; permg(n+1); (*_perm)[i]=0; } } } else { _sumperm++; if(_callback) (*_callback)(*_perm); } } template PERM_RANK_TYPE NRPerm::generate_all2(void (*callback)(const NRPerm&)) { this->copyonwrite(); this->clear(); _n = this->size(); _callback =callback; _sumperm=0; _perm = this; permg(1); return _sumperm; } template PERM_RANK_TYPE NRPerm::generate_all_lex(void (*callback)(const NRPerm&)) { PERM_RANK_TYPE np=0; this->identity(); do{ ++np; if(callback) (*callback)(*this); }while(this->next()); return np; } template PermutationAlgebra NRPerm::list_all_lex() { PERM_RANK_TYPE number = factorial(this->size()); PermutationAlgebra ret(number); PERM_RANK_TYPE np=0; this->identity(); do{ ret[np].perm = *this; ret[np].perm.copyonwrite(); ret[np].weight=0; ++np; }while(this->next()); return ret; } template static T _n2; template static void (*_callback2)(const NRPerm& ); static PERM_RANK_TYPE _sumperm2; template static NRPerm *_perm2; template static const T *pclasses2; static int sameclass; template static void permg2(T n) //place number n in a free box in all possible ways and according to restrictions { T i; if(n<=_n2) { T c=pclasses2[n]; for(i=1;i<=_n2;i++) { if((*_perm2)[i]>=1) goto skipme; /*place already occupied*/ if (sameclass) { if(sameclass==1 && c!=pclasses2[i]) goto skipme; if(sameclass== -1 && c==pclasses2[i] ) goto skipme; if(sameclass== -2) /*allow only permutations which leave elements of each class sorted*/ { T j,k; for(j=i+1; j<=_n2; j++) if((k=(*_perm2)[j])>=1) if(/* automatically fulfilled k[k]) goto skipme; } } /*put it there and next number*/ (*_perm2)[i]=n; permg2(n+1); (*_perm2)[i]=0; skipme:; } } else { _sumperm2++; if(_callback2) {(*_callback2)(*_perm2); _perm2 -> copyonwrite();} } } template PERM_RANK_TYPE NRPerm::generate_restricted(void (*callback)(const NRPerm&), const NRVec_from1 &classes, int restriction_type) { this->copyonwrite(); this->clear(); _n2 = this->size(); _callback2 =callback; _sumperm2=0; _perm2 = this; pclasses2 = &classes[1]-1; sameclass=restriction_type; permg2(1); return _sumperm2; } template static PermutationAlgebra *list_restricted_return; static PERM_RANK_TYPE list_restricted_index; static bool list_restricted_invert; template static void list_restricted_callback(const NRPerm &p) { (*list_restricted_return)[list_restricted_index].weight=p.parity(); (*list_restricted_return)[list_restricted_index].perm= list_restricted_invert?p.inverse():p; (*list_restricted_return)[list_restricted_index].perm.copyonwrite(); ++list_restricted_index; } template PermutationAlgebra NRPerm::list_restricted(const NRVec_from1 &classes, int restriction_type, bool invert) { PERM_RANK_TYPE number = this->generate_restricted(NULL,classes,restriction_type); PermutationAlgebra ret(number); list_restricted_return = &ret; list_restricted_index=0; list_restricted_invert=invert; generate_restricted(list_restricted_callback,classes,restriction_type); return ret; } template PERM_RANK_TYPE NRPerm::rank() const { int c,i,k; PERM_RANK_TYPE r; int n= this->size(); r=0; for (k=1; k<=n; ++k) { T l=(*this)[k]; c=0; for(i=k+1;i<=n;++i) if((*this)[i] NRVec_from1 NRPerm::inversions(const int type, PERM_RANK_TYPE *prank) const { PERM_RANK_TYPE s=0; int n=this->size(); int j,k; T l; NRVec_from1 i(n); i.clear(); switch(type) { case 3: /*number of elements right from p[j] smaller < p[j]*/ for(j=1;jj;--k) { if((*this)[k]p[j]*/ for(j=2;j<=n;++j) /*number of elements left from j bigger >j*/ { l=(*this)[j]; for(k=1;kl) ++i[j]; } } break; case 1: for(j=1;j<=n;++j) /*number of elements right from j smaller =1;--k) { if((*this)[k]==j) break; if((*this)[k]j*/ { for(k=1;k<=n;++k) { if((*this)[k]==j) break; if((*this)[k]>j) ++i[j]; } } break; default: laerror("illegal type in inversions"); if(prank) { if(type!=3) laerror("rank can be computed from inversions only for type 3"); l=1; for(j=1;j NRPerm::NRPerm(const int type, const NRVec_from1 &i) { int n=i.size(); this->resize(n); int k,l; T j; switch(type) { case 2: case 1: for(l=0,j=1; j<=n; ++j,++l) { /*shift left and place*/ for(k=n-l+1; k<=n-i[j]; ++k) (*this)[k-1]=(*this)[k]; (*this)[n-i[j]]=j; } break; case 3: case 0: for(l=0,j=n; j>0; --j,++l) { /*shift right and place*/ for(k=l; k>=i[j]+1; --k) (*this)[k+1]=(*this)[k]; (*this)[i[j]+1]=j; } break; default: laerror("illegal type in nrperm from inversions"); } if(type>=2) (*this) = this->inverse(); } template NRPerm::NRPerm(const int n, const PERM_RANK_TYPE rank) { this->resize(n); NRVec_from1 inv(n) ; #ifdef DEBUG if(rank>=factorial(n)) laerror("illegal rank for this n"); #endif inv[n]=0; int k; PERM_RANK_TYPE r=rank; for(k=n-1; k>=0; --k) { PERM_RANK_TYPE t; t=factorial(k); inv[n-k]=r/t; r=r%t; } *this = NRPerm(3,inv); } template PermutationAlgebra PermutationAlgebra::operator&(const PermutationAlgebra &rhs) const { PermutationAlgebra res(this->size()*rhs.size()); for(int i=0; isize(); ++i) for(int j=0; j PermutationAlgebra PermutationAlgebra::operator|(const PermutationAlgebra &rhs) const { PermutationAlgebra res(this->size()*rhs.size()); for(int i=0; isize(); ++i) for(int j=0; j PermutationAlgebra PermutationAlgebra::operator*(const PermutationAlgebra &rhs) const { if(this->size()>0 && rhs.size()>0 && (*this)[0].perm.size()!=rhs[0].perm.size()) laerror("permutation sizes do not match"); PermutationAlgebra res(this->size()*rhs.size()); for(int i=0; isize(); ++i) for(int j=0; j PermutationAlgebra PermutationAlgebra::operator+(const PermutationAlgebra &rhs) const { if(this->size()>0 && rhs.size()>0 && (*this)[0].perm.size()!=rhs[0].perm.size()) laerror("permutation sizes do not match"); PermutationAlgebra res=this->concat(rhs); res.simplify(); return res; } //////////////////////////////////////////////////////// template CyclePerm:: CyclePerm(const NRPerm &p) { #ifdef DEBUG if(!p.is_valid()) laerror("invalid permutation"); #endif T n=p.size(); NRVec_from1 used((T)0,n),tmp(n); T firstunused=1; T currentcycle=0; std::list > cyclelist; do { //find a cycle starting with first unused element T cyclelength=0; T next = firstunused; do { ++cyclelength; used[next]=1; tmp[cyclelength] = next; next = p[next]; } while(used[next]==0); if(cyclelength>1) //nontrivial cycle { NRVec_from1 cycle(&tmp[1],cyclelength); cyclelist.push_front(cycle); ++currentcycle; } while(used[firstunused]) {++firstunused; if(firstunused>n) break;} //find next unused element } while(firstunused<=n); //convert list to NRVec this->resize(currentcycle); T i=1; for(typename std::list >::iterator l=cyclelist.begin(); l!=cyclelist.end(); ++l) (*this)[i++] = *l; } template bool CyclePerm::is_valid() const { for(T i=1; i<=this->size(); ++i) { T n=(*this)[i].size(); if(n<=0) return false; for(T j=1; j<=n; ++j) { T x=(*this)[i][j]; if(x<=0) return false; //now check for illegal duplicity of numbers withis a cycle or across cycles for(T ii=i; ii<=this->size(); ++ii) { T nn=(*this)[ii].size(); for(T jj=(ii==i?j+1:1); jj<=nn; ++jj) { T xx=(*this)[ii][jj]; if(x==xx) return false; } } } } return true; } template bool CyclePerm::is_identity() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid cycleperm"); #endif for(T i=1; i<=this->size(); ++i) if((*this)[i].size()>1) return false; //at least one nontrivial cycle return true; } template CyclePerm CyclePerm::inverse() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid cycleperm"); #endif CyclePerm r; T ncycles=this->size(); r.resize(ncycles); for(T i=1; i<=ncycles; ++i) { T length=(*this)[i].size(); r[i].resize(length); //reverse order in cycles (does not matter in cycle lengths 1 and 2 anyway) for(T j=1; j<=length; ++j) r[i][j] = (*this)[i][length-j+1]; } return r; } //multiplication via NRPerm - could there be a more efficient direct algorithm? template CyclePerm CyclePerm::operator*(const CyclePerm &q) const { int m=this->max(); int mm=q.max(); if(mm>m) mm=m; NRPerm qq(q,m); NRPerm pp(*this,m); NRPerm rr=pp*qq; return CyclePerm(rr); } //mixed type multiplications template NRPerm NRPerm::operator*(const CyclePerm &r) const { NRPerm rr(r,this->size()); return *this * rr; } template NRPerm CyclePerm::operator*(const NRPerm &r) const { NRPerm tt(*this,r.size()); return tt*r; } template void CyclePerm::simplify(bool keep1) { int j=0; for(int i=1; i<=this->size(); ++i) { int il= (*this)[i].size(); if(keep1 && il>0 || il>1 ) { ++j; if(j!=i) (*this)[j] = (*this)[i]; //keep this } } this->resize(j,true); } template int CyclePerm::parity() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid cycleperm"); #endif int n_even_cycles=0; T ncycles=this->size(); for(T i=1; i<=ncycles; ++i) { T length=(*this)[i].size(); if((length&1)==0) ++n_even_cycles; } return (n_even_cycles&1)?-1:1; } template PERM_RANK_TYPE CyclePerm::order() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid cycleperm"); #endif PERM_RANK_TYPE r=1; T ncycles=this->size(); for(T i=1; i<=ncycles; ++i) { T length=(*this)[i].size(); r=lcm(r,(PERM_RANK_TYPE)length); } return r; } template CompressedPartition CyclePerm::cycles(T n) const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid cycleperm"); #endif if(n==0) n=max(); CompressedPartition r(n); r.clear(); T ncycles=this->size(); for(T i=1; i<=ncycles; ++i) { T length=(*this)[i].size(); if(length<=0||length>n) laerror("unexpected cycle length in permutation"); r[length]++; } //fill in trivial cycles of length one r[1] += n - r.sum(); if(r[1]<0) laerror("inconsistent cycle lengths in CyclePerm::cycles"); return r; } //auxiliary function for input of a permutation in cycle format //returns pointer after closing bracket or NULL if no cycle found //or input error template const char *read1cycle(NRVec_from1 &c, const char *p) { if(*p==0) return NULL; const char *openbracket = strchr(p,'('); if(!openbracket) return NULL; const char *closebracket = strchr(openbracket+1,')'); if(!closebracket) return NULL; const char *s = openbracket+1; int r; int length=0; std::list cycle; do { long int tmp; int nchar; if(*s==',') ++s; r = sscanf(s,"%ld%n",&tmp,&nchar); if(r==1) { ++length; s += nchar; cycle.push_back((T)tmp); } } while(r==1 && s::iterator l=cycle.begin(); l!=cycle.end(); ++l) c[++i] = *l; return closebracket+1; } template void CyclePerm::readfrom(const std::string &line) { const char *p=line.c_str(); std::list > cyclelist; int ncycles=0; int count=0; NRVec_from1 c; while(p=read1cycle(c,p)) { //printf("cycle %d of length %d read\n",count,c.size()); if(c.size()!=0) //store a nonempty cycle { ++count; cyclelist.push_back(c); } } //convert list to vector this->resize(count); T i=0; for(typename std::list >::iterator l=cyclelist.begin(); l!=cyclelist.end(); ++l) (*this)[++i] = *l; #ifdef DEBUG if(!this->is_valid()) laerror("readfrom received input of invalid CyclePerm"); #endif } template std::istream & operator>>(std::istream &s, CyclePerm &x) { std::string l; getline(s,l); x.readfrom(l); return s; } template std::ostream & operator<<(std::ostream &s, const CyclePerm &x) { for(int i=1; i<=x.size(); ++i) { s<<"("; for(int j=1; j<=x[i].size(); ++j) { s< CyclePerm CyclePerm::pow(const int n, const bool keep1) const { #ifdef DEBUG if(!this->is_valid()) laerror("power of invalid permutation"); #endif CyclePerm r; if(n==0) {r.identity(); return r;} //probably negative n will work automatically using the modulo approach std::list > cyclelist; T currentcycle=0; //go through all our cycles and compute power of each cycle separately for(int i=1; i<=this->size(); ++i) { int cyclesize = (*this)[i].size(); if(cyclesize>0 && keep1 || cyclesize>1) { int modulo = n%cyclesize; if(modulo<0) modulo += cyclesize; if(modulo==0) { if(keep1) //keep cycles of length 1 to keep info about the size of the permutation { for(int j=1; j<=cyclesize; ++j) {NRVec_from1 c(1); c[1] = (*this)[i][j]; ++currentcycle; cyclelist.push_back(c);} } } else //the nontrivial case { int nsplit=gcd(modulo,cyclesize); int splitsize=cyclesize/nsplit; for(int j=0; j c(splitsize); for(int k=1; k<=splitsize; ++k) //fill in the cycle { c[k] = (*this)[i][((k-1)*modulo)%cyclesize + 1 + j]; } ++currentcycle; cyclelist.push_back(c); } } } } //convert list to NRVec r.resize(currentcycle); int i=1; for(typename std::list >::iterator l=cyclelist.begin(); l!=cyclelist.end(); ++l) r[i++] = *l; return r; } /////////////////////////////////////////////////////// template PERM_RANK_TYPE CompressedPartition::Sn_class_size() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid partition"); #endif int n=this->size(); PERM_RANK_TYPE r=factorial(n); for(int i=1; i<=n; ++i) { T m=(*this)[i]; if(i>1 && m>0) r/=longpow(i,m); if(m>1) r/=factorial(m); } return r; } template PERM_RANK_TYPE Partition::Sn_irrep_dim() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid partition"); #endif int n=this->size(); PERM_RANK_TYPE prod=1; Partition adj=this->adjoint(); //hook length formula for(int i=1; i<=adj[1]; ++i) //rows for(int j=1; j<= (*this)[i]; ++j) //cols prod *= (*this)[i]-j+adj[j]-i+1; return factorial(n)/prod; } /*hammermesh eq 10-25, highest weight == generalized partition of r into n parts*/ template PERM_RANK_TYPE Partition::Un_irrep_dim(const int n) const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid partition"); #endif if(nparts()>n) return 0; //too antisymmetric partition int i,j; double prod; int r=this->size(); NRVec_from1 p(n); for(i=1;i<=n;i++) p[i]= (i<=r?(*this)[i]:0)+n-i; prod=1; for(j=n;j>=2;j--) { for(i=1;i Partition Partition::adjoint() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid partition"); #endif int n=this->size(); Partition r(n); r.clear(); for(int i=1;i<=n;++i) { int j; for(j=1; j<=n&&(*this)[j]>=i; ++j); r[i]=j-1; } return r; } template Partition::Partition(const YoungTableaux &x) { #ifdef DEBUG if(!x.is_valid()) laerror("operation with an invalid tableaux"); #endif int nparts=x.size(); int n=0; for(int i=1; i<=nparts; ++i) n+= x[i].size(); this->resize(n); this->clear(); for(int i=1; i<=nparts; ++i) (*this)[i]=x[i].size(); } //aux for the recursive procedure static PERM_RANK_TYPE partitioncount; template static void (*_pcallback)(const Partition&); static int partitiontyp; static int partitiontypabs; template static Partition *partition; template void partgen(int remain, int pos) { int hi,lo; if(remain==0) {++partitioncount; if(_pcallback) (*_pcallback)(*partition); return;} if(partitiontyp) lo=(remain+partitiontypabs-pos)/(partitiontypabs-pos+1); else lo=1; hi=remain; if(partitiontyp>0) hi -= partitiontypabs-pos; if(pos>1 && (*partition)[pos-1] < hi) hi= (*partition)[pos-1]; for((*partition)[pos]=hi; (*partition)[pos]>=lo; --(*partition)[pos]) partgen(remain-(*partition)[pos],pos+1); (*partition)[pos]=0; } template PERM_RANK_TYPE Partition::generate_all(void (*callback)(const Partition&), int nparts) { int n=this->size(); if(n==0) return 0; if(nparts>0 && ncopyonwrite(); this->clear(); partitioncount=0; _pcallback =callback; partitiontyp=nparts; partition = this; partitiontypabs= nparts>=0?nparts:-nparts; partgen(n,1); return partitioncount; } template std::ostream & operator<<(std::ostream &s, const CompressedPartition &x) { int n=x.size(); T sum=0; for(int i=n; i>0;--i) if(x[i]) { s<1) s<<'^'< int Partition::parity() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid partition"); #endif T n_even_cycles=0; T n=this->size(); for(T i=1; i<=n; ++i) { if((*this)[i]!=0 && ((*this)[i] &1 ) ==0) ++n_even_cycles; } return (n_even_cycles&1)?-1:1; } template int CompressedPartition::parity() const { #ifdef DEBUG if(!this->is_valid()) laerror("operation with an invalid partition"); #endif T n_even_cycles=0; T n=this->size(); for(T i=2; i<=n; i+=2) n_even_cycles += (*this)[i]; return (n_even_cycles&1)?-1:1; } /*see M. Aigner - Combinatorial Theory - number of partitions of number n*/ #define MAXPART 260 #define MIN(x,y) ((x)<(y)?(x):(y)) /* here even few more columns more could be saved, and also the property part(n,k>=n/2)=part(n-k) could be used */ #define partind(n,k) (((n)-3)*((n)-2)/2+(k)-2) #define getpart(n,k) (((k)>(n) || (k)<=0 )?0:(((k)==(n) || (k)==1)?1:(part[partind((n),(k))]))) PERM_RANK_TYPE partitions(int n, int k) { static PERM_RANK_TYPE part[partind(MAXPART+1,1)]={1}; static PERM_RANK_TYPE npart[MAXPART+1]={0,1,2,3}; static int ntop=3; int i,j,l; PERM_RANK_TYPE s; if( n<=0 || k<-1 ) laerror("Partitions are available only for positive numbers"); if(k== -1) /* total partitions */ { if(n>ntop)(void)partitions(n,3); /* make sure that table is calculated */ return npart[n]; } if(n==k||k==1) return 1; if(k==0||k>n) return 0; if(k==2) return n/2; if(n>MAXPART && k>=n/2) return(partitions(n-k,-1)); if( n>MAXPART) laerror("sorry, too big argument to partition enumerator"); if(n<=ntop) return(getpart(n,k)); /*calculate, if necessary, few next rows*/ for(i=4;i<=n;i++) { npart[i]=2; for(j=2;j<=i-1;j++) { s=0; for(l=1; l<=MIN(i-j,j);l++) s+=getpart(i-j,l); npart[i]+=(part[partind(i,j)]=s); } } ntop=n; return(part[partind(n,k)]); } #undef getpart #undef partind #undef MIN ////////////////////////////////////////////////////////////////////////////////////// template YoungTableaux::YoungTableaux(const Partition &frame) : NRVec_from1 >() { #ifdef DEBUG if(!frame.is_valid()) laerror("invalid partition used as young frame"); #endif int nlines=frame.nparts(); this->resize(nlines); for(int i=1; i<=nlines; ++i) {(*this)[i].resize(frame[i]); (*this)[i].clear();} } template bool YoungTableaux::is_valid() const { int nrows=this->size(); for(int i=1; i<=nrows; ++i) { if((*this)[i].size()<=0) return false; if(i>1 && (*this)[i].size()> (*this)[i-1].size()) return false; } return true; } template T YoungTableaux::sum() const { #ifdef DEBUG if(!is_valid()) laerror("invalid young frame"); #endif int sum=0; int nrows=this->size(); for(int i=1; i<=nrows; ++i) sum += (*this)[i].size(); return sum; } template T YoungTableaux::max() const { #ifdef DEBUG if(!is_valid()) laerror("invalid young frame"); #endif int m= -1; int nrows=this->size(); for(int i=1; i<=nrows; ++i) for(int j=1; j<= (*this)[i].size(); ++j) if((*this)[i][j]>m) m=(*this)[i][j]; return m; } template bool YoungTableaux::is_standard() const { #ifdef DEBUG if(!is_valid()) laerror("invalid young frame"); #endif int nrows=this->size(); //check numbers monotonous in rows and columns for(int i=1; i<=nrows; ++i) { for(int j=1; j<=(*this)[i].size(); ++j) { if(j>1 && (*this)[i][j] < (*this)[i][j-1]) return false; if(i>1 && (*this)[i][j] < (*this)[i-1][j]) return false; } } return true; } template std::ostream & operator<<(std::ostream &s, const YoungTableaux &x) { int nrows=x.size(); for(int i=1; i<=nrows; ++i) { for(int j=1; j<=x[i].size(); ++j) { s.width(4); s< NRVec_from1 YoungTableaux::yamanouchi() const { #ifdef DEBUG if(!is_valid()) laerror("invalid young frame"); #endif int n=sum(); NRVec_from1 yama(n); int i,j; for (i=1;i<=this->size();i++) for (j=1;j<=(*this)[i].size();j++) yama[n-(*this)[i][j]+1]=i; return yama; } template T YoungTableaux::character_contribution(int ncyc) const { #ifdef DEBUG if(!is_valid()) laerror("invalid young frame"); if(!is_standard()) laerror("nonstandardly filled young frame"); #endif if(!ncyc) ncyc=max(); //number of types of applied points NRVec_from1 onxlines((T)0,ncyc); for(int i=1;i<=(*this).size();i++) //rows { NRVec_from1 wasfound((T)0,ncyc); for(int j=1;j<=(*this)[i].size();j++) //columns { T k = (*this)[i][j]; onxlines[k] += (1-wasfound[k]); wasfound[k]=1; } } /*now sum the number of odd applications for all cycles*/ T contrib=0; for(int i=1;i<=ncyc;i++) contrib += ((onxlines[i]&1)^1); return (1-2*(contrib&1)); //add it to the character +1 for even no. of odd apl., -1 for odd no. of odd apl. } static bool _callyoung; template static void (*_young_callback)(const YoungTableaux&); template static T _character; template static YoungTableaux *_tchi; template static T _nowapplying; template static T _ncycles; template static NRVec_from1 *_aplnumbers; template static NRVec_from1 *_oclin; template static NRVec_from1 *_occol; template static T _nn; template static inline T mymin(T i,T j) { return(i void placedot(T l, T c) { (*_tchi)[l][c]= _nowapplying; (*_oclin)[l]++; (*_occol)[c]++; } //forward declaration for recursion template extern void nextapply(T ncyc); template void regulapply(T ncyc, T napl) //ncyc is number of cycles; napl is the size of now processed cycle { /*the main idea is that only the first point of each set can be placed in several independent ways; the other points (if present) must be placed in a given fixed way depending on the first one; than it is also necessary to test if the application was successfull at all and return back or call nextapply, respectively*/ Partition usedcols(_nn),usedlines(_nn); //stores the positions where points were placed, is necessary for later cleanup /*try to place first point*/ for(usedlines[1]=mymin((T)_tchi->nrows(),(*_occol)[1]+napl); usedlines[1]>0; usedlines[1]--) { if((*_oclin)[usedlines[1]]<=(*_tchi)[usedlines[1]].size()) /*line is not fully occupied*/ { int i; usedcols[1]= (*_oclin)[usedlines[1]]; /*in the line there is only one possible column*/ /* place first point */ placedot(usedlines[1],usedcols[1]); for(i=2;i<=napl;i++) usedlines[i]=usedcols[i]= 0; /* flag for cleaning that they were not placed*/ /*now place other ones, if not possible go to irregular*/ for(i=2;i<=napl;i++) { T thisrow; thisrow=usedlines[i-1]; if (thisrow==1) { if((*_oclin)[1]> (*_tchi)[1].size()) goto irregular; /*no place remained*/ placedot((usedlines[i]=1),(usedcols[i]= (*_oclin)[1])); } else { T thiscol; thiscol=usedcols[i-1]; if(!(*_tchi)[thisrow-1][thiscol]) /*the box at the top of last placed is free*/ { placedot((usedlines[i]=thisrow-1),(usedcols[i]=thiscol)); } else if((*_oclin)[thisrow] <= (*_tchi)[thisrow].size()) /*the position at the right exists*/ { #ifdef DEBUG if((*_tchi)[thisrow][thiscol+1]) laerror("error in regulapply!!!"); #endif placedot((usedlines[i]=thisrow),(usedcols[i]=thiscol+1)); } else goto irregular; } } /*test if it is regular*/ for(i=1;i<=napl;i++) { /*test if the box left and up from actual position is full (provided it exists)*/ if(usedlines[i]>1) if(!(*_tchi)[usedlines[i]-1][usedcols[i]]) goto irregular; if(usedcols[i]>1) if(!(*_tchi)[usedlines[i]][usedcols[i]-1]) goto irregular; } nextapply(ncyc+1); irregular: /* clear all points */ for(i=napl;i>0;i--) if(usedlines[i]>0 && usedcols[i]>0) { (*_tchi)[usedlines[i]][usedcols[i]]=0; (*_oclin)[usedlines[i]]--; (*_occol)[usedcols[i]]--; } } /*end of loop for nonfull lines of first point*/ } } template void nextapply(T ncyc) { _nowapplying++; if(ncyc> _ncycles) { if(_callyoung) { if(_young_callback) _young_callback(*_tchi); _character ++; } else _character += _tchi->character_contribution(_ncycles); } else regulapply(ncyc,(*_aplnumbers)[ncyc]); _nowapplying--; } template T Sn_character(const Partition &irrep, const Partition &cclass) { #ifdef DEBUG if(!irrep.is_valid()||!cclass.is_valid()) laerror("invalid input to Sn_character"); #endif //prepare numbers to apply to the tableaux int n=irrep.sum(); if(cclass.sum()!=n) laerror("irrep and class partitions do not match"); T ncycles=cclass.nparts(); NRVec_from1 aplnumbers(ncycles); CompressedPartition ccclass(cclass); T k=0; for(int j=n;j>0;j--) for(T l=1;l<=ccclass[j];l++) aplnumbers[++k]=j; if(aplnumbers[k]==1) { /*rotate to the right*/ for(T l=k;l>1;l--) aplnumbers[l]=aplnumbers[l-1]; aplnumbers[1]=1; } //applying aplnumbers[i] pieces of "i" //std::cout<<"TEST aplnumbers "< y(irrep); //y.clear(); //already done in the constructor _callyoung=false; _ncycles =ncycles; _tchi = &y; _nn = n; _nowapplying =0; _aplnumbers = &aplnumbers; _character =0; Partition oclin(n); _oclin = &oclin; Partition occol(n); _occol = &occol; for(int i=1; i<=n; ++i) (*_oclin)[i]= (*_occol)[i]= 1; nextapply(1); return _character; } template PERM_RANK_TYPE YoungTableaux::generate_all_standard(void (*callback)(const YoungTableaux&)) { #ifdef DEBUG if(!this->is_valid()) laerror("invalid young frame"); #endif //prepare numbers to apply to the tableaux T n=this->sum(); T ncycles=n; NRVec_from1 aplnumbers(n); for(T i=1; i<=n; ++i) aplnumbers[i]=1; //prepare static variables for the recursive procedure and generate all regular applications this->clear(); _callyoung=true; _young_callback =callback; _ncycles =ncycles; _tchi = this; _nn = n; _nowapplying =0; _aplnumbers = &aplnumbers; _character =0; Partition oclin(n); _oclin = &oclin; Partition occol(n); _occol = &occol; for(int i=1; i<=n; ++i) (*_oclin)[i]= (*_occol)[i]= 1; nextapply(1); return _character; } ////generation of the young operator template static NRPerm _aperm; template static NRPerm _sperm; template static const YoungTableaux *_tyou; template static const Partition *_tyou_cols; template static const Partition *_tyou_rows; static PERM_RANK_TYPE _nyoungterms, _expectterms; template static T _antparity; template PermutationAlgebra *_young_r; template static void symetr(T ilin, T iel) { if(ilin > (*_tyou_cols)[1]) { (*_young_r)[_nyoungterms].weight = _antparity; (*_young_r)[_nyoungterms].perm = _aperm*_sperm; ++_nyoungterms; } else if(iel > (*_tyou_rows)[ilin]) symetr(ilin+1,(T)1); else { int i; for(i=1;i<=(*_tyou_rows)[ilin];i++) if(!_sperm[(*_tyou)[ilin][i]]) { _sperm[(*_tyou)[ilin][i]]= (*_tyou)[ilin][iel]; symetr(ilin,iel+1); _sperm[(*_tyou)[ilin][i]]=0; } } } template static void antisym(T icol,T iel) { if(icol > (*_tyou_rows)[1]) { _antparity = _aperm.parity(); symetr(1,1); } else if(iel > (*_tyou_cols)[icol]) antisym(icol+1,(T)1); else { int i; for(i=1;i<=(*_tyou_cols)[icol];i++) if(!_aperm[(*_tyou)[i][icol]]) { _aperm[(*_tyou)[i][icol]]= (*_tyou)[iel][icol]; antisym(icol,iel+1); _aperm[(*_tyou)[i][icol]]=0; } } } template PermutationAlgebra YoungTableaux::young_operator() const { #ifdef DEBUG if(!this->is_standard()) laerror("young_operator called for non-standard tableaux"); #endif _nyoungterms =0; _tyou = this; Partition rows=Partition(*this); Partition cols=rows.adjoint(); _tyou_rows = &rows; _tyou_cols = &cols; T n=rows.sum(); _aperm.resize(n); _aperm.clear(); _sperm.resize(n); _sperm.clear(); _expectterms=1; for(int i=1;i<=cols[1];i++) _expectterms *= factorial(rows[i]); for(int i=1;i<=rows[1];i++) _expectterms *= factorial(cols[i]); PermutationAlgebra r(_expectterms); _young_r = &r; antisym(1,1); if(_nyoungterms!=_expectterms) laerror("youngconstruct: inconsistent number of terms"); return r; } template static NRVec_from1 > *_irreps; template static NRVec_from1 > *_classes; template static PERM_RANK_TYPE _ithrough; template static void _process_partition(const Partition &p) { ++_ithrough; (*_irreps)[_ithrough] = CompressedPartition(p); Partition q=p.adjoint(); (*_classes)[_ithrough] = CompressedPartition(q); } template Sn_characters::Sn_characters(const int n0) :n(n0) { Partition p(n); PERM_RANK_TYPE np = partitions(n); irreps.resize(np); classes.resize(np); classsizes.resize(np); chi.resize(np,np); _irreps = &irreps; _classes = &classes; _ithrough = 0; //generate partitions for classes and irreps PERM_RANK_TYPE tot=p.generate_all(_process_partition); //compute class sizes for(int i=1; i<=np; ++i) classsizes[i]= classes[i].Sn_class_size(); //compute characters for(int i=1; i<=np; ++i) { for(int j=1; j<=np; ++j) { chi(i,j) = Sn_character(irreps[i],classes[j]); } } } template bool Sn_characters::is_valid() const { //consistency of n and partition number PERM_RANK_TYPE np = partitions(n); if(np!=classes.size() || np!=irreps.size() || np!=classsizes.size() ||np!=chi.nrows() ||np!=chi.ncols()) return false; //consistency of sum = n in all partitions for(int i=1; i<=np; ++i) { if(irreps[i].sum()!=n) return false; if(classes[i].sum()!=n) return false; } //consistency of irrep dim squared to n! //consistency of irrep dimensions by hook length formula to character of identity (asserted as class no. 1) if(classes[1][1]!=n) laerror("assetion failed on class no. 1 being identity"); PERM_RANK_TYPE nf = factorial(n); PERM_RANK_TYPE s=0; for(int i=1; i<=np; ++i) { T d=chi(i,1); s += d*d; T dd=Partition(irreps[i]).Sn_irrep_dim(); if(d!=dd) return false; } if(s!=nf) return false; //consistency of class sizes sum to n! s=0; for(int i=1; i<=np; ++i) s+= classsizes[i]; if(s!=nf) return false; //check that irrep [n] is totally symmetric if(irreps[1][n]!=1) laerror("assetion failed on first irrep being [n]"); for(int i=1; i<=np; ++i) if(chi(1,i)!=1) return false; //check that irrep[1^n] is totally antisymmetric if(irreps[np][1]!=n) laerror("assetion failed on last irrep being [1^n]"); for(int i=1; i<=np; ++i) if(chi(np,i)!=classes[i].parity()) return false; //check character orthonormality between irreps for(int i=1; i<=np; ++i) for(int j=1; j<=i; ++j) { s=0; for(int k=1; k<=np; ++k) s+= classsizes[k]*chi(i,k)*chi(j,k); if(i==j) { if(s!=nf) return false; } else { if(s!=0) return false; } } return true; } template std::ostream & operator<<(std::ostream &s, const Sn_characters &c) { const int w=4+c.n; //some reasonable width s<<"S"< bool CycleIndex::is_valid() const { if(classes.size()!=classsizes.size()) return false; T n=classes[1].sum(); for(int i=2; i<=classes.size(); ++i) { if(classes[i].sum()!=n) return false; } return true; } template Polynomial CycleIndex::substitute(const Polynomial &p, PERM_RANK_TYPE *denom) const { Polynomial r(0); r[0]=(T)0; *denom =0; for(int i=1; i<=classes.size(); ++i) { Polynomial term(0); term[0]=(T)1; for(int j=1; j<=classes[i].size(); ++j) if(classes[i][j]) term *= (p.powx(j)).pow((int)classes[i][j]); r += term*classsizes[i]; *denom += classsizes[i]; } return r; } template NRMat Multable(T n) { NRPerm p(n); PermutationAlgebra all = p.list_all_lex(); NRMat r(all.size(),all.size()); for(PERM_RANK_TYPE i=0; i tmp = all[i].perm * all[j].perm; r(i,j) = tmp.rank(); } //consistency checks #ifdef DEBUG bitvector occ(all.size()); for(PERM_RANK_TYPE i=0; i NRMat RegularRepresentation(const PermutationAlgebra &a, const NRMat &mtable) { NRMat r(mtable.nrows(),mtable.ncols()); r.clear(); for(int i=0; i PermutationAlgebra general_antisymmetrizer(const NRVec > &groups, int restriction_type, bool inverted) { PermutationAlgebra r; int ngroups=groups.size(); if(ngroups==0) return r; NRVec > lists(ngroups); for(int i=0; i tmp(ni); lists[i] = tmp.list_restricted(groups[i],restriction_type,inverted); } //cross-product the lists r=lists[0]; for(int i=1; i; \ template class CyclePerm; \ template class CompressedPartition; \ template class Partition; \ template class YoungTableaux; \ template class Sn_characters; \ template class CycleIndex; \ template NRMat Multable(T n); \ template PermutationAlgebra general_antisymmetrizer(const NRVec > &groups, int, bool); \ template std::istream & operator>>(std::istream &s, CyclePerm &x); \ template std::ostream & operator<<(std::ostream &s, const CyclePerm &x); \ template std::ostream & operator<<(std::ostream &s, const CompressedPartition &x); \ template std::ostream & operator<<(std::ostream &s, const YoungTableaux &x); \ template T Sn_character(const Partition &irrep, const Partition &cclass); \ template std::ostream & operator<<(std::ostream &s, const Sn_characters &x); \ #define INSTANTIZE2(T,R) \ template class WeightPermutation; \ template class PermutationAlgebra; \ template std::istream & operator>>(std::istream &s, WeightPermutation &x); \ template std::ostream & operator<<(std::ostream &s, const WeightPermutation &x); \ template NRMat RegularRepresentation(const PermutationAlgebra &a, const NRMat &mtable); \ INSTANTIZE(int) INSTANTIZE(unsigned int) INSTANTIZE2(int,int) INSTANTIZE2(int,double) }//namespace