Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  search.gi   Sprache: unbekannt

 
#############################################################################
##
##                             orb package
##  search.gi
##                                                          Juergen Mueller
##                                                          Max Neunhoeffer
##                                                             Felix Noeske
##
##  Copyright 2005-2008 by the authors.
##  This file is free software, see license information at the end.
##
##  Implementation stuff for searching in groups.
##
#############################################################################

# Random vectors and matrices:

# Methods for Randomize for gf2 and 8bit vectors have been taken out
# they will be in GAP >= 4.5 in the library. In the meantime use
# cmats.

InstallGlobalFunction( MakeRandomVectors,
  function( arg )
    local i,l,number,randomsource,sample;
    
    if Length(arg) <= 1 or Length(arg) > 3 then
        ErrorNoReturn("Usage: MakeRandomVectors( sample, number [,randomsource] )");
    fi;
    sample := arg[1];
    number := arg[2];
    if Length(arg) = 3 then
        randomsource := arg[3];
    else
        randomsource := GlobalRandomSource;
    fi;
    
    l := [];
    for i in [number,number-1..1] do
        sample := ShallowCopy(sample);
        Randomize(sample,randomsource);
        l[i] := sample;
    od;
    return l;
  end );

InstallGlobalFunction( MakeRandomLines,
  function( arg )
    local i,l,number,pos,randomsource,sample;
    
    if Length(arg) <= 1 or Length(arg) > 3 then
        ErrorNoReturn("Usage: MakeRandomLines( sample, number [,randomsource] )");
    fi;
    sample := arg[1];
    number := arg[2];
    if Length(arg) = 3 then
        randomsource := arg[3];
    else
        randomsource := GlobalRandomSource;
    fi;
    
    l := [];
    for i in [number,number-1..1] do
        sample := ShallowCopy(sample);
        repeat
            Randomize(sample,randomsource);
            pos := PositionNonZero(sample);
        until pos <= Length(sample);
        MultVector(sample,sample[pos]^-1);
        l[i] := sample;
    od;
    return l;
  end );

# Product replacers:

BindGlobal( "ProductReplacersType", 
   NewType( ProductReplacersFamily, IsProductReplacer and IsMutable) );

InstallMethod( ProductReplacer, "for a list of group generators",
  [IsList], function( gens )
    return ProductReplacer( gens, rec( ) );
  end );

InstallMethod( ProductReplacer,
  "for a group object and an options record",
  [IsGroup, IsRecord],
  function(g,r) return ProductReplacer(GeneratorsOfGroup(g),r); end );

InstallMethod( ProductReplacer,
  "for a group object",
  [IsGroup],
  function(g) return ProductReplacer(GeneratorsOfGroup(g),rec()); end );

InstallMethod( ProductReplacer, 
  "for a list of group generators and an options record",
  [IsList, IsRecord],
  function( gens, opt )
    # First add some default options if not set:
    local pr;
    if Length(gens) = 0 then
        Error("ProductReplacer: need at least one generator");
        return fail;
    fi;
    pr := ShallowCopy(opt);
    if CanEasilySortElementsFamily(FamilyObj(gens[1])) then
        pr.gens := Set(gens);
    else
        pr.gens := ShallowCopy(gens);
    fi;
    pr.nrgens := Length(pr.gens);
    if not IsBound(pr.addslots) then
        pr.addslots := 5;
    fi;
    if not IsBound(pr.minslots) then
        pr.minslots := 0;
    fi;
    if Length(gens) = 1 and pr.addslots = 0 then
        # Make sure that the case of one generator is OK!
        pr.minslots := Maximum(2,pr.minslots);
    fi;
    if not IsBound(pr.randomsource) then
        pr.randomsource := GlobalRandomSource;
    fi;
    if not IsBound(pr.scramble) then 
        pr.scramble := 30; 
    fi;
    if not IsBound(pr.scramblefactor) then 
        pr.scramblefactor := 4;
    fi;
    pr.scramble := Maximum(pr.scramble,pr.scramblefactor*pr.nrgens);
    if not IsBound(pr.retirecaptain) then
        pr.retirecaptain := 2 * pr.scramble;
    fi;
    if not IsBound(pr.maxdepth) then
        pr.maxdepth := infinity;
    fi;
    if not IsBound(pr.noaccu) then
        pr.noaccu := false;
    fi;
    if not IsBound(pr.accus) or pr.accus < 0 then
        pr.accus := 5;
    elif pr.accus = 0 then
        pr.noaccu := true;
    fi;
    if not IsBound(pr.accelerator) then
        pr.accelerator := true;
    fi;
    if not IsBound(pr.normalin) then
        pr.normalin := false;
    else
        if pr.normalin <> false and
           not(IsProductReplacer(pr.normalin)) then
            Error("normalin option must be a product replacer");
        fi;
    fi;
    pr.slots := pr.nrgens + pr.addslots;
    if pr.slots < pr.minslots then
        pr.slots := pr.minslots;
    fi;
    pr.initialized := false;
    pr.steps := 0;
    if pr.noaccu = false then
        pr.accu := ListWithIdenticalEntries(pr.accus,pr.gens[1]^0);
        pr.accupos := 0;
    else
        pr.accu := fail;
        pr.accupos := 0;
    fi;
    Objectify(ProductReplacersType,pr);
    pr!.team := ShallowCopy(pr!.gens);
    while Length(pr!.team) < pr!.slots do
         Add(pr!.team,pr!.gens[Random(pr!.randomsource,1,pr!.nrgens)]);
    od;
    return pr;
  end );

