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


Quelle  matrix.gi   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of recog, a package for the GAP computer algebra system
##  which provides a collection of methods for the constructive recognition
##  of groups.
##
##  This files's authors include Max Neunhöffer, Ákos Seress.
##
##  Copyright of recog belongs to its developers whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##
##  A collection of find homomorphism methods for matrix groups.
##
#############################################################################

RECOG.HomToScalars := function(data,el)
  return ExtractSubMatrix(el,data.poss,data.poss);
end;

#! @BeginChunk DiagonalMatrices
#! This method is successful if and only if all generators of a matrix group
#! <A>G</A> are diagonal matrices. Otherwise, it returns <K>NeverApplicable</K>.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "DiagonalMatrices",
"check whether all generators are diagonal matrices",
function(ri, G)
  local H,d,f,gens,hom,i,isscalars,j,newgens,upperleft;

  d := ri!.dimension;
  if d = 1 then
      Info(InfoRecog,2,"Found dimension 1, going to Scalars method");
      return FindHomMethodsMatrix.Scalar(ri,G);
  fi;

  gens := GeneratorsOfGroup(G);
  if not ForAll(gens, IsDiagonalMat) then
      return NeverApplicable;
  fi;

  f := ri!.field;
  isscalars := true;
  i := 1;
  while isscalars and i <= Length(gens) do
      j := 2;
      upperleft := gens[i][1,1];
      while isscalars and j <= d do
          if upperleft <> gens[i][j,j] then
              isscalars := false;
          fi;
          j := j + 1;
      od;
      i := i + 1;
  od;

  if not isscalars then
      # We quickly know that we want to make a balanced tree:
      ri!.blocks := List([1..d],i->[i]);
      # Note that we cannot tell the upper levels that they should better
      # have made some more generators for the kernel!

      return FindHomMethodsMatrix.BlockScalar(ri,G);
  fi;

  # Scalar matrices, so go to dimension 1:
  newgens := List(gens,x->ExtractSubMatrix(x,[1],[1]));
  H := Group(newgens);
  hom := GroupHomByFuncWithData(G,H,RECOG.HomToScalars,rec(poss := [1]));
  SetHomom(ri,hom);
  findgensNmeth(ri).method := FindKernelDoNothing;

  # Hint to the image:
  AddMethod(InitialDataForImageRecogNode(ri).hints, FindHomMethodsMatrix.Scalar, 4000);

  return Success;
end);

#RECOG.DetWrapper := function(m)
#  local n;
#  n := ExtractSubMatrix(m,[1],[1]);
#  n[1][1] := DeterminantMat(m);
#  return n;
#end;

#FindHomMethodsMatrix.Determinant := function(ri, G)
#  local H,d,dets,gens,hom;
#  d := ri!.dimension;
#  if d = 1 then
#      Info(InfoRecog,2,"Found dimension 1, going to Scalar method");
#      return FindHomMethodsMatrix.Scalar(ri,G);
#  fi;
#
#  # check for a hint from above:
#  if IsBound(ri!.containedinsl) and ri!.containedinsl = true then
#      return false;  # will not succeed
#  fi;
#
#  gens := GeneratorsOfGroup(G);
#  dets := List(gens,RECOG.DetWrapper);
#  if ForAll(dets,IsOne) then
#      ri!.containedinsl := true;
#      return false;  # will not succeed
#  fi;
#
#  H := GroupWithGenerators(dets);
#  hom := GroupHomomorphismByFunction(G,H,RECOG.DetWrapper);
#
#  SetHomom(ri,hom);
#
#  # Hint to the kernel:
#  InitialDataForKernelRecogNode(ri).containedinsl := true;
#  return true;
#end;

SLPforElementFuncsMatrix.DiscreteLog := function(ri,x)
  local log;
  log := LogFFE(x[1,1],ri!.generator);
  if log = fail then
      return fail;
  fi;
  return StraightLineProgramNC([[1,log]],1);
end;

