#!/usr/bin/perl #Kazhdan-Lusztig matrix #kl -h for a help file #jda@math.umd.edu use strict; use Data::Dumper; use Getopt::Long; use vars qw($help $outputFile $w $quiet $standard $irreducible $q $transpose $format $inverse $parameter $blockFile $file $signed); my %options=('h' => \$help, 'o' => \$outputFile, 'q' => \$q, 't' => \$transpose, 'i' => \$inverse, 's' => \$signed, 'f' => \$format, 'b' => \$blockFile, 'p' => \$parameter, 'quiet' => \$quiet); GetOptions(\%options,qw(h! o=s p=s w=s q! s! i! quiet! t! f=s d=s i! p=s b=s)); if ($outputFile){ $quiet or print "Sending output to file $outputFile\n"; open(OUT,">$outputFile")||die("Can't open $outputFile for output"); select(OUT); } my $pml_tolerance = 1e-12; $file=$ARGV[0]; $help and &help; unless ($file){ print "You must specify an input file (output of klbasis). See -h for help\n"; exit; } my ($P,$Lengths)=process($file); #my $lengths=getLengths($blockFile) if ($blockFile); my $Q; output($P); $outputFile and close(OUT); sub getLengths{ my $file=shift; my %lengths; open(IN,"<$file")||die("Can't open $file for input\n"); foreach my $line (){ my ($parameter,$length)=($line =~ /\s*([0-9]*).*\]\s*([0-9*]).*/); $lengths{$parameter}=$length; } return \%lengths; } sub process{ my $file=shift; open(IN,"<$file")||die("Can't open $file for input"); my $n=0; foreach my $line (){ chomp($line); $line =~ s/\s//g; my @line=split ':', $line; next unless (scalar(@line)==3); $n = $line[0] if ($n<$line[0]); } close(IN); my @P; my %Lengths; foreach my $j (0..$n){ push @P, [(0)x($n+1)]; } open(IN,"<$file")||die("Can't open $file for input"); my $i=0; foreach my $line (){ chomp($line); $line =~ s/\#.*//; $line =~ s/\s//g; next unless $line; next unless $line =~ /[0-9]+:[0-9q]+/; my @line=split ':', $line; if (scalar(@line) == 2){ unshift @line,$i; }else{ $i=$line[0]; $Lengths{$i}=0; } if ($line[1]<$line[0]){ if ($Lengths{$line[1]}+1 > $Lengths{$line[0]}){ $Lengths{$line[0]} = $Lengths{$line[1]}+1; } } #print Dumper(\%Lengths); # print Dumper($lengths); my $value=$line[2]; unless ($q){ $value =~ s/([0-9]+)q/\1*q/g; $value =~ s/q/1/g; $value =~ s/\^/**/g; $value=eval($value); } $P[$line[0]]->[$line[1]]=$value; } close(IN); return (\@P,\%Lengths); } sub output{ my $P=shift; if (!$quiet){ print "(*Kazhdan-Lusztig matrix from file $file:*)\n"; if ($q){ print "(*Including q parameter*)\n"; }else{ print "(*Setting q=1*)\n"; } my $size=matrixDim($P); print "(*Size of matrix: $size*)\n"; print "(*This matrix gives the multiplicity of standard modules in irreducibles, up to sign*)\n"; $transpose and print "(*Displaying the transpose matrix*)\n"; } print "klmatrix = \n"; $transpose?print matrixString(transpose($P)):print matrixString($P); print "\n"; if (!$q and ($parameter or ($parameter eq '0'))){ print "(*Irreducible $parameter as sum of standards up to signs:*)\n $parameter= "; displayRow($P,$parameter); print "\n"; } print "\n"; my $Psigned; if ($signed or $inverse){ my @Psigned; foreach my $i (0..$#{@$P}){ my $row=$P->[$i]; my @row=@$row; foreach my $j (0..$#row){ my $sign=(-1)**($Lengths->{$i}-$Lengths->{$j}); $row[$j]=$row[$j]*$sign; } push @Psigned, \@row; } $Psigned=\@Psigned; } if ($signed){ if ($q){ $quiet or print "(* Not printing signed matrix when -q is specificed. *)\n"; }else{ $quiet or print "(* Signed KL matrix: *)\n"; $quiet or print "(* This matrix gives the multiplicities of standards in irreducibles *)\n"; print "klmatrixsigned =\n"; $transpose?print matrixString(transpose($Psigned)):print matrixString($Psigned); print ";\n"; if (!$q and ($parameter or ($parameter eq '0'))){ print "(* Irreducible $parameter as sum of standards:*)\n$parameter= "; displayRow($Psigned,$parameter); print "\n"; } print "\n"; } } if ($inverse){ my @save; foreach my $row (@$P){ my @row=@$row; push @save, \@row; } my $Pinverse=matinv($Psigned,$Psigned,matrixDim($Psigned),0); $P=\@save; if ($q){ $quiet or print "(* Not printing inverse matrix when -q is specified *)\n"; }else{ print "klmatrixinverse =\n"; $transpose?print matrixString(transpose($Pinverse),1):print matrixString($Pinverse,1); print ";\n"; if ($parameter or ($parameter eq '0')){ print "(* Standard $parameter as sum of irreducibles*):\n$parameter= "; displayRow($Pinverse,$parameter); print "\n"; } print "\n"; } } # print "test:\n", matrixString(matrixMult($P,$Pinverse)); } sub displayRow{ my $P=shift; my $r=shift; my $n=matrixDim($P); my @rv; print column; foreach my $i (0..$n-1){ my $coeff=roundToInteger($P->[$r]->[$i]); next unless $coeff; if ($coeff == 1){ $coeff=''; }elsif ($coeff == -1){ $coeff = '-'; }else{ $coeff = "$coeff".'x'; } print "$coeff".$i." "; } } sub help{ print " Compute the Kazhdan-Lusztig matrix for a given block. The output is an n by n matrix M; the entry in row i and column j is the Kazhdan-Lusztig polynomial P_{i,j}. Here i,j run over integers 0 to n, parametrizing the block, as given by the block command in atlas. Row i of this matrix is the multiplicity of standard modules 0..n in (the formal character of) the irreducible representation i, up to sign. With the option -s will display the signed matrix. This script reads a file produced by the klbasis command of atlas as input. By default q is set to 1, keep q as an indeterminate with the -q option. With q=1 this is the matrix of multiplicities of irreducible modules in standards. The -i option (without -q) will give the inverse matrix, which gives the multiplicities of irreducibles in standards. Use -t for the transpose. WARNING: The computation of inverses is very slow and recommended only for very small rank. For larger matrices computing the inverse in another program is better. With the -p option, give information about parameter p. This is the i^th row of the matrix (or column if -t), and is included for legibility. For example -p 5 gives the irreducible module 5 as a sum of standards (up to sign). With the -i option, also gives irreducible module 5 as a sum of standards. Not allowed with -q. Output is in mathematica format by default. With -o and not -v, the resulting file can be read directly into mathematica. For maple use the -f flag. Usage: kl [-t] [-q] [-i] [-s] [-o outputfile] [-p parameter] [-v] [-f maple|mathematica] file [-h] The file argument should contain the output of the atlas command klbasis. Options: -q matrix of polynomials in q (default: set q=1) -i output inverse matrix also (not allowed with -q) -s signed Kazhdan-Lusztig matrix -p parameter information about parameter p (p^th column of matrix) -o outputfile send output to file instead of terminal -f maple maple format (default: mathematica) -t output the tranpose matrix -q quiet -h this help file Examples: kl klbasisC2 Kazhdan-Lusztig matrix of integers for type C2 kl -v klbasisC2 more output kl -q klBasisC2 give Kazhdan-Lusztig matrix of polynomials in q for type C2 kl -t klbasisC2 transpose Kazhdan-Lusztig matrix kl -s klbasisC2 also give the signed matrix kl -i klbasisC2 also give the inverse matrix kl -o kloutput klBasisC2 send output to file kloutput kl -f maple klbasisC2 matrices in maple format kl -p 5 klbasisC2 also give irreducible module 5 as a sum of standards (up to signs) kl -p 5 -s klbasisC2 also give irreducible module 5 as a sum of standards with correct signs kl -p 5 -i klbasisC2 also give standard module 5 as a sum of irreducibles Sample output: Note: For the signs we need to compute (-1)^(l(x)-l(y)). This is available from the block command. According to an educated guess by David Vogan we can also get it from the output of klbasis. That is what we do here. See the computation of the Lengths hash. Send questions and bugs to jda\@math.umd.edu "; exit; } sub transpose{ my $matrix=shift; my $rows=scalar(@$matrix); my $columns=scalar(@{$matrix->[0]}); my @rv; foreach my $i (0..$columns-1){ my @row; foreach my $j (0..$rows-1){ $row[$j]=$matrix->[$j][$i]; } push @rv, \@row; } return \@rv; } sub matrixString{ my $string; my $m=shift; my $round=shift; my @matrix=@$m; my @output; foreach my $row (@matrix){ my @row=@$row; if ($round){ foreach my $i (0..$#row){ $row->[$i]=roundToInteger($row->[$i]); } } my $line= join ',', @$row; push @output,$line; } my $open='{'; my $close='}'; if ($format =~ /map/){ $open='['; $close=']'; } $string.="$open$open"; $string.=join "$close,\n $open", @output; $string.="$close$close"; return $string; } sub matrixMult { my ($A,$B) = @_; my ($Arows,$Acols) = matrixDim($A); my ($Brows,$Bcols) = matrixDim($B); unless ($Acols == $Brows){ print "ERROR"; die "Error: size of matrices don't match: $Acols != $Brows"; } my $result = []; my ($i, $j, $k); for $i (range($Arows)) { for $j (range($Bcols)) { for $k (range($Acols)) { $result->[$i][$j] = roundToInteger($result->[$i][$j]+$A->[$i][$k] * $B->[$k][$j]); } } } return $result; } sub roundToInteger{ my $a=shift; return int($a+.5*($a<=>0)); } sub range { 0 .. ($_[0] - 1) } sub matrixDim { my $matrix = $_[0]; my $rows = scalar(@$matrix); my $cols = scalar(@{$matrix->[0]}); return ($rows, $cols); } sub matinv { my ($aref, $bref, $n, $quiet) = @_; my ($row_start, $i); my $twon = $n + $n; my @ai; die "matinv(): Need as arguments two matrix references and dimensions.\n" unless defined $n; # First, paste the input and the identity side by side. $row_start = 0; for (my $i = 0; $i < $n; $i++) { for (my $j = 0; $j < $n; $j++) { $ai[$i][$j] = $$aref[$i][$j]; } } for (my $i = 0; $i < $n; $i++) { for (my $j = 0; $j < $n; $j++) { $ai[$i][$j+$n] = 0.0; } } for (my $i = 0; $i < $n; $i++) { $ai[$i][$i+$n] = 1.0; } # Second, use Householder transformations to put it into upper-triangular # form. for (my $i = 0; $i < $n; $i++) { if (!$quiet) { my @Q = householder_on_submatrix_Q(\@ai, $n, $twon, $i); # print "-- matinv Q $i:\n"; # print_matrix(\@Q, $n, $n); # print Dumper(\@Q); # print "\n"; # print "-- matinv aug $i:\n"; # print_matrix(\@ai, $n, $twon); # print Dumper(\@ai); # print "\n"; } householder_on_submatrix(\@ai, $n, $twon, $i); } # Third, put 1 on the left diagonal. for (my $i = 0; $i < $n; $i++) { my $d = $ai[$i][$i]; if ($d == 0.0) { die "Singular.\n"; } # elsif (tol_zero($d)) { # die "Nearly singular.\n"; # } for (my $j = 0; $j < $twon; $j++) { $ai[$i][$j] = $ai[$i][$j] / $d; } } # Fourth, clear out the rest of the left-hand side. # 1 . . . . . . . . . # 0 1 . . . . . . . . # 0 0 1 . . . . . . . # 0 0 0 1 . . . . . . <-- i # 0 0 0 0 1 . . . . . <-- i2 for (my $i = $n-2; $i >= 0; $i--) { for (my $i2 = $n-1; $i2 > $i; $i2--) { my $mul = $ai[$i][$i2]; for (my $j = 0; $j < $twon; $j++) { $ai[$i][$j] -= $ai[$i2][$j] * $mul; } } } # Fifth, obtain the inverse from the right-hand side. for (my $i = 0; $i < $n; $i++) { for (my $j = 0; $j < $n; $j++) { $$bref[$i][$j] = $ai[$i][$j+$n]; } } return $bref; } sub householder_on_submatrix { my ($aref, $nr, $nc, $a) = @_; my $height = $nr - $a; my ($i, $j); die "householder_on_submatrix(): Need as arguments matrix reference, dimensions, and start column.\n" unless defined $a; # --- First column --- # Get v my @v = get_submatrix_column($aref, $nr, $nc, $a, $a); # Compute ||v||; write w. my $normv = sqrt(dot(\@v, \@v, $height)); if ($v[0] >= 0.0) { $normv = -$normv; } my @w; $w[0] = $normv; for ($i = 1; $i < $height; $i++) { $w[$i] = 0.0; } # Compute z = v - w. my @z; for ($i = 0; $i < $height; $i++) { $z[$i] = $v[$i] - $w[$i]; } put_submatrix_column($aref, $nr, $nc, $a, $a, \@w); # Compute 2 * (z dot z) my $zdotz = dot(\@z, \@z, $height); my $tnz2; if (abs($zdotz) < $pml_tolerance) { $tnz2 = 0.0; } else { $tnz2 = 2.0 / $zdotz; } # --- Subsequent columns --- my $b; for ($b = $a + 1; $b < $nc; $b++) { # Get column c. my @c = get_submatrix_column($aref, $nr, $nc, $b, $a); # Compute the scalar z^t c my $ztc = dot(\@z, \@c, $height); # Compute d = (-2 / z dot z) * z^t c my $d = -$tnz2 * $ztc; # New column is q = c + d * z. for ($j = 0; $j < $height; $j++) { $w[$j] = $c[$j] + $d * $z[$j]; } put_submatrix_column($aref, $nr, $nc, $b, $a, \@w); } } sub householder_on_submatrix_Q { my ($aref, $nr, $nc, $a) = @_; my $height = $nr - $a; my ($i, $j); die "householder_on_submatrix_Q(): Need as arguments matrix reference, dimensions, and start column.\n" unless defined $a; # --- First column --- # Get v my @v = get_submatrix_column($aref, $nr, $nc, $a, $a); # Compute ||v||; write w. my $normv = sqrt(dot(\@v, \@v, $height)); if ($v[0] >= 0.0) { $normv = -$normv; } my @w; $w[0] = $normv; for ($i = 1; $i < $height; $i++) { $w[$i] = 0.0; } # Compute z = v - w. my @z; for ($i = 0; $i < $height; $i++) { $z[$i] = $v[$i] - $w[$i]; } # Compute 2 / z dot z my $zdotz = dot(\@z, \@z, $height); my $tnz2; if (abs($zdotz) < $pml_tolerance) { $tnz2 = 0.0; } else { $tnz2 = 2.0 / $zdotz; } # Q = I - (2 z z^t / z^t z) my @Q; for ($i = 0; $i < $nr; $i++) { for ($j = 0; $j < $nc; $j++) { $Q[$i][$j] = 0.0; } } for ($i = 0; $i < $nr; $i++) { $Q[$i][$i] = 1.0; } my @zzt = outer(\@z, \@z, $height, $height); for ($i = $a; $i < $nr; $i++) { for ($j = $a; $j < $nr; $j++) { $Q[$i][$j] -= $zzt[$i-$a][$j-$a] * $tnz2; } } return @Q; } sub householder_on_submatrix_Q { my ($aref, $nr, $nc, $a) = @_; my $height = $nr - $a; my ($i, $j); die "householder_on_submatrix_Q(): Need as arguments matrix reference, dimensions, and start column.\n" unless defined $a; # --- First column --- # Get v my @v = get_submatrix_column($aref, $nr, $nc, $a, $a); # Compute ||v||; write w. my $normv = sqrt(dot(\@v, \@v, $height)); if ($v[0] >= 0.0) { $normv = -$normv; } my @w; $w[0] = $normv; for ($i = 1; $i < $height; $i++) { $w[$i] = 0.0; } # Compute z = v - w. my @z; for ($i = 0; $i < $height; $i++) { $z[$i] = $v[$i] - $w[$i]; } # Compute 2 / z dot z my $zdotz = dot(\@z, \@z, $height); my $tnz2; if (abs($zdotz) < $pml_tolerance) { $tnz2 = 0.0; } else { $tnz2 = 2.0 / $zdotz; } # Q = I - (2 z z^t / z^t z) my @Q; for ($i = 0; $i < $nr; $i++) { for ($j = 0; $j < $nc; $j++) { $Q[$i][$j] = 0.0; } } for ($i = 0; $i < $nr; $i++) { $Q[$i][$i] = 1.0; } my @zzt = outer(\@z, \@z, $height, $height); for ($i = $a; $i < $nr; $i++) { for ($j = $a; $j < $nr; $j++) { $Q[$i][$j] -= $zzt[$i-$a][$j-$a] * $tnz2; } } foreach my $i (0..$a){ foreach my $j (0..$a){ # print "fix $i,$j"; if (abs($Q[$i][$j])<$pml_tolerance){ # print "FIXED $Q[$i][$j]\n";; # $Q[$i][$j]=0 } } } return @Q; } sub get_submatrix_column { my ($aref, $nr, $nc, $colidx, $start_row) = @_; my ($src, $dst); my @submatrix_column; for ($src = $start_row, $dst = 0; $src < $nr; $src++, $dst++) { $submatrix_column[$dst] = $$aref[$src][$colidx]; } return @submatrix_column; } sub dot { my ($uref, $vref, $n) = @_; die "dot(): Need as arguments two vector references and length.\n" unless defined $n; my $sum = 0.0; my $i; for ($i = 0; $i < $n; $i++) { $sum += $$uref[$i] * $$vref[$i]; } return $sum; } sub outer { my ($uref, $vref, $m, $n) = @_; die "outer(): Need as arguments two vector references and lengths.\n" unless defined $n; my ($i, $j, @uv); for ($i = 0; $i < $m; $i++) { for ($j = 0; $j < $n; $j++) { $uv[$i][$j] = $$uref[$i] * $$vref[$j]; } } return @uv; } sub put_submatrix_column { my ($aref, $nr, $nc, $colidx, $start_row, $pcolref) = @_; my ($src, $dst); for ($src = 0, $dst = $start_row; $dst < $nr; $src++, $dst++) { $$aref[$dst][$colidx] = $$pcolref[$src]; } }