InstallMethod( Reset, "for a product replacer", [IsProductReplacer],
  function(pr)
    if not(pr!.initialized) then return; fi;
    pr!.team := ShallowCopy(pr!.resetteam);
    pr!.accu := ShallowCopy(pr!.resetaccu);
    pr!.accupos := pr!.resetaccupos;
    pr!.steps := pr!.resetsteps;
    if pr!.normalin <> false then
        Reset(pr!.normalin);
    fi;
  end );

InstallMethod( Next, "for a product replacer", [IsProductReplacer],
  function(pr)
    local OneStep,i;
    OneStep := function(pr)
        local a, b, c, l, x, result, i;
        pr!.steps := pr!.steps + 1;
        c := Random(pr!.randomsource,1,2);
        if pr!.accelerator and pr!.steps <= pr!.retirecaptain then
            a := Random(pr!.randomsource,2,pr!.slots);
            b := Random(pr!.randomsource,2,pr!.slots);
            if c = 1 then
                if pr!.normalin <> false then
                    pr!.team[1] := pr!.team[1]*pr!.team[b]^Next(pr!.normalin);
                    pr!.team[a] := pr!.team[a]*pr!.team[1];
                else
                    pr!.team[1] := pr!.team[1]*pr!.team[b];
                    pr!.team[a] := pr!.team[a]*pr!.team[1];
                fi;
            else
                if pr!.normalin <> false then
                    pr!.team[1] := pr!.team[b]^Next(pr!.normalin)*pr!.team[1];
                    pr!.team[a] := pr!.team[1] * pr!.team[a];
                else
                    pr!.team[1] := pr!.team[b] * pr!.team[1];
                    pr!.team[a] := pr!.team[1] * pr!.team[a];
                fi;
            fi;
            result := pr!.team[a];
        else
            a := Random(pr!.randomsource,1,pr!.slots);
            b := Random(pr!.randomsource,1,pr!.slots-1);
            if b >= a then b := b + 1; fi;
            if c = 1 then
                if pr!.normalin <> false then
                    pr!.team[a]:=pr!.team[a]*pr!.team[b]^Next(pr!.normalin);
                else
                    pr!.team[a]:=pr!.team[a]*pr!.team[b];
                fi;
            else
                if pr!.normalin <> false then
                    pr!.team[a]:=pr!.team[b]^Next(pr!.normalin)*pr!.team[a];
                else
                    pr!.team[a]:=pr!.team[b]*pr!.team[a];
                fi;
            fi;
            result := pr!.team[a];
        fi;
        if pr!.noaccu or
           pr!.steps <= Maximum(pr!.nrgens * pr!.scramblefactor, pr!.scramble)
                        - pr!.accus then
            return result;
        else
            pr!.accupos := pr!.accupos + 1;
            if pr!.accupos > pr!.accus then pr!.accupos := 1; fi;
            pr!.accu[pr!.accupos] := pr!.accu[pr!.accupos] * result;   # Rattle
            return pr!.accu[pr!.accupos];
        fi;
    end;
    if pr!.steps > pr!.maxdepth then Reset(pr); fi;
    if not(pr!.initialized) then
        for i in [1..Maximum(pr!.nrgens * pr!.scramblefactor, pr!.scramble)] do
            OneStep(pr);
        od;
        pr!.resetteam := ShallowCopy(pr!.team);  # Remember for reset
        pr!.resetaccu := ShallowCopy(pr!.accu);
        pr!.resetaccupos := pr!.accupos;
        pr!.resetsteps := pr!.steps;
        pr!.initialized := true;
    fi;
    return OneStep(pr);
  end );

InstallMethod( AddGeneratorToProductReplacer, 
  "for a product replacer and a new generator",
  [ IsProductReplacer, IsObject ],
  function(pr,el)
    local i;
    Add(pr!.gens,el);
    pr!.nrgens := pr!.nrgens + 1;
    for i in [1..Length(pr!.team)] do
        pr!.team[i] := pr!.team[i] * el^Random(0,7);
    od;
    Add(pr!.team,el);
    if pr!.initialized then
        for i in [1..Length(pr!.resetteam)] do
            pr!.resetteam[i] := pr!.resetteam[i] * el^Random(0,7);
        od;
        Add(pr!.resetteam,el);
    fi;
    pr!.slots := pr!.slots + 1;
  end );