#! @BeginChunk Scalar
#! TODO
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "Scalar",
"Hint TODO",
function(ri, G)
  local f,gcd,generator,gens,i,l,o,pows,q,rep,slp,subset,z;
  if ri!.dimension > 1 then
      return NotEnoughInformation;
  fi;

  # FIXME: FieldOfMatrixGroup
  f := ri!.field;
  o := One(f);
  gens := List(GeneratorsOfGroup(G),x->x[1,1]);
  subset := Filtered([1..Length(gens)], i -> not IsOne(gens[i]));
  if subset = [] then
      return FindHomMethodsGeneric.TrivialGroup(ri,G);
  fi;
  gens := gens{subset};
  q := Size(f);
  z := PrimitiveRoot(f);
  pows := [LogFFE(gens[1],z)];     # zero cannot occur!
  Add(pows,q-1);
  gcd := Gcd(Integers,pows);
  i := 2;
  while i <= Length(gens) and gcd > 1 do
      pows[i] := LogFFE(gens[i],z);
      Add(pows,q-1);
      gcd := Gcd(Integers,pows);
      i := i + 1;
  od;
  rep := GcdRepresentation(Integers,pows);
  l := [];
  for i in [1..Length(pows)-1] do
      if rep[i] <> 0 then
          Add(l,subset[i]);
          Add(l,rep[i]);
      fi;
  od;
  slp := StraightLineProgramNC([[l]],Length(GeneratorsOfGroup(G)));
  Setslptonice(ri,slp);   # this sets the nice generators
  Setslpforelement(ri,SLPforElementFuncsMatrix.DiscreteLog);
  ri!.generator := z^gcd;
  SetFilterObj(ri,IsLeaf);
  return Success;
end);


# Given a matrix `mat`, and a set of indices `poss`,
# check whether the projection to the block mat{poss}{poss}
# is valid -- that is, whether the rows and columns in
# the set poss contain only zero elements outside of the
# positions indicated by poss.
RECOG.IsDiagonalBlockOfMatrix := function(m, poss)
  local n, outside, z, i, j;
  Assert(1, NrRows(m) = NrCols(m) and IsSubset([1..NrRows(m)], poss));
  outside := Difference([1..NrRows(m)], poss);
  z := ZeroOfBaseDomain(m);
  for i in poss do
    for j in outside do
      if m[i,j] <> z or m[j,i] <> z then
        return false;
      fi;
    od;
  od;
  return true;
end;


# Homomorphism used by these recognition methods:
# - FindHomMethodsMatrix.BlockScalar
# - FindHomMethodsProjective.BlocksModScalars
RECOG.HomToDiagonalBlock := function(data,el)
  # Verify el is in the domain of definition of this homomorphism.
  # Assuming the recognition result is correct, this is of course always
  # the case if el is a group element. However, this function may also
  # be called for elements which are not contained in the group.
  # TODO: add a switch to optionally bypass this verification
  # when we definitely don't need it?
  if not RECOG.IsDiagonalBlockOfMatrix(el, data.poss) then
    return fail;
  fi;

  return ExtractSubMatrix(el,data.poss,data.poss);
end;

#! @BeginChunk BlockScalar
#! This method is only called by a hint. Alongside with the hint it gets
#! a block decomposition respected by the matrix group <A>G</A> to be recognised
#! and the promise that all diagonal blocks of all group elements
#! will only be scalar matrices. This method recursively builds a balanced tree
#! and does scalar recognition in each leaf.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "BlockScalar",
"Hint TODO",
function(ri, G)
  # We assume that ri!.blocks is a list of ranges where the non-trivial
  # scalar blocks are. Note that their length does not have to sum up to
  # the dimension, because some blocks at the end might already be trivial.
  local H,data,hom,middle,newgens,nrblocks,topblock;
  nrblocks := Length(ri!.blocks);  # this is always >= 1
  if nrblocks <= 2 then   # the image is only one block
      # go directly to scalars in that case:
      data := rec(poss := ri!.blocks[nrblocks]);
      newgens := List(GeneratorsOfGroup(G),x->RECOG.HomToDiagonalBlock(data,x));
      H := GroupWithGenerators(newgens);
      hom := GroupHomByFuncWithData(G,H,RECOG.HomToDiagonalBlock,data);
      SetHomom(ri,hom);
      AddMethod(InitialDataForImageRecogNode(ri).hints, FindHomMethodsMatrix.Scalar, 2000);

      if nrblocks = 1 then     # no kernel:
          findgensNmeth(ri).method := FindKernelDoNothing;
      else   # exactly two blocks:
          # FIXME: why don't we just compute a precise set of generators of the kernel?
          # That should be easily and efficiently possible at this point, no?
          # The kernel is abelian, so we don't need to do normal closures.
          findgensNmeth(ri).method := FindKernelRandom;
          findgensNmeth(ri).args := [7];
          InitialDataForKernelRecogNode(ri).blocks := ri!.blocks{[1]};
          # We have to go to BlockScalar with 1 block because the one block
          # is only a part of the whole matrix:
          AddMethod(InitialDataForKernelRecogNode(ri).hints, FindHomMethodsMatrix.BlockScalar, 2000);
          Setimmediateverification(ri,true);
      fi;
      return Success;
  fi;

  # We hack away at least two blocks and leave at least one:
  middle := QuoInt(nrblocks,2)+1;   # the first one taken
  topblock := ri!.blocks[nrblocks];
  data := rec(poss := [ri!.blocks[middle][1]..topblock[Length(topblock)]]);
  newgens := List(GeneratorsOfGroup(G),x->RECOG.HomToDiagonalBlock(data,x));
  H := GroupWithGenerators(newgens);
  hom := GroupHomByFuncWithData(G,H,RECOG.HomToDiagonalBlock,data);
  SetHomom(ri,hom);

  # the image are the last few blocks:
  InitialDataForImageRecogNode(ri).blocks := List(ri!.blocks{[middle..nrblocks]},
                               x->x - (ri!.blocks[middle][1]-1));
  AddMethod(InitialDataForImageRecogNode(ri).hints, FindHomMethodsMatrix.BlockScalar, 2000);

  # the kernel is the first few blocks (can be only one!):
  # FIXME: why don't we just compute a precise set of generators of the kernel?
  # That should be easily and efficiently possible at this point, no?
  findgensNmeth(ri).args[1] := 3 + nrblocks;
  findgensNmeth(ri).args[2] := 5;
  InitialDataForKernelRecogNode(ri).blocks := ri!.blocks{[1..middle-1]};
  AddMethod(InitialDataForKernelRecogNode(ri).hints, FindHomMethodsMatrix.BlockScalar, 2000);
  Setimmediateverification(ri,true);
  return Success;
