Revision 547954673cf6a06cbf60a942a8e5bccf6185ce0d authored by simpcomp on 17 October 2023, 07:13:10 UTC, committed by GitHub on 17 October 2023, 07:13:10 UTC
2 parent s d117d97 + d1d4173
Raw File
fromgroup.gi
################################################################################
##
##  simpcomp / fromgroup.gi
##
##  Compute Simplicial Complexes from given Permutation Groups.  
##
##  $Id: glprops.gi 68 2010-04-23 14:08:15Z felix $
##
################################################################################

################################################################################
##<#GAPDoc Label="SCsFromGroupExt">
## <ManSection>
## <Func Name="SCsFromGroupExt" Arg="G,n,d,objectType,cache,removeDoubleEntries,
## outfile,maxLinkSize,subset,single"/>
## <Returns>a list of simplicial complexes of type <C>SCSimplicialComplex</C> 
## upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Computes all combinatorial <Arg>d</Arg>-pseudomanifolds, <M>d=2</M> / all 
## strongly connected combinatorial <Arg>d</Arg>-pseudomanifolds, 
## <M>d \geq 3</M>, as a union of orbits of the group action of <Arg>G</Arg> 
## on <C>(d+1)</C>-tuples on the set of <Arg>n</Arg> vertices, see 
## <Cite Key="Lutz03TrigMnfFewVertVertTrans" />. The integer argument 
## <Arg>objectType</Arg> specifies, whether complexes exceeding the maximal 
## size of each vertex link for combinatorial manifolds are sorted out 
## (<C>objectType = 0</C>) or not (<C>objectType = 1</C>, in this case some 
## combinatorial pseudomanifolds won't be found, but no combinatorial manifold 
## will be excluded). A value of <C>objectType = 2</C> behaves like
## <C>objectType = 0</C> but without bistallar flips checking vertex links.
## The integer argument <Arg>cache</Arg> specifies if the 
## orbits are held in memory during the computation, a value of <C>0</C> means 
## that the orbits are discarded, trading speed for memory, any other value 
## means that they are kept, trading memory for speed. The boolean argument 
## <Arg>removeDoubleEntries</Arg> specifies whether the results are checked for 
## combinatorial isomorphism, preventing isomorphic entries. The argument 
## <Arg>outfile</Arg> specifies an output file containing all complexes found 
## by the algorithm, if <Arg>outfile</Arg> is anything else than a string, not 
## output file is generated. The argument <Arg>maxLinkSize</Arg> determines a 
## maximal link size of any output complex. If <Arg>maxLinkSize</Arg><M>=0</M> 
## or if <Arg>maxLinkSize</Arg> is anything else than an integer the argument 
## is ignored. The argument <Arg>subset</Arg> specifies a set of orbits (given 
## by a list of indices of <C>repHigh</C>) which have to be contained in any 
## output complex. If <Arg>subset</Arg> is anything else than a subset of 
## <C>matrixAllowedRows</C> the argument is ignored. If <Arg>single</Arg> is 
## set to <C>true</C>, only single orbit solutions are returned.
## <Example><![CDATA[
## gap> G:=PrimitiveGroup(8,5);
## gap> Size(G);
## gap> Transitivity(G);
## gap> list:=SCsFromGroupExt(G,8,3,1,0,true,false,0,[]);
## gap> c:=SCFromIsoSig(list[1]);
## gap> SCNeighborliness(c); 
## gap> c.F;
## gap> c.IsManifold; 
## gap> SCLibDetermineTopologicalType(SCLink(c,1));
## gap> # there are no 3-neighborly 3-manifolds with 8 vertices
## gap> list:=SCsFromGroupExt(PrimitiveGroup(8,5),8,3,0,0,true,false,0,[]); 
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCsFromGroupExt,
  function(G,n,d,objectType,cache,removeDoubleEntries,outfile,maxLinkSize,
    subset,single)
  local
  forbiddenHigh,forbiddenLow,stab,repHighForbidden,repHigh,repHighTmp,repLow,
  repHighOrbitLen,repHighOrbitLenTmp,repOrbitLen,matrix,matrixRows,matrixCols,
  matrixAllowedRows,allCount,repLowCount,repHighCount,cachedHighOrbits,
  candidateCount,max,tmp,t,orbit,pos,curRow,complex,complex_collection,
  count,maxIdxLow,maxIdxHigh,maxIdxLowReal,maxIdxHighReal,simplex,
  maxSimplexPos,simplexPos,add,simplexIdx,rl,ru,rlIdx,rowIdx,singleResults,
  element,j,i,o,name,column,s,vectorsum,curComb,stop,stopSize,foundComplex,
  colPos,curPos,lastRow,srctr,subsetValid,isoSig,isoSigs,

  # functions
  examineComplex,
  Faces,
  assembleComplex,
  updateRowVector;

  candidateCount:=0;
  
  if not IsString(outfile) then
    outfile:=false;
  else
    tmp:=SmallGeneratingSet(G);
    if tmp = [] then
      PrintTo(outfile,"G:=Group(());\nc:=[];\n\n");
    else
      PrintTo(outfile,"G:=Group(\n",SmallGeneratingSet(G),"\n);\nc:=[];\n\n");
    fi;
  fi;
  #upper boundaries for link length
  if IsInt(maxLinkSize) and maxLinkSize > 0 then
  max:=maxLinkSize;
  else 
    if objectType in [0,2] then #manifolds
      if IsInt((d-1)/2) = true then
        max:=Binomial((n-1)-Int(d/2)-1,Int(d/2))+
          Binomial((n-1)-1-(d-1)/2,(d-1)/2);
      else
        max:=Binomial((n-1)-d/2,d/2)+Binomial((n-1)-1-
          Int((d-1)/2)-1,Int((d-1)/2));
      fi;
    else #pseudomanifolds
      if d = 4 then
        max:=Binomial((n-1)-d/2,d/2)+Binomial((n-1)-1-
          Int((d-1)/2)-1,Int((d-1)/2));
      else
        max:=Binomial(n-1,d);
      fi;
    fi;
  fi;
  
  # upper boundary for length of facet list
  max:=Int(n*max/(d+1));
  complex_collection:=[];

  updateRowVector:=function(add,row)
    local i;
    if(add) then
      #add row to chosen combination
      for i in matrix[row] do
        if vectorsum[i[1]]+i[2] >= 3 then
          return false;
        fi;
      od;
      if vectorsum[repLowCount+2]+repHighOrbitLen[row] > max then
        return false;
      fi;
      for i in matrix[row] do
        vectorsum[i[1]]:=vectorsum[i[1]]+i[2];
        if vectorsum[i[1]] = 1 then
          vectorsum[repLowCount+1]:=vectorsum[repLowCount+1]+1;
        elif vectorsum[i[1]]=2 and i[2]=1 then
          vectorsum[repLowCount+1]:=vectorsum[repLowCount+1]-1;
        fi;
      od;
      vectorsum[repLowCount+2]:=vectorsum[repLowCount+2]+repHighOrbitLen[row];
    else
      #remove row from chosen combination
      for i in matrix[row] do
        if vectorsum[i[1]]=2 and i[2]=1 then
          vectorsum[repLowCount+1]:=vectorsum[repLowCount+1]+1;  
        elif vectorsum[i[1]] = 1 then
          vectorsum[repLowCount+1]:=vectorsum[repLowCount+1]-1;
        fi;
        vectorsum[i[1]]:=vectorsum[i[1]]-i[2];
      od;
      vectorsum[repLowCount+2]:=vectorsum[repLowCount+2]-repHighOrbitLen[row];        
    fi;
    return true;
  end;

  assembleComplex:=function(combination)
    local j,tmp,orbit;
    
    complex:=[];
    tmp:=[];
    for j in combination do
      #generate orbit
      if cache=1 then
        orbit:=cachedHighOrbits[j];
      else
        orbit:=Orbit(G,repHigh[j],OnSets);
      fi;      
      Append(complex,orbit);
    od;
  end;
  
  examineComplex := function(complex,complex_collection,objectType,t)
    local k,j,d,isconn,c,lk;
    c:=SCFromFacets(complex);
    if t = 0 then
      lk:=SCLinks(c,0);
    else
      lk:=[SCLink(c,1)];
    fi;
    d:=Size(complex[1])-1;
    if not SCIsConnected(c) then 
      Info(InfoSimpcomp,2,"complex not connected.");
      return false;
    fi;

    if not SCIsStronglyConnected(c) then 
      Info(InfoSimpcomp,2,"complex not strongly connected.");
      return false;
    fi;

    Info(InfoSimpcomp,2,"Testing connectedness of link(s)...");
    isconn:= List(lk,x->SCIsConnected(x)) ;
    if (objectType in [0,2] and false in isconn) or (objectType=1 and false in isconn 
        and d > 2) then
      Info(InfoSimpcomp,2,"link not connected.");
      return false;
    fi;
    if (objectType = 1 and IsInt(d/2)) or objectType in [0,2] then
      Info(InfoSimpcomp,2,"Testing vertex link...");
      if Set(List(lk,x->SCEulerCharacteristic(x))) <> [1 + (-1)^(d-1)] then
        Info(InfoSimpcomp,2,"link not valid.");
        return false;
      fi;
    fi;
    Info(InfoSimpcomp,2,"ok.");

    # test lower links only if objectType = 0 is set
    if objectType = 0 then
      return SCIsManifold(c);
    else
      return true;
    fi;
  end;
  
  Info(InfoSimpcomp,2,"Calculating for group ",G);
  
  # check if G operates on [1..n]
  if DegreeOperation(G) <> n then
    Info(InfoSimpcomp,2,"Group does not operate on ",n," points.");
    return [];
  fi;

  # check group size
  if Size(G) > Factorial(d+1)*max and objectType in [0,2] then
    Info(InfoSimpcomp,2,"Group too large for combinatorial manifolds.");
    return [];
  fi;

  # check if G operates on [1..n]
  if n < d+2 then
    Info(InfoSimpcomp,2,"There is no ",d,"-dimensional (pseudo)manifold with ",
      n," vertices.");
    return [];
  fi;

  repHigh:=[];
  repLow:=[];

  repHighOrbitLen:=[];
  repOrbitLen:=0;
  
  matrix:=[];
  matrixRows:=0;
  matrixCols:=0;
  matrixAllowedRows:=[];
  allCount:=[];
  
  cachedHighOrbits:=[];

  t:=Transitivity(G);
  if t < 1 then
    Info(InfoSimpcomp,1,"Automorphism is not transitive.");
    #return fail;
  fi;
  
  #complex
  complex:=[];
  
  count:=0;
  
  if removeDoubleEntries then
    isoSigs:=[];
  fi;

  ##############################################################################
  #MAIN
  ##############################################################################

  #number of (possible) simplices calculated
  maxIdxLow:=Binomial(n-t,d-t); #maximum number of (d-1)-vertices
  maxIdxHigh:=Binomial(n-t,d+1-t); #maximum number of d-vertices
  
  maxIdxLowReal:=Binomial(n,d);
  maxIdxHighReal:=Binomial(n,d+1);
    
  if(maxIdxLow<1) then maxIdxLow:=1; fi;
  if(maxIdxHigh<1) then maxIdxHigh:=1; fi;
  
  Info(InfoSimpcomp,2,"Simplices count low=",maxIdxLow," high=",maxIdxHigh);
  Info(InfoSimpcomp,2,"Generating low simplices count=",maxIdxLow);

  forbiddenLow:=[];
  #compute low represetative list
  maxSimplexPos:=d;
  simplex:=[1..d];
  simplex[d]:=d-1;
  for simplexIdx in [1..maxIdxLow] do
    simplexPos:=d;
    simplex[simplexPos]:=simplex[simplexPos]+1;
    if(simplex[simplexPos]>n) then
      simplexPos:=simplexPos-1;
      simplex[simplexPos]:=simplex[simplexPos]+1;
      while(simplex[simplexPos]>n or simplex[simplexPos]+
        maxSimplexPos-simplexPos>n) do
        simplexPos:=simplexPos-1;
        simplex[simplexPos]:=simplex[simplexPos]+1;
      od;
      while(simplexPos<maxSimplexPos) do
        simplex[simplexPos+1]:=simplex[simplexPos]+1;
        simplexPos:=simplexPos+1;
      od;
    fi;
    #generate orbit & look if already represented
    add:=1;
    orbit:=Orbit(G,simplex,OnSets);
    #Info(InfoSimpcomp,2,"orbit ",orbit);

    for rl in repLow do
      pos:=Position(orbit,rl);
      if(pos<>fail) then
        #Info(InfoSimpcomp,2,"discarded simplex ",simplex);
        add:=0;
        break;
      fi;
    od;
    for rl in forbiddenLow do
      pos:=Position(orbit,rl);
      if(pos<>fail) then
        #Info(InfoSimpcomp,2,"discarded simplex ",simplex);
        add:=0;
        break;
      fi;
    od;
    if(add=1) then
      # check, if pointwise stabilizer is valid (<= C2)
      stab:=Stabilizer(G,simplex,OnTuples);
      if d > 2 then
        if Size(stab) <= 2 then
          Add(repLow,ShallowCopy(simplex));
        else
          Info(InfoSimpcomp,3,"simplex ",simplex,
            " not valid, stabilizer too large.");
          Add(forbiddenLow,ShallowCopy(simplex));
        fi;
      elif d <= 2 then
        Add(repLow,ShallowCopy(simplex));
      fi;
      repOrbitLen:=repOrbitLen+Length(orbit);
    fi;

    if((simplexIdx mod 100)=0) then
      Info(InfoSimpcomp,2,"Processing (d-1)-simplex ",simplexIdx,"/",maxIdxLow,
      ", ",Length(repLow)," represenative(s) so far. simplices=",repOrbitLen,
      "/",maxIdxLowReal);
      GASMAN("collect");
    fi;
    if(repOrbitLen=maxIdxLowReal) then
      Info(InfoSimpcomp,3,"No more low orbits possible. Finished.");
      break;
    fi;
  od;
  
  repLowCount:=Length(repLow);
  Info(InfoSimpcomp,2,repLowCount," low represenative(s): ",repLow," orbits=",
    repOrbitLen,"/",maxIdxLowReal);
  
  allCount:=[];
  allCount[1]:=ListWithIdenticalEntries(repLowCount,0);
  allCount[2]:=ListWithIdenticalEntries(repLowCount,0);
    
  repHigh:=[];
  forbiddenHigh:=[];
  Info(InfoSimpcomp,2,"Generating high simplices count=",maxIdxHigh,
    " and matrix...");
  #compute high representative list & matrix
  repOrbitLen:=0;
  maxSimplexPos:=d+1;
  simplex:=[1..d+1];
  simplex[d+1]:=d;
  for simplexIdx in [1..maxIdxHigh] do
    simplexPos:=d+1;
    simplex[simplexPos]:=simplex[simplexPos]+1;
    if(simplex[simplexPos]>n) then
      simplexPos:=simplexPos-1;
      simplex[simplexPos]:=simplex[simplexPos]+1;
      while(simplex[simplexPos]>n or simplex[simplexPos]+maxSimplexPos-
          simplexPos>n) do
        simplexPos:=simplexPos-1;
        simplex[simplexPos]:=simplex[simplexPos]+1;
      od;
    
      while(simplexPos<maxSimplexPos) do
        simplex[simplexPos+1]:=simplex[simplexPos]+1;
        simplexPos:=simplexPos+1;
      od;
    fi;
    #Info(InfoSimpcomp,2,"simplex ",simplex);
  
    #generate orbit & check whether already represented
    add:=1;
    orbit:=Orbit(G,simplex,OnSets);
    for ru in repHigh do
      pos:=Position(orbit,ru);
      if(pos<>fail) then
        #Info(InfoSimpcomp,2,"discarded simplex ",simplex);
        add:=0;
        break;
      fi;
    od;
    for ru in forbiddenHigh do
      pos:=Position(orbit,ru);
      if(pos<>fail) then
        #Info(InfoSimpcomp,2,"discarded simplex ",simplex);
        add:=0;
        break;
      fi;
    od;
    if(add=1 and d>2) then
      # check, if pointwise stabilizer is trivial
      stab:=Stabilizer(G,simplex,OnTuples);
      if Size(stab) = 1 then
        Add(repHigh,ShallowCopy(simplex));
        Add(repHighOrbitLen,Length(orbit));
      else
        Add(forbiddenHigh,ShallowCopy(simplex));
        Info(InfoSimpcomp,3,"simplex ",simplex,
          " not valid, stabilizer not trivial.");
      fi;
      repOrbitLen:=repOrbitLen+Length(orbit);
    elif(add=1 and d <= 2) then
      Add(repHigh,ShallowCopy(simplex));
      Add(repHighOrbitLen,Length(orbit));
    fi;

    if((simplexIdx mod 100)=0) then
      Info(InfoSimpcomp,2,"Processing d-simplex ",simplexIdx,"/",maxIdxHigh,
        ", ",Length(repHigh)," represenative(s) so far. simplices="
        ,repOrbitLen,"/",maxIdxHighReal);
      GASMAN("collect");
    fi;
    
    if(repOrbitLen=maxIdxHighReal) then
      Info(InfoSimpcomp,3,"No more high orbits possible. Finished.");
      break;
    fi;
    od;
  
  repHighCount:=Length(repHigh);
  Info(InfoSimpcomp,2,repHighCount, " high represenative(s): ",repHigh,
     " orbits=",repOrbitLen,"/",maxIdxHighReal);
  
  repHighForbidden:=[];
  
  #check pseudomanifold property & generate matrix
  Info(InfoSimpcomp,2,"Calculating matrix...");
  cachedHighOrbits:=[];
  for simplexIdx in [1..repHighCount] do
    simplex:=repHigh[simplexIdx];
    if(simplexIdx mod 100 = 1) then Info(InfoSimpcomp,2,
      "Calculating incidence matrix row ",simplexIdx,"/",repHighCount); fi;
    add:=1;
    curRow:=[];
    orbit:=Orbit(G,simplex,OnSets);
    
    if(Size(orbit) > max) then
      add:=0;
    fi;
    
    if(add=1) then
      #calculate how often every (d-1)-simplex is contained in this d-simplex
      for rlIdx in [1..repLowCount] do
        count:=0;
        for element in orbit do
          if(IsSubset(element,repLow[rlIdx])) then
            count:=count+1;
            if(count>2) then
              break;
            fi;
          fi;  
        od;
    
        if(count>2) then
          add:=0;
          break;
        elif(count>0) then
          curRow[rlIdx]:=count;
        fi;
      od;
    fi;
    
    if(add=1) then
      tmp:=[];
      for i in [1..repLowCount] do
        if(IsBound(curRow[i])) then     
          allCount[curRow[i]][i]:=allCount[curRow[i]][i]+1;
          Add(tmp,[i,curRow[i]]);
        fi;
      od;
      #save matrix row
      Add(matrix,ShallowCopy(tmp));
      if(cache=1) then
        Add(cachedHighOrbits,ShallowCopy(orbit));
      fi;
    else
        Add(repHighForbidden,simplex);
    fi;
  
  od;

  repHighTmp:=[];
  repHighOrbitLenTmp:=[];
  for i in [1..Length(repHigh)] do  
    if(repHigh[i] in repHighForbidden) then
      continue;
    fi;
    
    Add(repHighTmp,repHigh[i]);
    Add(repHighOrbitLenTmp,repHighOrbitLen[i]);
  od;
  
  repHigh:=repHighTmp;
  repHighOrbitLen:=repHighOrbitLenTmp;
  repHighCount:=Length(repHigh);
  Info(InfoSimpcomp,2,repHighCount, " valid high represenative(s).");
  Info(InfoSimpcomp,2,"Matrix calculation done.");

  #setup matrix  
  matrixRows:=Length(matrix);
  matrixCols:=repLowCount;
  matrixAllowedRows:=[1..matrixRows];

  if(matrixRows=0 or matrixCols=0) then
    Info(InfoSimpcomp,2,"Nothing to compute.");
    return [];
  fi;

  #get single results
  singleResults:=[];
  
  for rowIdx in matrixAllowedRows do
      if(ForAll(matrix[rowIdx],x->x[2]=2)) then
      Add(singleResults,rowIdx);
    fi;
  od;
  Info(InfoSimpcomp,2,"Got ",Length(singleResults)," single results.");

  # test subset
  subsetValid:=true;
  srctr:=0;
  if not IsList(subset) or subset = [] then
    subsetValid := false;
  else
    SCIntFunc.DeepSortList(subset);
    subset:=Set(subset);
    for i in [1..Size(subset)] do
      pos:=Position(repHigh,subset[i]);
      if pos = fail or not pos in matrixAllowedRows then
        subsetValid:=false;
        break;
      fi;
      if pos in singleResults then
        srctr:=srctr+1;
      fi;
    od;
  fi;
  if srctr > 1 then
    return [];
  fi;
  if subsetValid then
    subset:=List(subset,x->Position(repHigh,x));
  fi;
  
  if Size(subset) = 1 and srctr = 1 then
    #generate complex
    complex:=Orbit(G,repHigh[subset[1]],OnSets);

    if Union(complex) = [1..n] then
      tmp:=[[repHigh[subset[1]],Size(complex)]];
      if(examineComplex(complex,complex_collection,objectType,t)) then
        complex:=SCFromFacets(complex);
        # we cannot perform iso sig computations for 2-pseudomanifolds
        if removeDoubleEntries and not (d=2 and objectType=1) then
          Info(InfoSimpcomp,2,"Testing if complex is equivalent ",
             "to previous one...");
          isoSig:=SCExportIsoSig(complex);
          if isoSig = fail then
            return fail;
          fi;
          if isoSig in isoSigs then
            Info(InfoSimpcomp,2,"yes.");
          else
            Info(InfoSimpcomp,2,"no.");
            Add(isoSigs,isoSig);
            candidateCount:=candidateCount+1;
            if outfile <> false then
              AppendTo(outfile,"Add(c,SCFromGenerators(G,",[repHigh[subset[1]]],"));\n");
            fi;
          fi;
        else
          if not HasName(G) then
            SetName(G,StructureDescription(G));
          fi;
          name:=Concatenation("Complex ",String(Size(complex_collection)+1),
            " of ",Name(G)," (single orbit)");
          SCRename(complex,name);
          if outfile <> false then
            AppendTo(outfile,"Add(c,SCFromGenerators(G,",
              [repHigh[subset[1]]],"));\n");
          fi;
          Add(complex_collection,complex);
          candidateCount:=candidateCount+1;
        fi;
      fi;
      if removeDoubleEntries then
        return isoSigs;
      else
        return complex_collection;
      fi;
    fi;
  elif not subsetValid then
    for i in [1..Length(singleResults)] do
      Info(InfoSimpcomp,2,"Single result ",i,"/",Length(singleResults),
        " :  ",matrix[singleResults[i]],"(",singleResults[i],")");
      for j in matrix[singleResults[i]] do
         allCount[2][j[1]]:=allCount[2][j[1]]-1;
      od;
      
      #generate complex
      complex:=Orbit(G,repHigh[singleResults[i]],OnSets);

      if Union(complex) = [1..n] then
        tmp:=[[repHigh[singleResults[i]],Size(complex)]];
        if(examineComplex(complex,complex_collection,objectType,1)) then
          complex:=SCFromFacets(complex);
          # we cannot perform iso sig computations for 2-pseudomanifolds
          if removeDoubleEntries and not (d=2 and objectType=1) then
            Info(InfoSimpcomp,2,"Testing if complex is equivalent ",
              "to previous one...");
            isoSig:=SCExportIsoSig(complex);
            if isoSig = fail then
              return fail;
            fi;
            if isoSig in isoSigs then
              Info(InfoSimpcomp,2,"yes.");
            else
              Info(InfoSimpcomp,2,"no.");
              Add(isoSigs,isoSig);
              candidateCount:=candidateCount+1;
              if outfile <> false then
                AppendTo(outfile,"Add(c,SCFromGenerators(G,",[repHigh[subset[1]]],"));\n");
              fi;
            fi;
          else
            if not HasName(G) then
              SetName(G,StructureDescription(G));
            fi;
            name:=Concatenation("Complex ",String(Size(complex_collection)+1),
              " of ",Name(G)," (single orbit)");
            SCRename(complex,name);
            if outfile <> false then
              AppendTo(outfile,"Add(c,SCFromGenerators(G,",
                [repHigh[subset[1]]],"));\n");
            fi;
            Add(complex_collection,complex);
            candidateCount:=candidateCount+1;
          fi;
        fi;
      fi;
    od;
  fi;
    
  # note to self: here we could stop and just return single results  
    
  SubtractSet(matrixAllowedRows,singleResults);
  matrixRows:=Length(matrixAllowedRows);
  if(matrixRows<1) or single = true then
  #if(matrixRows<1) then
    Info(InfoSimpcomp,2,"No more computations needed (no more rows).");
    if removeDoubleEntries then
      return isoSigs;
    else
      return complex_collection;
    fi;
  fi;
    
  Info(InfoSimpcomp,2,"Incidence matrix:\n",matrix);
  
  if(matrixRows<1 or matrixCols<1) then
    Info(InfoSimpcomp,2,"No more computations needed (no more rows/cols).");
    if removeDoubleEntries then
      return isoSigs;
    else
      return complex_collection;
    fi;
  fi;
    
  GASMAN("collect");
  Info(InfoSimpcomp,2,"Got ",matrixRows," rows, ",matrixCols,
    " cols in matrix, computing row combinations...");
  
  #init column vector
  column:=[];
  for s in [1..repLowCount] do
    column[s]:=[];
  od;

  for i in [1..matrixRows] do
    Add(column[matrix[matrixAllowedRows[i]][1][1]],matrixAllowedRows[i]);
  od;
  
  if matrixRows <= 1 then
    stop:=1;
  else
    stop:=0;
  fi;
  
  # vectorsum{[1..repLowCount]} = current combination of rows
  # vectorsum[repLowCount+1] = number of 1-entries in combination
  # vectorsum[repLowCount+2] = size of complex (facets)
  vectorsum:=ListWithIdenticalEntries(repLowCount+2,0);

  ##########################
  ### SETUP BACKTRACKING ###
  ##########################
  
  if subsetValid then
    # case: "subset" is valid
    curComb:=[];
    for i in subset do
      Add(curComb,i);
      if (updateRowVector(true,i)=false) then
        Info(InfoSimpcomp,2,"argument subset valid but not part ",
          "of a candidate complex.");
        return [];
      fi;
    od;
    if not matrixAllowedRows[1] in subset then
      i:=1;
      while (updateRowVector(true,matrixAllowedRows[i])=false) do
        i:=i+1;
        if i in subset then
          break;
        fi;
      od;
      if not i in subset then
        Add(curComb,matrixAllowedRows[i]);
        colPos:=matrix[matrixAllowedRows[i]][1][1];
        i:=Position(column[colPos],matrixAllowedRows[i])+1;
        while i <= Size(column[colPos]) and column[colPos][i] in subset do
          i:=i+1;
        od;
        curPos:=i;
      else
        colPos:=matrix[subset[1]][1][1];
        curPos:=Position(column[colPos],subset[1])+1;
      fi;
    else
      colPos:=matrix[subset[1]][1][1];
      curPos:=Position(column[colPos],subset[1])+1;
    fi;    
  else
    # case: "subset" is invalid or empty
    i:=1;
    while (updateRowVector(true,matrixAllowedRows[i])=false) do
      i:=i+1;
    od;
    curComb:=[matrixAllowedRows[i]];
    colPos:=matrix[matrixAllowedRows[i]][1][1];
    curPos:=Position(column[colPos],matrixAllowedRows[i])+1;
  fi;
  foundComplex:=0;
  # column: column[i] contains all row numbers x with row[x][i] <> 0 and 
  # row[x][j] = 0 for all j < i
  # colPos: position of first non zero entry of current row
  # curPos: next row where first non zero entry is at the same 
  # position as in current row
  while stop = 0 do
    foundComplex:=0;
    while foundComplex = 0 and colPos <= repLowCount do
      # if there is a row left where the first non zero entry is at the 
      # same column as colPos
      if curPos > Length(column[colPos]) then
        if vectorsum[colPos] in [0,2] then
          colPos:=colPos+1;
          if subsetValid then
            i:=1;
            while colPos <= Size(column) and i <= Size(column[colPos]) and 
              column[colPos][i] in subset do
              i:=i+1;
            od;
            curPos:=i;
          else
            curPos:=1;
          fi;
        else
          if subsetValid and Size(curComb) = Size(subset) then
            stop:=1;
            break;
          fi;
          lastRow:=curComb[Length(curComb)];
          updateRowVector(false,lastRow);
          Unbind(curComb[Length(curComb)]);
          if curComb = [] and colPos > repLowCount then
            stop:=1;
          fi;
          colPos:=matrix[lastRow][1][1];
          if subsetValid then
            i:=Position(column[colPos],lastRow)+1;
            while i <= Size(column[colPos]) and column[colPos][i] in subset do
              i:=i+1;
            od;
            curPos:=i;
          else
            curPos:=Position(column[colPos],lastRow)+1;
          fi;
        fi;
        continue;
      fi;
      # column colPos is already mapped -> next step
      if vectorsum[colPos] = 2 then
        colPos:=colPos+1;
        if subsetValid then
          i:=1;
          while colPos <= Size(column) and i <= Size(column[colPos]) and 
            column[colPos][i] in subset do
            i:=i+1;
          od;
          curPos:=i;
        else
          curPos:=1;
        fi;
      # if this is the last row where the first non zero entry is at the 
      # same column as colPos and orbit cannot be closed anymore -> next step
      elif vectorsum[colPos] = 0 and matrix[column[colPos][curPos]][1][2] = 1 
         and curPos = Length(column[colPos]) then
        colPos:=colPos+1;
        if subsetValid then
          i:=1;
          while colPos <= Size(column) and i <= Size(column[colPos]) 
            and column[colPos][i] in subset do
            i:=i+1;
          od;
          curPos:=i;
        else
          curPos:=1;
        fi;
      elif subsetValid and column[colPos][curPos] in subset then
        curPos:=curPos+1;
      else 
        foundComplex:=1;
        stopSize:=0;
        if(updateRowVector(true,column[colPos][curPos])=false) then
          stopSize:=1;
        fi;

        if stopSize = 0 then
          Add(curComb,column[colPos][curPos]);

          if vectorsum[repLowCount+1] = 0 then
            assembleComplex(curComb);
            if (Union(complex) = [1..n] and 
              examineComplex(complex,complex_collection,objectType,1)) then
              complex:=SCFromFacets(complex);
              # we cannot perform iso sig computations for 2-pseudomanifolds
              if removeDoubleEntries and not (d=2 and objectType=1) then
                Info(InfoSimpcomp,2,"Testing if complex is equivalent ",
                  "to previous one...");
                isoSig:=SCExportIsoSig(complex);
                if isoSig = fail then
                  return fail;
                fi;
                if isoSig in isoSigs then
                  Info(InfoSimpcomp,2,"yes.");
                else
                  Info(InfoSimpcomp,2,"no.");
                  Info(InfoSimpcomp,2,"Found candidate.");
                  Add(isoSigs,isoSig);
                  candidateCount:=candidateCount+1;
                  if outfile <> false then
                    AppendTo(outfile,"Add(c,SCFromGenerators(G,",repHigh{curComb},"));\n");
                  fi;
                fi;
              else
                if not HasName(G) then
                  SetName(G,StructureDescription(G));
                fi;
                name:=Concatenation("Complex ",
                  String(Size(complex_collection)+1)," of ",Name(G),
                  " (multiple orbits)");
                SCRename(complex,name);
                if outfile <> false then
                  AppendTo(outfile,"Add(c,SCFromGenerators(G,",
                    repHigh{curComb},"));\n");
                fi;
                Info(InfoSimpcomp,2,"Found candidate.");
                Add(complex_collection,complex);
                candidateCount:=candidateCount+1;
              fi;
            fi;
          else
            foundComplex:=0;
          fi;
        else
          foundComplex:=0;
          #updateRowVector(false,column[colPos][curPos]);  
        fi;
        if subsetValid then
          curPos:=curPos+1;
          while curPos <= Length(column[colPos]) and 
            column[colPos][curPos] in subset do
            curPos:=curPos+1;
          od;
        else
          curPos:=curPos+1;
        fi;
      fi;
    od;
    if stop = 1 then break; fi; 
    if curComb = [] and colPos > repLowCount then
      stop:=1;
      break;
    else
      if subsetValid and Size(curComb) = Size(subset) then
        stop:=1;
        break;
      fi;
      lastRow:=curComb[Length(curComb)];
      updateRowVector(false,lastRow);
      Unbind(curComb[Length(curComb)]);
      colPos:=matrix[lastRow][1][1];
      if subsetValid then
        i:=Position(column[colPos],lastRow)+1;
        while i <= Size(column[colPos]) and column[colPos][i] in subset do
          i:=i+1;
        od;
        curPos:=i;
      else
        curPos:=Position(column[colPos],lastRow)+1;
      fi;
    fi;
  od; 


  Info(InfoSimpcomp,2,"All done, ",candidateCount," candidate(s).");
  if removeDoubleEntries then
    return isoSigs;
  else
    return complex_collection;
  fi;