InstallMethod( ViewObj, "for a product replacer", [IsProductReplacer],
  function(pr)
    Print("<product replacer gens=",pr!.nrgens," slots=",pr!.slots,
          " scramble=",Maximum(pr!.nrgens*pr!.scramblefactor,pr!.scramble),
          " maxdepth=",pr!.maxdepth,"\n         steps=",pr!.steps);
    if pr!.noaccu then Print(" (without accu");
    else Print(" (rattle accus=",pr!.accus); fi;
    if pr!.accelerator then Print(", with accelerator)");
    else Print(")"); fi;
    if pr!.normalin <> false then
        Print("\n         normalin=");
        ViewObj(pr!.normalin);
    fi;
    Print(">");
  end );


# Finding things in groups, random searchers:

BindGlobal( "RandomSearchersType", 
              NewType( RandomSearchersFamily, 
                       IsRandomSearcher and IsMutable ) );

InstallMethod( RandomSearcher, 
  "for a list of generators and a test function", 
  [IsList, IsFunction],
  function( gens, testfunc )
    return RandomSearcher( gens, testfunc, rec() );
  end );

InstallMethod( RandomSearcher,
  "for a list of generators, a test function, and an options record",
  [IsList, IsFunction, IsRecord],
  function( gens, testfunc, opt )
    # Set some default options:
    local r;
    r := ShallowCopy(opt); 
    if not IsBound(r.exceptions) then
        r.exceptions := [];
    fi;
    if not IsBound(r.maxdepth) then
        r.maxdepth := 4 * Maximum(100,10*Length(gens));
    fi;
    if not IsBound(r.addslots) then
        r.addslots := 10;
    fi;
    if not IsBound(r.limit) then
        r.limit := infinity;
    fi;
    r.stops := 0;
    r.tries := 0;
    r.testfunc := testfunc;
    if IsBound( r.scramble ) then
        r.productreplacer := ProductReplacer( gens,
              rec( maxdepth := r.maxdepth,
                   scramble := 100,
                   scramblefactor := 10,
                   addslots := r.addslots ) );
              # this is the standard PseudoRandom behaviour
    else  # the standard case using all random elements:
        r.productreplacer := ProductReplacer( gens,
              rec( maxdepth := r.maxdepth,
                   scramble := 0,
                   scramblefactor := 0,
                   addslots := r.addslots ) );
              # this uses all random elements from the first on
    fi;
    Objectify( RandomSearchersType,r );
    return r;
  end );

InstallMethod( ViewObj, "for a random searcher", [IsRandomSearcher],
  function( rs )
    Print("<random searcher");
    if IsBound(rs!.target) then
        Print(" for ",rs!.target);
    fi;
    Print(", tries: ",rs!.tries," stops: ",rs!.stops,", exceptions: ",
          Length(rs!.exceptions),">");
  end );

InstallMethod( Search, "for a random searcher", [IsRandomSearcher],
  function( rs )
    local x,y;
    repeat
        repeat
            x := Next( rs!.productreplacer );
            rs!.tries := rs!.tries + 1;
            if rs!.tries mod 10000 = 0 then
                Info(InfoOrb,2,"Tried ",rs!.tries," elements.");
            fi;
            if IsObjWithMemory(x) then
                y := x!.el;
            else
                y := x;
            fi;
            if rs!.tries > rs!.limit then
                return fail;
            fi;
        until not( y in rs!.exceptions );
    until rs!.testfunc(y);
    rs!.stops := rs!.stops + 1;
    Add(rs!.exceptions,y);
    return x;
  end );


###################################################
# Involution centralisers and the dihedral trick: #
###################################################

InstallGlobalFunction( FindInvolution,
function(pr)
  # g a matrix group
  local i,o,x;
  for i in [1..1000] do
      x := Next(pr);
      o := Order(StripMemory(x));
      if o mod 2 = 0 then
          return x^(o/2);
      fi;
  od;
  return fail;
end );

InstallGlobalFunction( FindCentralisingElementOfInvolution,
function(pr,x)
  # pr a product replacer for G, x an involution in G
  local o,r,y,z;
  r := Next(pr);
  y := x^r;
  # Now x and y generate a dihedral group
  if x=y then return r; fi;
  z := x*y;
  o := Order(StripMemory(z));
  if IsEvenInt(o) then
      return z^(o/2);
  else
      return z^((o+1)/2)*r^(-1);
  fi;
end );

InstallGlobalFunction( FindInvolutionCentraliser,
function(pr,x,n)
  # pr a product replacer for G, x an involution in G, n an integer
  local i,l,y;
  l := [];
  for i in [1..n] do   # find 20 generators of the centraliser
      y := FindCentralisingElementOfInvolution(pr,x);
      AddSet(l,y);
  od;
  return l;
end );