end);

# A helper function for base changes:
RECOG.HomDoBaseChange := function(data,el)
  return data.t*el*data.ti;
end;

RECOG.CleanRow := function( basis, vec, extend, dec)
  local c,firstnz,i,j,lc,len,lev,mo,newpiv,pivs;
  # INPUT
  # basis: record with fields
  #        pivots   : integer list of pivot columns of basis matrix
  #        vectors : matrix of basis vectors in semi echelon form
  # vec  : vector of same length as basis vectors
  # extend : boolean value indicating whether the basis will we extended
  #          note that for the greased case also the basis vectors before
  #          the new one may be changed
  # OUTPUT
  # returns decomposition of vec in the basis, if possible.
  # otherwise returns 'fail' and adds cleaned vec to basis and updates
  # pivots
  # NOTES
  # destructive in both arguments

  # Clear dec vector if given:
  if dec <> fail then
    MultRowVector(dec,Zero(dec[1]));
  fi;

  # First a little shortcut:
  firstnz := PositionNonZero(vec);
  if firstnz > Length(vec) then
      return true;
  fi;

  len := Length(basis.vectors);
  i := 1;

  for j in [i..len] do
    if basis.pivots[j] >= firstnz then
      c := vec[ basis.pivots[ j ] ];
      if not IsZero( c ) then
        if dec <> fail then
          dec[ j ] := c;
        fi;
        AddRowVector( vec, basis.vectors[ j ], -c );
      fi;
    fi;
  od;

  newpiv := PositionNonZero( vec );
  if newpiv = Length( vec ) + 1 then
    return true;
  else
    if extend then
      c := vec[newpiv]^-1;
      MultRowVector( vec, vec[ newpiv ]^-1 );
      if dec <> fail then
        dec[len+1] := c;
      fi;
      Add( basis.vectors, vec );
      Add( basis.pivots, newpiv );
    fi;
    return false;
  fi;
end;

RECOG.FindAdjustedBasis := function(l)
  # l must be a list of matrices coming from MTX.BasesCompositionSeries.
  local blocks,i,pos,seb,v;
  blocks := [];
  seb := rec( vectors := [], pivots := [] );
  pos := 1;
  for i in [2..Length(l)] do
      for v in l[i] do
          RECOG.CleanRow(seb,ShallowCopy(v),true,fail);
      od;
      Add(blocks,[pos..Length(seb.vectors)]);
      pos := Length(seb.vectors)+1;
  od;
  ConvertToMatrixRep(seb.vectors);
  return rec(base := seb.vectors, baseinv := seb.vectors^-1, blocks := blocks);
end;

