> < ^ Date: Fri, 03 Mar 2000 15:26:45 +0100 (CET)
> < ^ From: Frank Luebeck <frank.luebeck@math.rwth-aachen.de >
< ^ Subject: Re: Computing Structure Constants

Dear Forum, dear Simon,

norton@math.binghamton.edu said:
> I have written a GAP program to compute structure constants in
> symmetric groups without computing the whole table. I don't know how
> inefficient I am being, but it took over an hour to show that in S34
> (2^17,3^11.1,6^3.5.4^2.3) was 271/72, this being the simplest test I
> could work out for a rather far-out conjecture I am investigating
> (which this value confirmed).

Can I reasonably expect to go significantly further, and if so how far?

I'm including below some GAP code which you may find useful. After
reading this into GAP the example above can be computed by:

p0 := 0*[1..34]+1;
p1 := 0*[1..17]+2;
p2 := Concatenation( 0*[1..11]+3, [1]);
p3 := [6,6,6,5,4,4,3];

v0 := CharacterValueColumnSymmetric(p0);;time;
v1 := CharacterValueColumnSymmetric(p1);;time;
v2 := CharacterValueColumnSymmetric(p2);;time;
v3 := CharacterValueColumnSymmetric(p3);;time;

strconst := CentralizerOrderPartition(p0) / 
            (CentralizerOrderPartition(p1) *
             CentralizerOrderPartition(p2) *
             CentralizerOrderPartition(p3) ) *
            Sum(List([1..NrPartitions(34)], i-> v1[i]*v2[i]*v3[i]/v0[i]));

This takes on my notebook about 25 seconds. So, it should be possible
to use the programs for some larger examples as well.

Background: The code must be very similar to the code for character
tables of symmetric groups in the GAP library (by Goetz Pfeiffer). The
idea is to use the Murnaghan-Nakayama recursion formula and to read it
as a reduction of the computation of a column of the table of S_n to
the computation of *one* column for some S_m with m<n. We save a lot
of work in the recursion by storing and reusing some intermediate
values.

For more details see the very nice description by Goetz Pfeiffer,
contained in the GAP-3.4.4 distribution in etc/ctweyl.dvi.

With best regards,

Frank Lübeck

------------------  snip  here  ----------------------------------
##############################################################################
## 
##                       cvalsym.g
##  
##  Frank Luebeck          3/2000    Lehrstuhl D fuer Mathematik, RWTH Aachen
##  
##  The main function in this file is:
##  
##      CharacterValueColumnSymmetric(p);
##  
##  Here p is  a partition of some natural  number n. The function returns
##  the list of  character values of the  symmetric group  S_n on elements
##  with  cycle type p.  The ordering  of the  characters (also labeled by
##  partitions of n) is given by the ordering of `Partitions(n);'.
##  
##  Note that the code here is very similar to Goetz Pfeiffers code in the
##  GAP library.  It has the main advantage  that  single columns of large
##  tables can be computed separately.
##    
##  Due to the global caching  the programs here  are also pretty fast for
##  computing all complete character tables in a range [1..n] for some n.
##  

# We cache partitions and their duals
PARTITIONS := [];
DUALPARTITIONS := [];

Partitions1 := function(n)
if not IsBound(PARTITIONS[n]) then
PARTITIONS[n] := Immutable(Partitions(n));
IsSet(PARTITIONS[n]);
fi;
return PARTITIONS[n];
end;

DualPartitions1 := function(n)
if not IsBound(DUALPARTITIONS[n]) then
DUALPARTITIONS[n] := Immutable(List(Partitions1(n), AssociatedPartition));
fi;
return DUALPARTITIONS[n];
end;

# This returns for a partition p of n, its position in `Partitions(n)'
# Arguments are: p[, n]
NrPartition := function(arg)
  local   p,  n;
  p := arg[1];
  if Length(p)=0 then
    return 1;
  fi;
  if Length(arg)>1 then
    n := arg[2];
  else
    n := Sum(p);
  fi;
  return Position(Partitions1(n), p);
end;

# This computes the entry [n, pnr, k] of the result of the GAP command
# `InductionScheme(n)'
# (Here we use pairs of partition and its dual, instead of beta-sets.)
REMOVEHOOKCACHE := [];
RemoveHookInfo := function(n, pnr, k)
local rr, p, pt, km1, res, i, j, pr;