InstallGlobalFunction( ReduceNumberOfGeneratorsUsingRecog,
function(l,Sizerecogniser)
  local drop,i,ll,ri,ri2,si,wholegroup;
  wholegroup := Group(StripMemory(l));
  ri := Sizerecogniser(wholegroup);
  si := Size(ri);
  Print("Whole size: ",si,"\n");
  i := Length(l);
  l := ShallowCopy(l);
  while i >= 1 do
      drop := false;
      ll := Concatenation(l{[1..i-1]},l{[i+1..Length(l)]});
      ri := Sizerecogniser(Group(StripMemory(ll)));
      if Size(ri) <> si then
          ri2 := Sizerecogniser(Group(StripMemory(ll)));
          if Size(ri2) <> Size(ri) then
              if Size(ri2) = si then
                  # we do not need this generator
                  drop := true;
              fi;
              # otherwise non-conclusive, leave the generator where it is
          fi;
      else
          # we most probably do not need this generator
          drop := true;
      fi;
      if drop then
          Remove(l,i);
          i := i - 1;
          Print("-\c");
      else
          i := i - 1;
          Print("+\c");
      fi;
  od;
  Print("\n");
  return l;
end );


###############################################
# Find class representatives using powermaps: #
###############################################

InstallGlobalFunction( ClassMaker,
function(ct,cyc)
  # ct an ordinary character table and cyc a list of class names we have
  local cln,done,erg,i,l,m,p,po,pow,todo,start;

  pow := ComputedPowerMaps(ct);
  cln := ClassNames(ct);

  cyc := List(cyc,LowercaseString);
  for i in [1..Length(cyc)] do
      p := Position(cyc[i],'-');
      if p <> fail then
          Info(InfoOrb,1,"Warning: Class name containing \"-\"!");
          cyc[i] := cyc[i]{[1..p-1]};
      fi;
  od;
  erg := [];
  todo := [];
  for i in [1..Length(cyc)] do
      po := Position(cln,cyc[i]);
      erg[po] := [i,1];
      Add(todo,po);
  od;
  done := Length(cyc);
  while done < Length(cln) and Length(todo) > 0 do
      i := todo[1];
      todo := todo{[2..Length(todo)]};
      for p in [1..Length(pow)] do
          if IsBound(pow[p]) then
              m := pow[p];
              if not(IsBound(erg[m[i]])) then
                  erg[m[i]] := [erg[i][1],erg[i][2]*p];
                  Add(todo,m[i]);
                  done := done + 1;
              fi;
          fi;
      od;
  od;
  if done < Length(cln) then
      Error("Unvorhergesehener Fall Nummer 1!");
  fi;
  if cln[1] = "1a" then
      l := [[1,0]];
      start := 2;
  else
      l := [];
      start := 1;
  fi;
  for i in [start..Length(cln)] do
      Add(l,erg[i]);
  od;
  return StraightLineProgramNC([l],Length(cyc));
end);


###########################
# Finding nice quotients: #
###########################

InstallGlobalFunction( OrbitStatisticOnVectorSpace,
  function(gens,size,ti)
  # gens must be a list of compressed matrices, size the order of the group
  local len,nr,o,t,total,v;
  v := ShallowCopy(gens[1][1]);
  t := Runtime();
  total := 0;
  nr := 0;

  while Runtime() < t + ti*1000 do
      Randomize(v);
      o := Orb(gens,v,OnRight,rec(orbsizebound := size,
                                  hashlen := 3*size, report := 0));
      Enumerate(o);
      len := Length(o!.orbit);
      total := total + len;
      nr := nr + 1;
      Print("Found length ",String(Length(o!.orbit),9),", have now ",
            String(nr,4)," orbits, average length: ",
            QuoInt(total+QuoInt(nr,2),nr),"     \r");
  od;
  Print("\n");
end);

InstallGlobalFunction( OrbitStatisticOnVectorSpaceLines,
  function(gens,size,ti)
  # gens must be a list of compressed matrices, size the order of the group
  local len,nr,o,t,total,v,c;
  v := ShallowCopy(gens[1][1]);
  t := Runtime();
  total := 0;
  nr := 0;

  while Runtime() < t + ti*1000 do
      Randomize(v);
      c := PositionNonZero(v);
      if c <= Length(v) then
          v := v / v[c];
      fi;
      o := Orb(gens,v,OnLines,rec(orbsizebound := size,
                                  hashlen := 3*size, report := 0));
      Enumerate(o);
      len := Length(o!.orbit);
      total := total + len;
      nr := nr + 1;
      Print("Found length ",String(Length(o!.orbit),9),", have now ",
            String(nr,4)," orbits, average length: ",
            QuoInt(total+QuoInt(nr,2),nr),"     \r");
  od;
  Print("\n");
end);

###############################################
# Finding nice generating sets for subgroups: #
###############################################