#! @BeginChunk ReducibleIso
#! This method determines whether a matrix group <A>G</A> acts irreducibly.
#! If yes, then it returns <K>NeverApplicable</K>. If <A>G</A> acts reducibly then
#! a composition series of the underlying module is computed and a base
#! change is performed to write <A>G</A> in a block lower triangular form.
#! Also, the method passes a hint to the image group that it is in
#! block lower triangular form, so the image immediately can make
#! recursive calls for the actions on the diagonal blocks, and then to the
#! lower <M>p</M>-part. For the image the method
#! <Ref Subsect="BlockLowerTriangular" Style="Text"/> is used.
#! 
#! Note that this method is implemented in a way such that it can also be
#! used as a method for a projective group <A>G</A>. In that case the
#! recognition node has the <C>!.projective</C> component bound
#! to <K>true</K> and this information is passed down to image and kernel.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "ReducibleIso",
"use the MeatAxe to find invariant subspaces",
# alternative comment:
#"use MeatAxe to find a composition series, do base change",
function(ri,G)
  # First we use the MeatAxe to find an invariant subspace:
  local H,bc,compseries,f,hom,isirred,m,newgens;

  RECOG.SetPseudoRandomStamp(G,"ReducibleIso");

  if IsBound(ri!.isabsolutelyirred) and ri!.isabsolutelyirred then
      # this information is coming from above
      return NeverApplicable;
  fi;

  # FIXME:
  f := ri!.field;
  m := GModuleByMats(GeneratorsOfGroup(G),f);
  isirred := MTX.IsIrreducible(m);

  # Save the MeatAxe module for later use:
  ri!.meataxemodule := m;
  # Report enduring failure if irreducible:
  if isirred then
      return NeverApplicable;
  fi;

  # Now compute a composition series:
  compseries := MTX.BasesCompositionSeries(m);
  bc := RECOG.FindAdjustedBasis(compseries);

  Info(InfoRecog,2,"Found composition series with block sizes ",
       List(bc.blocks,Length)," (dim=",ri!.dimension,")");

  # Do the base change:
  newgens := List(GeneratorsOfGroup(G),x->bc.base*x*bc.baseinv);
  H := GroupWithGenerators(newgens);
  hom := GroupHomByFuncWithData(G,H,RECOG.HomDoBaseChange,
                                rec(t := bc.base,ti := bc.baseinv));

  # Now report back:
  SetHomom(ri,hom);
  findgensNmeth(ri).method := FindKernelDoNothing;

  # Inform authorities that the image can be recognised easily:
  InitialDataForImageRecogNode(ri).blocks := bc.blocks;
  AddMethod(InitialDataForImageRecogNode(ri).hints, FindHomMethodsMatrix.BlockLowerTriangular, 4000);

  return Success;
end);

# Given a matrix `mat` and a list of index sets `blocks`,
# verify that the matrix has block lower triangular shape
# with respect to the given blocks.
RECOG.IsBlockLowerTriangularWithBlocks := function(mat, blocks)
  local z, b, col, row;
  Assert(0, Concatenation(blocks) = [1..Length(mat)]);
  z := ZeroOfBaseDomain(mat);
  for b in blocks do
    # Verify that there are only zeros above each block
    for row in [1..b[1]-1] do
      for col in b do
        if mat[row,col] <> z then
          return false;
        fi;
      od;
    od;
  od;
  return true;
end;

# Homomorphism method used by FindHomMethodsMatrix.BlockLowerTriangular.
RECOG.HomOntoBlockDiagonal := function(data,el)
  local m, x;

  # Verify el is in the domain of definition of this homomorphism.
  # Assuming the recognition result is correct, this is of course always
  # the case if el is a group element. However, this function may also
  # be called for elements which are not contained in the group.
  if not RECOG.IsBlockLowerTriangularWithBlocks(el,data.blocks) then
    return fail;
  fi;

  m := ZeroMutable(el);
  for x in data.blocks do
    CopySubMatrix(el, m, x, x, x, x);
  od;
  return m;
end;

#! @BeginChunk BlockLowerTriangular
#! This method is only called when a hint was passed down from the method
#! <Ref Subsect="ReducibleIso" Style="Text"/>. In that case, it knows that a
#! base change to block lower triangular form has been performed. The method
#! can then immediately find a homomorphism by mapping to the diagonal blocks.
#! It sets up this homomorphism and gives hints to image and kernel. For the
#! image, the method <Ref Subsect="BlockDiagonal" Style="Text"/> is used and
#! for the kernel, the method <Ref Subsect="LowerLeftPGroup" Style="Text"/>
#! is used.
#! 
#! Note that this method is implemented in a way such that it can also be
#! used as a method for a projective group <A>G</A>. In that case the
#! recognition node has the <C>!.projective</C> component bound
#! to <K>true</K> and this information is passed down to image and kernel.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "BlockLowerTriangular",
"for a group generated by block lower triangular matrices",
function(ri,G)
  # This is only used coming from a hint, we know what to do:
  # A base change was done to get block lower triangular shape.
  # We first do the diagonal blocks, then the lower p-part:
  local H,data,hom,newgens;
  data := rec( blocks := ri!.blocks );
  newgens := List(GeneratorsOfGroup(G),
                  x->RECOG.HomOntoBlockDiagonal(data,x));
  Assert(0, not fail in newgens);
  H := GroupWithGenerators(newgens);
  hom := GroupHomByFuncWithData(G,H,RECOG.HomOntoBlockDiagonal,data);
  SetHomom(ri,hom);

  # Now give hints downward:
  InitialDataForImageRecogNode(ri).blocks := ri!.blocks;
  AddMethod(InitialDataForImageRecogNode(ri).hints, FindHomMethodsMatrix.BlockDiagonal, 2000);
  findgensNmeth(ri).method := FindKernelLowerLeftPGroup;
  findgensNmeth(ri).args := [];
  AddMethod(InitialDataForKernelRecogNode(ri).hints, FindHomMethodsMatrix.LowerLeftPGroup, 2000);
  InitialDataForKernelRecogNode(ri).blocks := ri!.blocks;
  return Success;