# remove rim hook
rr := function(p, pt, i, j)
  local   prr,  k;
  prr := p{[1..i-1]};
  for k in [i..pt[j]-1] do 
    Add(prr, p[k+1]-1);
  od;
  Add(prr,j-1);
  Append(prr, p{[pt[j]+1..Length(p)]});

  # remove trailing zero's (in case j=1)
  if j=1 then
    k := Length(prr);
    while k>0 and prr[k]=0 do
      Unbind(prr[k]);
      k := k-1;
    od;
  fi;
  return prr;
end;

# caching
if not IsBound(REMOVEHOOKCACHE[n]) then
REMOVEHOOKCACHE[n] := [];
fi;
if not IsBound(REMOVEHOOKCACHE[n][pnr]) then
REMOVEHOOKCACHE[n][pnr] := [];
fi;
if IsBound(REMOVEHOOKCACHE[n][pnr][k]) then
return REMOVEHOOKCACHE[n][pnr][k];
fi;

p := Partitions1(n)[pnr];
pt := DualPartitions1(n)[pnr];
km1 := k-1;

# find k-hooks
res := [];
for i in [1..Length(p)] do
  j := 1;
  while j<=p[i] and p[i]-j+pt[j]-i > km1 do
    j := j+1;
  od;
  if j<=p[i] and p[i]-j+pt[j]-i = km1 then
    pr := NrPartition(rr(p,pt,i,j), n-k);
    Add(res,  (-1)^(pt[j]-i) * pr);
  fi;
od;
  # store in cache
  REMOVEHOOKCACHE[n][pnr][k] := res;
  return res;
end;

# Here we cache columns of character tables of symmetric groups,
# in position [n][i] is the column of the table of S_n and class
# given by `Partitions(n)[i]'.
CSYMVALCOLS := [[[1]]];

CharValSymCol := function(n, cl)
  local   clp,  k,  cln,  coln,  rem,  res,  a,  val,  b;
  if n=0 then
    return [1];
  fi;
  if not IsBound(CSYMVALCOLS[n]) then
    CSYMVALCOLS[n] := [];
  fi;
  if IsBound(CSYMVALCOLS[n][cl]) then
    return CSYMVALCOLS[n][cl];
  fi;
  
  clp := Partitions1(n)[cl];
  k := clp[1];
  cln := NrPartition(clp{[2..Length(clp)]}, n-k);
  coln := CharValSymCol(n-k, cln);
  rem := List([1..Length(Partitions1(n))], i-> RemoveHookInfo(n, i, k));
  res := [];
  for a in rem do
    val := 0;
    for b in a do
      if b>=0 then
        val := val + coln[b];
      else
        val := val - coln[-b];
      fi;
    od;
    Add(res, val);
  od;
  CSYMVALCOLS[n][cl] := res;
  return res;
end;

# The user function, as explained above. For efficiency we compute the
# character degrees in a separate loop (as in the GAP library).
CharacterValueColumnSymmetric := function(pp)
local n, res, ps, pst, i, p, pt, prd, j;
n := Sum(pp);

# case of character degrees
if pp[1]=1 then
  res := [];
  ps := Partitions1(n);
  pst := DualPartitions1(n);
  for i in [1..Length(ps)] do
    p := ps[i];
    pt := pst[i];
    prd := 1;     # product over hook lengths
    for i in [1..Length(p)] do
      for j in [1..p[i]] do
        prd := prd * (p[i]-j+pt[j]-i+1);
      od;
    od;
    Add(res, Factorial(n) / prd);
  od;
  if not IsBound(CSYMVALCOLS[n]) then
    CSYMVALCOLS[n] := [];
  fi;
  CSYMVALCOLS[n][1] := res;
  return res;
fi;

return CharValSymCol(n, NrPartition(pp, n));
end;

# We need this for the structure constants.
CentralizerOrderPartition := function(p)
return CharTableSymmetric.centralizers[1](Sum(p), p);
end;

##  Example: 
##    
##  pp := Partitions(34);;
##  p0 := 0*[1..34]+1;
##  p1 := 0*[1..17]+2;
##  p2 := Concatenation( 0*[1..11]+3, [1]);
##  p3 := [6,6,6,5,4,4,3];
##  
##  v0 := CharacterValueColumnSymmetric(p0);;time;
##  v1 := CharacterValueColumnSymmetric(p1);;time;
##  v2 := CharacterValueColumnSymmetric(p2);;time;
##  v3 := CharacterValueColumnSymmetric(p3);;time;
##  
##  strconst := CentralizerOrderPartition(p0) / 
##              (CentralizerOrderPartition(p1) *
##               CentralizerOrderPartition(p2) *
##               CentralizerOrderPartition(p3) ) *
##              Sum(List([1..NrPartitions(34)], i-> v1[i]*v2[i]*v3[i]/v0[i]));

> < [top]