InstallGlobalFunction( ORB_PowerSet,
function(s)
  local i,l,le,ll;
  if Length(s) = 0 then
      return [[]];
  elif Length(s) = 1 then
      return [[],[s[1]]];
  else
      l := ORB_PowerSet(s{[1..Length(s)-1]});
      le := Length(l);
      for i in [1..le] do
          ll := ShallowCopy(l[i]);
          Add(ll,s[Length(s)]);
          Add(l,ll);
      od;
      return l;
  fi;
end );

InstallGlobalFunction( ORB_SLPLineFromWord, 
function(wo)
  local li,i,j;
  li := [];
  i := 1;
  while i <= Length(wo) do
      j := i+1;
      while j <= Length(wo) and wo[j] = wo[i] do
          j := j + 1;
      od;
      if wo[i] > 0 then
          Add(li,wo[i]);
          Add(li,j-i);
      else
          Add(li,-wo[i]);
          Add(li,-(j-i));
      fi;
      i := j;
  od;
  return li;
end );

InstallMethod( FindShortGeneratorsOfSubgroup, "without option rec or func",
  [ IsGroup, IsGroup ],
  function(G,U)
    return FindShortGeneratorsOfSubgroup(G,U,
               rec( membershiptest := \in, sizetester := Size ) );
  end );

InstallMethod( FindShortGeneratorsOfSubgroup, "with option rec or func",
  [ IsGroup, IsGroup, IsObject ],
function(G,U,opt)
  local su,o,si,s,ps,min,minsi,subgens,subwords,i,l,membershiptest,sizetester;
  if IsFunction(opt) then
      membershiptest := opt;
      sizetester := Size;
      su := Size(U);
  elif IsRecord(opt) then
      if IsBound(opt.membershiptest) then
          membershiptest := opt.membershiptest;
      else
          membershiptest := \in;
      fi;
      if IsBound(opt.sizetester) then
          sizetester := opt.sizetester;
      else
          sizetester := Size;
      fi;
      if IsBound(opt.sizeU) then
          su := opt.sizeU;
      else
          if HasSize(U) then
              su := Size(U);
          else
              su := sizetester(U);
          fi;
      fi;
  fi;
  if su = 1 then   # the trivial subgroup is easy to generate:
      return rec( gens := [One(U)], 
                  slp := StraightLineProgram( [[[1,0]]],
                                              Length(GeneratorsOfGroup(G)) ) );
  fi;
  o := Orb(GeneratorsOfGroup(G),One(G),OnRight,
           rec( lookingfor := function(o,x) return membershiptest(x,U); end, 
                schreier := true ) );
  Enumerate(o);
  subgens := [o!.orbit[o!.found]];
  subwords := [TraceSchreierTreeForward(o,o!.found)];
  l := 1;   # will always be the length of subgens and subwords
  si := sizetester(Group(ShallowCopy(subgens)));
  Info(InfoOrb,2,"Found subgroup of size ",si,":",subwords);
  if si = su then
      # Cyclic subgroup
      s := StraightLineProgram([[ORB_SLPLineFromWord(subwords[1])]],
                               Length(GeneratorsOfGroup(G)));
      return rec( gens := subgens, slp := s );
  fi;
  while true do
      Enumerate(o);
      Add(subgens,o!.orbit[o!.found]);
      Add(subwords,TraceSchreierTreeForward(o,o!.found));
      l := l + 1;
      if l <> Length(subgens) or l <> Length(subwords) then
          Error();
      fi;
      s := sizetester(Group(ShallowCopy(subgens)));
      if s = su then
          # OK, we have got a generating set:
          # Now try shortening generating set:
          Info(InfoOrb,2,"Found full subgroup of size ",si,":",subwords);
          Info(InfoOrb,2,"Need ",l," generators.");
          if l <= 8 then
              ps := ORB_PowerSet([1..l]);
              min := Length(ps);
              minsi := l;
              for i in [2..Length(ps)-1] do
                  s := sizetester(Group(subgens{ps[i]}));
                  if s = su and Length(ps[i]) < minsi then
                      min := i;
                      minsi := Length(ps[i]);
                      Info(InfoOrb,2,"Need ",minsi," generators.");
                  fi;
              od;
              subgens := subgens{ps[min]};
              subwords := subwords{ps[min]};
          else
              i := 1;
              while i <= Length(subgens) do
                  s := sizetester(
                          Group(subgens{Concatenation([1..i-1],[i+1..l])}));
                  if s = su then  # we do not need generator i
                      Remove(subgens,i);
                      Remove(subwords,i);
                      l := l - 1;
                  else
                      i := i + 1;
                  fi;
              od;
          fi;
          l := List(subwords,ORB_SLPLineFromWord);
          s := StraightLineProgram([l],Length(GeneratorsOfGroup(G)));
          return rec( gens := subgens, slp := s );
      fi;
      if s=si then
          Unbind(subgens[l]);
          Unbind(subwords[l]);
          l := l - 1;
      else
          si := s;
          Info(InfoOrb,2,"Found subgroup of size ",si,":",subwords);
      fi;
  od;
end );