end);

#! @BeginChunk BlockDiagonal
#! This method is only called when a hint was passed down from the method
#! <Ref Subsect="BlockLowerTriangular" Style="Text"/>.
#! In that case, it knows that the group is in block diagonal form.
#! The method is used both in the matrix- and the projective case.
#! 
#! The method immediately delegates to projective methods handling
#! all the diagonal blocks projectively. This is done by giving a hint
#! to the image to use the method
#! <Ref Subsect="BlocksModScalars" Style="Text"/>.
#! The method for the kernel then has to deal with only scalar blocks,
#! either projectively or with scalars, which is again done by giving a hint
#! to either use <Ref Subsect="BlockScalar" Style="Text"/> or
#! <Ref Subsect="BlockScalarProj" Style="Text"/> respectively.
#! 
#! Note that this method is implemented in a way such that it can also be
#! used as a method for a projective group <A>G</A>. In that case the
#! recognition node has the <C>!.projective</C> component bound
#! to <K>true</K> and this information is passed down to image and kernel.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "BlockDiagonal",
"for groups generated by block diagonal matrices",
function(ri,G)
  # This is only called by a hint, so we know what we have to do:
  # We do all the blocks projectively and thus are left with scalar blocks.
  # In the projective case we still do the same, the BlocksModScalars
  # will automatically take care of the projectiveness!
  SetHomom(ri, IdentityMapping(G));
  # Now give hints downward:
  InitialDataForImageRecogNode(ri).blocks := ri!.blocks;
  AddMethod(InitialDataForImageRecogNode(ri).hints, FindHomMethodsProjective.BlocksModScalars, 2000);
  # We go to projective, although it would not matter here, because we
  # gave a working hint anyway:
  Setmethodsforimage(ri,FindHomDbProjective);

  # the kernel:
  # The kernel is abelian, so we don't need to do normal closures.
  findgensNmeth(ri).method := FindKernelRandom;
  findgensNmeth(ri).args := [Length(ri!.blocks)+10];
  # In the projective case we have to do a trick: We use an isomorphism
  # to a matrix group by multiplying things such that the last block
  # becomes an identity matrix:
  if ri!.projective then
      AddMethod(InitialDataForKernelRecogNode(ri).hints,
                FindHomMethodsProjective.BlockScalarProj,
                2000);
  else
      AddMethod(InitialDataForKernelRecogNode(ri).hints, FindHomMethodsMatrix.BlockScalar, 2000);
  fi;
  InitialDataForKernelRecogNode(ri).blocks := ri!.blocks;
  Setimmediateverification(ri, true);
  return Success;
end);