end);

################################################################################
##<#GAPDoc Label="SCsFromGroupByTransitivity">
## <ManSection>
## <Func Name="SCsFromGroupByTransitivity" Arg="n,d,k,maniflag,computeAutGroup,
## removeDoubleEntries"/>
## <Returns>a list of simplicial complexes of type <C>SCSimplicialComplex</C> 
## upon success, <K>fail</K> otherwise.</Returns>
## <Description>
## Computes all combinatorial <Arg>d</Arg>-pseudomanifolds, <M>d = 2</M> / all 
## strongly connected combinatorial <Arg>d</Arg>-pseudomanifolds, 
## <M>d \geq 3</M>, as union of orbits of group actions for all 
## <Arg>k</Arg>-transitive groups on <C>(d+1)</C>-tuples on the set of 
## <Arg>n</Arg> vertices, see <Cite Key="Lutz03TrigMnfFewVertVertTrans" />. 
## The boolean argument <Arg>maniflag</Arg> specifies, whether the resulting 
## complexes should be listed separately by combinatorial manifolds, 
## combinatorial pseudomanifolds and complexes where the verification that the 
## object is at least a combinatorial pseudomanifold failed. The boolean 
## argument <Arg>computeAutGroup</Arg> specifies whether or not the real 
## automorphism group should be computed (note that a priori the generating 
## group is just a subgroup of the automorphism group). The boolean argument 
## <Arg>removeDoubleEntries</Arg> specifies whether the results are checked 
## for combinatorial isomorphism, preventing isomorphic entries. Internally 
## calls <Ref Func="SCsFromGroupExt" /> for every group.
## <Example><![CDATA[
## gap> list:=SCsFromGroupByTransitivity(8,3,2,true,true,true);
## ]]></Example>
## </Description>
## </ManSection>
##<#/GAPDoc>
################################################################################
InstallGlobalFunction(SCsFromGroupByTransitivity,
  function(n,d,k,maniflag,computeAutGroup,removeDoubleEntries)
  local coll, i, G, tmp, dim, verts, sum, Gcollection, g, gg, retList, 
    retListTmp, c, lk, pm, m, flag, ctr, cc, j, l, warn, isoSigs;
  
  if((not IsPosInt(n) and not (IsList(n) and ForAll(n,x->IsPosInt(x)))) or 
    (not IsPosInt(d) and not (IsList(d) and ForAll(d,x->IsPosInt(x)))) or 
    not IsPosInt(k) or not IsBool(maniflag) or not IsBool(computeAutGroup) 
    or not IsBool(removeDoubleEntries)) then
    Info(InfoSimpcomp,1,"SCsFromGroupCheckByTransitivity: 'n' and 'd' must ",
      "be positive integers or a list of positive integers, 'k' must be a ",
      "positive integer, 'maniflag', computeAutGroup' and ",
      "'removeDoubleEntries' must be boolean");
    return fail;
  fi;
  
  if IsInt(n) then
    n:=[n];
  fi;
  if IsInt(d) then
    d:=[d];
  fi;

  if removeDoubleEntries then
    isoSigs:=[];
  fi;

  # detect group list, do not check groups with k-transitive subgroups
  Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: Building list of groups...");
  Gcollection:=[];
  if k > 1 then
    for verts in n do
      Gcollection[verts]:=[];
      for i in [1..NrPrimitiveGroups(verts)] do
        g:=PrimitiveGroup(verts,i);
        if Transitivity(g) <> k then
          continue;
        fi;
        flag := 0;
        for gg in Gcollection[verts] do
          if IsSubgroup(g,gg) then
            flag:=1;
            break;
          fi;
        od;
        if flag = 0 then
          Add(Gcollection[verts],g);
        fi;
      od;
    od;
  elif k = 1 then
    for verts in n do
      Gcollection[verts]:=[];
      for i in [1..NrTransitiveGroups(verts)] do
        g:=TransitiveGroup(verts,i);
        flag := 0;
        for gg in Gcollection[verts] do
          if IsSubgroup(g,gg) then
            flag:=1;
            break;
          fi;
        od;
        if flag = 0 then
          Add(Gcollection[verts],g);
        fi;
      od;
    od;
  else
    Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: third argument 'k' must ",
      "be a positive integer.");
    return fail;
  fi;
  sum:=0;
  for i in Gcollection do
    sum:=sum+Size(i);
  od;
  Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ...",sum," groups found.");
  for i in Gcollection do
    if i <> [] then
      Info(InfoSimpcomp,1,"degree ",Position(Gcollection,i),": ",i);
    fi;
  od;
  
  coll:=[];
  retList:=[];
  if maniflag then
    retList[1]:=[];
    retList[2]:=[];
    retList[3]:=[];
  fi;
  warn:=false;
  for dim in d do
    if dim=2 and removeDoubleEntries=true then
      warn:=true;
    fi;
    Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: Processing dimension ",
      dim,".");
    coll[dim]:=[];
    for verts in n do
      Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: Processing degree ",
        verts,".");
      sum:=0;
      retListTmp:=[];
      if maniflag then
        retListTmp[1]:=[];
        retListTmp[2]:=[];
        retListTmp[3]:=[];
      fi;
      for G in Gcollection[verts] do
        tmp:=SCsFromGroupExt(G,verts,dim,1,0,removeDoubleEntries,false,0,[],false);
        if removeDoubleEntries then
          for i in [1..Size(tmp)] do
            if tmp[i] in isoSigs then
              Unbind(tmp[i]);
            else
              Add(isoSigs,tmp[i]);
            fi;
          od;
          tmp:=Compacted(tmp);
        fi;
        sum:=sum+1;
        Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ",sum," / ",
          Size(Gcollection[verts])," groups calculated, found ",Size(tmp),
          " complexes.");
        if computeAutGroup then
          Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: Calculating ",
            Size(tmp)," automorphism and homology groups...");
          cc:=0;
          for i in [1..Size(tmp)] do
            if removeDoubleEntries then
              tmp[i]:=SCFromIsoSig(tmp[i]);
            fi;
            cc:=cc+1;
            SCAutomorphismGroup(tmp[i]);
            SCHomology(tmp[i]);
            SCGenerators(tmp[i]);
            Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ",cc," / ",
              Size(tmp)," automorphism groups calculated.");
          od;
          Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ...all ",
            "automorphism groups calculated for group ",sum," / ",
            Size(Gcollection[verts]),".");
        fi;
        if maniflag then
          for c in tmp do
            lk:=SCLink(c,1);
            pm:=SCIsManifold(lk);
            if pm = fail then
              Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: Could not ",
                "determine whether candidate is combinatorial pseudomanifold ",
                "or not");
              Add(retListTmp[3],c);
            fi;
            if pm then
              m:=SCIsManifold(c);
              if m then
                Add(retListTmp[1],c);
              elif not m or m = fail then
                Add(retListTmp[2],c);
              fi;
            elif not pm then
              Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: Combinatorial ",
                "pseudomanifold property not fulfilled, discarding complex.");
            fi;
          od;
        else
          Append(retListTmp,tmp);
        fi;
      od;
      
    
      if maniflag then
        Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ...done dim = ",dim,
          ", deg =  ",verts,", ",Size(retListTmp[1])," manifolds, ",
          Size(retListTmp[2])," pseudomanifolds, ",Size(retListTmp[3]),
          " candidates found.");
      else
        Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ...done dim = ",
          dim,", deg =  ",verts,", ",Size(retListTmp)," candidates found.");
      fi;
    od;
    Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: ...done dim = ",dim,".");
  od;
  
  if warn then
    Info(InfoSimpcomp,1,"SCsFromGroupByTransitivity: can not remove double ",
      "entries for pp-surfaces.");
  fi;
  return retList;
end);

back to top