##############################################################
# Helpers for permutation characters for certain operations: #
##############################################################

InstallGlobalFunction( NumberFixedVectors,
  function( mat )
    local f,n,q;
    n := NullspaceMat(mat - mat^0);
    f := BaseField(mat);
    q := Size(f);
    return q^Length(n);
  end );

InstallGlobalFunction( NumberFixedLines,
  function( mat )
    local el,f,n,nr,q;
    f := BaseField(mat);
    q := Size(f);
    nr := 0;
    for el in f do
        if not(IsZero(el)) then
            n := NullspaceMat(mat - el*mat^0);
            nr := nr + (q^Length(n)-1)/(q-1);
        fi;
    od;
    return nr;
  end );

InstallGlobalFunction( SpacesOfFixedLines,
  function( mats )
    local el,f,i,n,q,spcs;
    f := BaseField(mats);
    q := Size(f);
    spcs := List(mats,i->[]);
    for i in [1..Length(mats)] do
        for el in f do
            if not(IsZero(el)) then
                n := NullspaceMat(mats[i] - el*mats[i]^0);
                Add(spcs[i],n);
            fi;
        od;
    od;
    # to be continued...
    return spcs;
  end );

##################################################
# Helpers for making short SLPs from word lists: #
##################################################

InstallGlobalFunction( SLPForWordList,
  function( wordlist, nrgens )
    local bestlen,bestn,bestword,havewords,i,j,l,line,n,p,slp,w,where,wo,
          wordset,writepos,ww;
    havewords := [[]];
    where := [0];
    for i in [1..nrgens] do
        Add(havewords,[i]);
        Add(where,i);
        Add(havewords,[-i]);
        Add(where,-i);
    od;
    wordset := Set(wordlist);
    slp := [];
    writepos := nrgens+1;
    for w in wordset do
      if not(w in havewords) then
        wo := ShallowCopy(w);
        line := [];
        while Length(wo) > 0 do
            # Look through all havewords and see which one helps most:
            bestlen := 0;
            bestword := 0;
            bestn := 0;
            for j in [2..Length(havewords)] do
                ww := havewords[j];
                l := Length(ww);
                if l <= Length(wo) and wo{[1..l]} = ww then
                    # How often does it fit here?
                    n := 1;
                    while (n+1) * l <= Length(wo) and
                          ww = wo{[n*l+1..(n+1)*l]} do
                        n := n + 1;
                    od;
                    if n*l > bestlen then
                        bestlen := n*l;
                        bestword := j;
                        bestn := n;
                    fi;
                fi;
            od;
            if where[bestword] > 0 then
                Add(line,where[bestword]);
                Add(line,bestn);
            else
                Add(line,-where[bestword]);
                Add(line,-bestn);
            fi;
            l := Length(havewords[bestword]);
            wo := wo{[l*bestn+1..Length(wo)]};
        od;
        Add(slp,line);
        Add(havewords,w);
        Add(where,writepos);
        Add(havewords,-Reversed(w));
        Add(where,-writepos);
        writepos := writepos+1;
      fi;
    od;
    line := [];
    for w in wordlist do
        p := Position(havewords,w);
        if where[p] > 0 then
            Add(line,[where[p],1]);
        elif where[p] = 0 then   # The empty word
            Add(line,[1,0]);
        else
            Add(line,[-where[p],-1]);
        fi;
    od;
    Add(slp,line);
    return [StraightLineProgram(slp,nrgens),havewords,where];
end );


#############################################################################
# A generic way to find stabilizers:
#############################################################################

InstallGlobalFunction( ORB_EstimatePermGroupSize,
  function( gens )
    local g,s;
    g := Group(gens);
    s := StabChain(g,rec( random := 900 ));
    return SizeStabChain(s);
  end );


InstallGlobalFunction( ORB_FindStabilizerMC,
  function( gens, pt, op, memory, limit, errorbound, estimatefunc, opt )
    local counter,done,est,gensi,mem,o,oldest,orbpart,p,pr,prob,stab,tries,
          x,y,z;
    mem := Memory(pt);
    if mem = fail then
        mem := SIZE_OBJ(pt);
        if mem = 0 then mem := GAPInfo.BytesPerVariable; fi;
    fi;
    orbpart := QuoInt(memory,mem);
    o := Orb( gens, pt, op, rec( report := QuoInt(orbpart,10), 
                                 schreier := true, 
                                 hashlen := NextPrimeInt( orbpart*2 ) ) );
    Info( InfoOrb, 2, "Enumerating up to ",orbpart," elements of orbit..." );
    Enumerate(o,orbpart);

    # Now find stabilizer generators:
    stab := [];
    done := false;
    tries := 0;
    pr := ProductReplacer(gens,opt);
    est := 1;
    prob := 1;
    gensi := List(gens,x->x^-1);
    while prob > errorbound do
        counter := 0;
        repeat
            counter := counter + 1;
            x := Next(pr);
            y := op(pt,x);
            p := Position(o,y);
            Print(".\c");
        until p <> fail or counter > limit;
        Print("\n");
        if p = fail then
            Info( InfoOrb, 1, "Giving up..." );
            return rec(stab := stab, prob := fail);
        fi;
        z := x * Product(gensi{TraceSchreierTreeBack(o,p)});
        if IsOne(z) then
            Info( InfoOrb, 2, "Found identity element." );
        else
            AddSet(stab,z);
            oldest := est;
            if IsFunction(estimatefunc) then
                Info( InfoOrb, 2, "Starting estimation (old est=", est,")..." );
                est := estimatefunc(stab);
                Info( InfoOrb, 2, "Finished estimation: ", est );
            fi;
            if est = oldest then
                prob := prob / 2;
                Info( InfoOrb, 2, "New estimate is the same, prob=",prob );
            else
                prob := 1;
                Info( InfoOrb, 2, "New estimate is ",est,", prob=",prob );
            fi;
        fi;
    od;
    return rec(stab := stab, prob := prob);
  end );


InstallGlobalFunction( ORB_FindNeedleMappers,
  function( gens, pt, op, needles, memory, mappers, opt )
    local foundmappers,gensi,i,j,lookfunc,mem,o,p,r,size,x;
    size := fail;
    if IsGroup(gens) then
        if HasSize(gens) then
            size := Size(gens);
        fi;
        gens := GeneratorsOfGroup(gens);
    fi;
    if not IsBound(opt.orblen) then
        mem := Memory(pt);
        if mem = fail then
            mem := SIZE_OBJ(pt);
            if mem = 0 then 
                mem := GAPInfo.BytesPerVariable;
            fi;
        fi;
        opt.orblen := QuoInt(memory,Length(needles)*mem);
        if size <> fail and opt.orblen * opt.orblen > size then
            opt.orblen := 2^(QuoInt(Log2Int(size),2)+1);
        fi;
    fi;
    o := [];
    Info( InfoOrb, 1, "Enumerating ",Length(needles)," orbits up to length ",
          opt.orblen, "..." );
    for i in [1..Length(needles)] do
        o[i] := Orb( gens, needles[i], op, rec( report := QuoInt(opt.orblen,5),
                                                schreier := true ) );
        Enumerate(o[i],opt.orblen);
        Info( InfoOrb, 2, "Finished one orbit." );
    od;

    j := fail;
    p := fail;

    lookfunc := function( x )
      # Uses o from outer function!
      # Writes j and p in outer function!
      # This uses the feature of GAP, that j and p here always refer to
      # the outer function ORB_FindNeedleMappers
      # Reads pt and op from outer function.
      j := 1;
      while j <= Length(o) do
          p := Position(o[j],op(pt,x));
          if p <> fail then return true; fi;
          j := j + 1;
      od;
      j := fail;
      p := fail;
      return false;
    end;

    gensi := List(gens,x->x^-1);
    r := RandomSearcher( gens, lookfunc, opt );
    # we just hand down the options record to the random searcher!

    foundmappers := [];
    i := 1;
    while Length(foundmappers) <= mappers and i <= 15 * mappers do
        x := Search(r);
        if x = fail then return foundmappers; fi;
        AddSet(foundmappers,x * Product(gensi{TraceSchreierTreeBack(o[j],p)}));
        Info( InfoOrb, 2, "Found a needle mapper, have now ",
              Length(foundmappers), ".");
        i := i + 1;
    od;

    return foundmappers;
  end );

############################################################################
# A method to find transversals in matrix groups:
############################################################################