#RECOG.HomInducedOnFactor := function(data,el)
#  local dim,m;
#  dim := Length(el);
#  m := ExtractSubMatrix(el,[data.subdim+1..dim],[data.subdim+1..dim]);
#  # FIXME: no longer necessary when vectors/matrices are in place:
#  ConvertToMatrixRep(m);
#  return m;
#end;
#
#FindHomMethodsMatrix.InducedOnFactor := function(ri,G)
#  local H,dim,gens,hom,newgens,gen,data;
#  # Are we applicable?
#  if not IsBound(ri!.subdim) then
#      return NotEnoughInformation;
#  fi;
#
#  # Project onto image:
#  gens := GeneratorsOfGroup(G);
#  dim := ri!.dimension;
#  data := rec(subdim := ri!.subdim);
#  newgens := List(gens, x->RECOG.HomInducedOnFactor(data,x));
#  H := GroupWithGenerators(newgens);
#  hom := GroupHomByFuncWithData(G,H,RECOG.HomInducedOnFactor,data);
#
#  # Now report back:
#  SetHomom(ri,hom);
#
#  # Inform authorities that the kernel can be recognised easily:
#  InitialDataForKernelRecogNode(ri).subdim := ri!.subdim;
#  AddMethod(InitialDataForKernelRecogNode(ri).hints, FindHomMethodsMatrix.InducedOnSubspace, 2000);
#
#  return true;
#end;
#
RECOG.ExtractLowStuff := function(m,layer,blocks,lens,basisOfFieldExtension)
  local block,i,j,k,l,pos,v,what,where;
  v := ZeroVector(lens[layer],m[1]);
  pos := 0;
  i := layer+1;
  l := Length(blocks);
  # Extract the blocks with block coordinates (i,1)..(l,l-i+1):
  for j in [1..l-i+1] do
      block := [j+i-1,j];
      what := blocks[block[2]];
      for k in blocks[block[1]] do
          where := [pos+1..pos+Length(what)];
          CopySubVector(m[k],v,what,where);
          pos := pos + Length(what);
      od;
  od;
  if basisOfFieldExtension <> fail then
      # needed because we assume for example in
      # SLPforElementFuncsMatrix.LowerLeftPGroup that we work
      # over a field of order p (not a p power)
      return BlownUpVector(basisOfFieldExtension, v);
  fi;
  return v;
end;

RECOG.ComputeExtractionLayerLengths := function(blocks)
  local block,i,j,l,len,lens;
  lens := [];
  l := Length(blocks);
  for i in [2..Length(blocks)] do
      len := 0;
      for j in [1..l-i+1] do
          block := [j+i-1,j];
          len := len + Length(blocks[block[1]]) * Length(blocks[block[2]]);
      od;
      Add(lens,len);
  od;
  return lens;
end;

InstallGlobalFunction( FindKernelLowerLeftPGroup,
  function(ri)
    local basisOfFieldExtension,curlay,done,el,f,i,l,lens,lvec,nothingnew,pivots,pos,ready,
          rifac,s,v,x,y;

    Info(InfoRecog, 2, "Running FindKernelLowerLeftPGroup...");
    f := ri!.field;
    l := [];       # here we collect generators of N
    lvec := [];    # the linear part of the layer cleaned out by the gens
    pivots := [];  # pairs of numbers indicating the layer and pivot columns
                   # this will stay strictly increasing (lexicographically)
    lens := RECOG.ComputeExtractionLayerLengths(ri!.blocks);

    if not IsPrimeField(f) then
        basisOfFieldExtension := CanonicalBasis(f);  # a basis over the prime field
        lens := lens * Length(basisOfFieldExtension);
    else
        basisOfFieldExtension := fail;
    fi;

    nothingnew := 0;   # we count until we produced 10 new generators
                       # giving no new dimension
    rifac := ImageRecogNode(ri);
    while nothingnew < 10 do
        x := RandomElm(ri,"KERNEL",true).el;
        Assert(2, ValidateHomomInput(ri, x));
        s := SLPforElement(rifac,ImageElm( Homom(ri), x!.el ));
        y := ResultOfStraightLineProgram(s, ri!.pregensfacwithmem);
        x := x^-1 * y;   # this is in the kernel

        # Now clean out this vector and remember what we did:
        curlay := 1;
        v := RECOG.ExtractLowStuff(x,curlay,ri!.blocks,lens,basisOfFieldExtension);
        pos := PositionNonZero(v);
        i := 1;
        done := 0*[1..Length(lvec)];   # this refers to the current gens
        ready := false;
        while not ready do
            # Find out where there is something left:
            while pos > Length(v) and not ready do
                curlay := curlay + 1;
                if curlay <= Length(lens) then
                    v := RECOG.ExtractLowStuff(x,curlay,ri!.blocks,lens,basisOfFieldExtension);
                    pos := PositionNonZero(v);
                else
                    ready := true;   # x is now equal to the identity!
                fi;
            od;
            # Either there is something left in this layer or we are done
            if ready then break; fi;

            # Clean out this layer:
            while i <= Length(l) and pivots[i][1] <= curlay do
                if pivots[i][1] = curlay then
                    # we might have jumped over a layer
                    done := -v[pivots[i][2]];
                    if not IsZero(done) then
                        AddRowVector(v,lvec[i],done);
                        x := x * l[i]^IntFFE(done);
                    fi;
                fi;
                i := i + 1;
            od;
            pos := PositionNonZero(v);
            if pos <= Length(v) then  # something left here!
                ready := true;
            fi;
        od;
        # Now we have cleaned out x until one of the following happened:
        #   x is the identity (<=> curlay > Length(lens))
        #   x has been cleaned up to some layer and is not yet zero
        #     in that layer (<=> pos <= Length(v))
        #     then a power of x will be a new generator in that layer and
        #     has to be added in position i in the list of generators
        if curlay <= Length(lens) then   # a new generator
            # Now find a new pivot:
            el := v[pos]^-1;
            MultRowVector(v,el);
            x := x ^ IntFFE(el);
            Add(l,x,i);
            Add(lvec,v,i);
            Add(pivots,[curlay,pos],i);
            nothingnew := 0;
        else
            nothingnew := nothingnew + 1;
        fi;
    od;
    # Now make sure those things get handed down to the kernel:
    InitialDataForKernelRecogNode(ri).gensNvectors := lvec;
    InitialDataForKernelRecogNode(ri).gensNpivots := pivots;
    InitialDataForKernelRecogNode(ri).blocks := ri!.blocks;
    InitialDataForKernelRecogNode(ri).lens := lens;
    InitialDataForKernelRecogNode(ri).basisOfFieldExtension := basisOfFieldExtension;
    # this is stored on the upper level:
    SetgensN(ri,l);
    ri!.leavegensNuntouched := true;
    return true;
  end );

# computes a straight line program (SLP) for an element <g> of a p-group
# described by <ri> (that corresponds to a matrix group consisting of lower
# triangular matrices).
# The SLP is constructed by using matrices forming a pcgs (polycyclic
# generating sequence) to cancel out the
# entries in <g>. The coefficents of the process create the SLP.
# TODO Max H. wants to rewrite the code to work row-by-row
# instead of blockwise; that should result in both simple and faster code.
SLPforElementFuncsMatrix.LowerLeftPGroup := function(ri,g)
  # First project and calculate the vector:
  local done,h,i,l,layer,pow;
  # Take care of the projective case:
  if ri!.projective and not IsOne(g[1,1]) then
      g := (g[1,1]^-1) * g;
  fi;
  l := [];
  i := 1;
  for layer in [1..Length(ri!.lens)] do
      h := RECOG.ExtractLowStuff(g,layer,ri!.blocks,ri!.lens,
                                 ri!.basisOfFieldExtension);
      while i <= Length(ri!.gensNvectors) and ri!.gensNpivots[i][1] = layer do
          done := h[ri!.gensNpivots[i][2]];
          if not IsZero(done) then
              AddRowVector(h,ri!.gensNvectors[i],-done);
              pow := IntFFE(done);
              g := NiceGens(ri)[i]^(-pow) * g;
              Add(l,i);
              Add(l,IntFFE(done));
          fi;
          i := i + 1;
      od;
      if not IsZero(h) then
          return fail;
      fi;
  od;
  if Length(l) = 0 then
      l := [1, 0];
  fi;
  return StraightLineProgramNC([l], Length(ri!.gensNvectors));
end;

#! @BeginChunk LowerLeftPGroup
#! This method is only called by a hint from <Ref Subsect="BlockLowerTriangular" Style="Text"/>
#! as the kernel of the homomorphism mapping to the diagonal blocks.
#! The method uses the fact that this kernel is a <M>p</M>-group where
#! <M>p</M> is the characteristic of the underlying field. It exploits
#! this fact and uses this special structure to find nice generators
#! and a method to express group elements in terms of these.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "LowerLeftPGroup",
"Hint TODO",
function(ri,G)
  local f,p;
  # Do we really have our favorite situation?
  if not (IsBound(ri!.blocks) and
          IsBound(ri!.lens) and
          IsBound(ri!.basisOfFieldExtension) and
          IsBound(ri!.gensNvectors) and
          IsBound(ri!.gensNpivots)) then
      return NotEnoughInformation;
  fi;
  # We are done, because we can do linear algebra:
  f := ri!.field;
  p := Characteristic(f);
  SetFilterObj(ri,IsLeaf);
  Setslpforelement(ri,SLPforElementFuncsMatrix.LowerLeftPGroup);
  SetSize(ri,p^Length(ri!.gensNvectors));
  return Success;
end);

#! @BeginChunk GoProjective
#! This method defines a homomorphism from a matrix group <A>G</A>
#! into the projective group <A>G</A> modulo scalar matrices. In fact, since
#! projective groups in &GAP; are represented as matrix groups, the
#! homomorphism is the identity mapping and the only difference is that in
#! the image the projective group methods can be applied.
#! The bulk of the work in matrix recognition is done in the projective group
#! setting.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "GoProjective",
"divide out scalars and recognise projectively",
function(ri,G)
  local hom,q;
  Info(InfoRecog,2,"Going projective...");
  hom := IdentityMapping(G);
  SetHomom(ri,hom);
  # Now give hints downward:
  Setmethodsforimage(ri,FindHomDbProjective);
  # Make sure that immediate verification is performed to safeguard against the
  # kernel being too small.
  Setimmediateverification(ri, true);
  # note that RecogniseGeneric detects the use of FindHomDbProjective and
  # sets ri!.projective := true for the image
  # the kernel:
  q := Size(ri!.field);
  findgensNmeth(ri).method := FindKernelRandom;
  findgensNmeth(ri).args := [Length(Factors(q-1))+5];
  return Success;