InstallGlobalFunction( FindWordsForRightTransversal,
  function( Ggens, Hgens, index )
    # Ggens and Hgens must be lists of invertible matrices over a
    # finite field. The group H generated by Hgens must be a subgroup
    # of G, which is generated by Ggens, index must be the index.
    # This function returns words in Ggens that are a complete set
    # of coset representatives of the right cosets {Hg}.
    local Check,i,inv,l,n,res,v;

    # Find invariant vectors:
    Info(InfoOrb,2,"Computing invariant spaces of generators...");
    l := List(Hgens,x->NullspaceMat(x-One(x)));
    Info(InfoOrb,2,"Found dimensions: ",List(l,Length));
    if Length(l) = 1 then
        inv := l[1];
    else
        inv := SumIntersectionMat(l[1],l[2]);
        inv := inv[2];
        Info(InfoOrb,2,"Intersection has dim ",NrRows(inv));
        i := 3;
        while i <= Length(l) and NrRows(inv) > 0 do
            inv := SumIntersectionMat(inv,l[i]);
            inv := inv[2];
            Info(InfoOrb,2,"Intersection has dim ",NrRows(inv));
            i := i + 1;
        od;
    fi;
    # Now inv contains vectors that span the invariant space.
    if NrRows(inv) = 0 then
        Info(InfoOrb,1,"no H-invariants except 0");
        return fail;
    fi;
    Unbind(l);

    Check := function( pt, op )
      local i,o,words;
      o := Orb(Ggens,pt,op,rec( report := 2000, schreier := true, 
                                hashlen := NextPrimeInt( index*3 ) ));
      Enumerate(o);
      if Length(o) = index then   
          # Hurra!
          words := [[]];
          for i in [2..Length(o)] do
              Add(words,TraceSchreierTreeForward(o,i));
          od;
          return rec( words := words, orbit := o );
      fi;
      Info(InfoOrb,2,"Found orbit length ",Length(o));
      return fail;
    end;
      
    # First try whether one basis vector provides an orbit of the
    # right length:
    Info(InfoOrb,2,"Trying to find a nice orbit on vectors...");
    for i in [1..NrRows(inv)] do
        Info(InfoOrb,2,"Using vector #",i,"(",NrRows(inv),")");
        res := Check(ShallowCopy(inv[i]),OnRight);
        if res <> fail then return res; fi;
    od;

    # Now try tuples of vectors:
    n := 2;  # first pairs, then maybe more
    repeat
        l := Matrix([],NrCols(inv),inv);
        for i in [1..n] do
            v := ZeroVector(NrRows(inv),inv[1]);
            Randomize(v);
            Add(l,v*inv);
        od;
        Info(InfoOrb,2,"Using ",n,"-tuple...");
        res := Check(l,OnRight);
        if res <> fail then return res; fi;
        n := n + 1;
    until n = NrRows(inv);

    # Now try the last resort:
    Info(InfoOrb,2,"Trying full invariant space...");
    return Check(inv,OnRight);
  end );

InstallGlobalFunction( FindWordsForLeftTransversal,
  function( Ggens, Hgens, index )
    # The same as above, but gives words for coset reps of the left
    # cosets {gH}. This is done by using the transposition
    # anti-automorphism. This maps a right transversal of the
    # transposed groups to a left transversal. Since we take the
    # transposed generators in both groups, the words have to be
    # reversed. Note that the orbit is the one obtained for the
    # transposed generators!
    local res;
    res := FindWordsForRightTransversal(List(Ggens,TransposedMat),
                                        List(Hgens,TransposedMat),index);
    if res = fail then return fail; fi;
    res.words := List(res.words,Reversed);
    return res;
  end );

############################################################################
# Find transforming matrices:
############################################################################

InstallGlobalFunction( TransformingMatsLSE, 
  function( A, B )
  local M,n;
  # Returns the matrix of the linear system of equations to solve TA=BT 
  # such that the nullspace of that matrix gives a basis of the solution 
  # space in flat notation.
  # "Flat notation" means Concatenation(T) instead of T.
  # Each row of M will be an equation:
  # M := [ [A[1][1],A[2][1],A[3][1],0,0,0,0,0,0],
  #        [A[1][2],A[2][2],A[3][2],0,0,0,0,0,0],
  #        [A[1][3],A[2][3],A[3][3],0,0,0,0,0,0],
  #        [0,0,0,A[1][1],A[2][1],A[3][1],0,0,0],
  #        [0,0,0,A[1][2],A[2][2],A[3][2],0,0,0],
  #        [0,0,0,A[1][3],A[2][3],A[3][3],0,0,0],
  #        [0,0,0,0,0,0,A[1][1],A[2][1],A[3][1]],
  #        [0,0,0,0,0,0,A[1][2],A[2][2],A[3][2]],
  #        [0,0,0,0,0,0,A[1][3],A[2][3],A[3][3]] ]
  #     -[ [B[1][1],0,0,B[1][2],0,0,B[1][3],0,0],
  #        [0,B[1][1],0,0,B[1][2],0,0,B[1][3],0],
  #        [0,0,B[1][1],0,0,B[1][2],0,0,B[1][3]],
  #        [B[2][1],0,0,B[2][2],0,0,B[2][3],0,0],
  #        [0,B[2][1],0,0,B[2][2],0,0,B[2][3],0],
  #        [0,0,B[2][1],0,0,B[2][2],0,0,B[2][3]],
  #        [B[3][1],0,0,B[3][2],0,0,B[3][3],0,0],
  #        [0,B[3][1],0,0,B[3][2],0,0,B[3][3],0],
  #        [0,0,B[3][1],0,0,B[3][2],0,0,B[3][3]] ];
  n := NrRows(A);
  if n <> NrCols(A) or n <> NrRows(B) or n <> NrCols(B) then
      Error("need square matrices of same size");
  fi;
  M :=   KroneckerProduct(OneMutable(A),TransposedMat(A))
       - KroneckerProduct(B,OneMutable(B));
  return TransposedMat(M);
end);

InstallGlobalFunction( TransformingMats,
  function(A, B)
    return NullspaceMat(TransformingMatsLSE(A,B));
  end );

##
##  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 <https://www.gnu.org/licenses/>.
##

[ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucher

Besucher

Monitoring

Montastic status badge