end);

#! @BeginChunk KnownStabilizerChain
#! If a stabilizer chain is already known, then the kernel node is given
#! knowledge about this known stabilizer chain, and the image node is told to
#! use homomorphism methods from the database for permutation groups.
#! If a stabilizer chain of a parent node is already known this is used for
#! the computation of a stabilizer chain of this node. This stabilizer chain
#! is then used in the same way as above.
#! @EndChunk
BindRecogMethod(FindHomMethodsMatrix, "KnownStabilizerChain",
"use an already known stabilizer chain for this group",
function(ri,G)
  local S,hom;
  if HasStoredStabilizerChain(G) then
      Info(InfoRecog,2,"Already know stabilizer chain, using 1st orbit.");
      S := StoredStabilizerChain(G);
      hom := OrbActionHomomorphism(G,S!.orb);
      SetHomom(ri,hom);
      Setmethodsforimage(ri,FindHomDbPerm);
      InitialDataForKernelRecogNode(ri).StabilizerChainFromAbove := S;
      return Success;
  elif IsBound(ri!.StabilizerChainFromAbove) then
      Info(InfoRecog,2,"Know stabilizer chain for super group, using base.");
      S := StabilizerChain(G,rec( Base := ri!.StabilizerChainFromAbove ));
      Info(InfoRecog,2,"Computed stabilizer chain, size=",Size(S));
      hom := OrbActionHomomorphism(G,S!.orb);
      SetHomom(ri,hom);
      Setmethodsforimage(ri,FindHomDbPerm);
      InitialDataForKernelRecogNode(ri).StabilizerChainFromAbove := S;
      return Success;
  fi;
  return NeverApplicable;
end);

#FindHomMethodsMatrix.SmallVectorSpace := function(ri,G)
#  local d,f,hom,l,method,o,q,r,v,w;
#  d := ri!.dimension;
#  f := ri!.field;
#  q := Size(f);
#  if q^d > 10000 then
#      return false;
#  fi;
#
#  # Now we will for sure find a rather short orbit:
#  # FIXME: adjust to new vector/matrix interface:
#  v := FullRowSpace(f,d);
#  repeat
#      w := Random(v);
#  until not IsZero(w);
#  o := Orbit(G,w,OnRight);
#  hom := ActionHomomorphism(G,o,OnRight);
#
#  Info(InfoRecog,2,"Found orbit of length ",Length(o),".");
#  if Length(o) >= d then
#      l := Minimum(Length(o),3*d);
#      r := RankMat(o{[1..l]});
#      if r = d then
#          # We proved that it is an isomorphism:
#          findgensNmeth(ri).method := FindKernelDoNothing;
#          Info(InfoRecog,2,"Spans rowspace ==> found isomorphism.");
#      else
#          Info(InfoRecog,3,"Rank of o{[1..3*d]} is ",r,".");
#      fi;
#  fi;
#
#  SetHomom(ri,hom);
#  Setmethodsforimage(ri,FindHomDbPerm);
#  return true;
#end;
#
#FindHomMethodsMatrix.IsomorphismPermGroup := function(ri,G)
#  SetHomom(ri,IsomorphismPermGroup(G));
#  findgensNmeth(ri).method := FindKernelDoNothing;
#  Setmethodsforimage(ri,FindHomDbPerm);
#  return true;
#end;

#! @BeginCode AddMethod_Matrix_FindHomMethodsGeneric.TrivialGroup
AddMethod(FindHomDbMatrix, FindHomMethodsGeneric.TrivialGroup, 3100);
#! @EndCode

AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.DiagonalMatrices, 1100);

AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.KnownStabilizerChain, 1175);

AddMethod(FindHomDbPerm, FindHomMethodsGeneric.FewGensAbelian, 1050);

AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.ReducibleIso, 1000);

AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.GoProjective, 900);



###AddMethod( FindHomDbMatrix, FindHomMethodsMatrix.SmallVectorSpace,
###           700, "SmallVectorSpace",
###           "for small vector spaces directly compute an orbit" );

[ Dauer der Verarbeitung: 0.42 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 ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge