Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /usr/share/gap/doc/ref/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : //usr/share/gap/doc/ref/chap87.txt

  
  87 More about Stabilizer Chains
  
  This  chapter  contains  some  rather  technical complements to the material
  handled in the chapters 42 and 43.
  
  
  87.1 Generalized Conjugation Technique
  
  The  command  ConjugateGroup(  G,  p  )  (see ConjugateGroup (39.2-6)) for a
  permutation  group  G  with  stabilizer  chain equips its result also with a
  stabilizer  chain,  namely with the chain of G conjugate by p. Conjugating a
  stabilizer  chain  by  a  permutation p means replacing all the points which
  appear  in  the orbit components by their images under p and replacing every
  permutation  g  which  appears  in  a labels or transversal component by its
  conjugate  g^p. The conjugate g^p acts on the mapped points exactly as g did
  on the original points, i.e., (pnt.p). g^p = (pnt.g).p. Since the entries in
  the  translabels components are integers pointing to positions of the labels
  list, the translabels lists just have to be permuted by p for the conjugated
  stabilizer.  Then  generators  is  reconstructed  as labels{ genlabels } and
  transversal{ orbit } as labels{ translabels{ orbit } }.
  
  This conjugation technique can be generalized. Instead of mapping points and
  permutations  under the same permutation p, it is sometimes desirable (e.g.,
  in the context of permutation group homomorphisms) to map the points with an
  arbitrary mapping map and the permutations with a homomorphism hom such that
  the   compatibility  of  the  actions  is  still  valid:  map(pnt).hom(g)  =
  map(pnt.g).  (Of  course the ordinary conjugation is a special case of this,
  with map(pnt) = pnt.p and hom(g) = g^p.)
  
  In the generalized case, the conjugated chain need not be a stabilizer chain
  for  the image of hom, since the preimage of the stabilizer of map(b) (where
  b  is  a  base  point) need not fix b, but only fixes the preimage map^{-1}(
  map(b)  ) setwise. Therefore the method can be applied only to one level and
  the next stabilizer must be computed explicitly. But if map is injective, we
  have  map(b).hom(g) = map(b) if and only if b.g = b, and if this holds, then
  g  =  w(g_1,  ...,  g_n)  is  a  word in the generators g_1, ..., g_n of the
  stabilizer  of b  and  hom(g)  =^*  w(  hom(g_1),  ..., hom(g_n) ) is in the
  conjugated  stabilizer.  If,  more  generally,  hom  is a right inverse to a
  homomorphism  φ (i.e., φ(hom(g)) = g for all g), equality * holds modulo the
  kernel  of  φ;  in  this  case  the conjugated chain can be made into a real
  stabilizer  chain  by extending each level with the generators of the kernel
  and  appending  a  proper  stabilizer  chain of the kernel at the end. These
  special   cases   will   occur  in  the  algorithms  for  permutation  group
  homomorphisms (see 40).
  
  To conjugate the points (i.e., orbit) and permutations (i.e., labels) of the
  Schreier  tree,  a loop is set up over the orbit list constructed during the
  orbit  algorithm,  and for each vertex b with unique edge a(l)b ending at b,
  the label l is mapped with hom and b with map. We assume that the orbit list
  was built w.r.t. a certain ordering < of the labels, where l' < l means that
  every  point  in  the  orbit was mapped with l' before it was mapped with l.
  This  shape of the orbit list is guaranteed if the Schreier tree is extended
  only  by  AddGeneratorsExtendSchreierTree  (43.11-10),  and  it is then also
  guaranteed  for  the  conjugated  Schreier tree. (The ordering of the labels
  cannot be read from the Schreier tree, however.)
  
  In  the  generalized case, it can happen that the edge a(l)b bears a label l
  whose  image  is  old,  i.e., equal to the image of an earlier label l' < l.
  Because  of  the  compatibility  of  the  actions  we  then  have  map(b)  =
  map(a).hom(l)^{-1}  =  map(a).hom(l')^{-1}  =  map(a{l'}^{-1}), so map(b) is
  already  equal  to the image of the vertex a{l'}^{-1}. This vertex must have
  been  encountered  before  b  = al^{-1} because l' < l. We conclude that the
  image  of  a  label  can  be  old  only  if  the  vertex  at  the end of the
  corresponding edge has an old image, too, but then it need not be conjugated
  at  all.  A  similar  remark  applies  to  labels which map under hom to the
  identity.
  
  
  87.2 The General Backtrack Algorithm with Ordered Partitions
  
  Section  43.12  describes  the  basic  functions for a backtrack search. The
  purpose  of  this section is to document how the general backtrack algorithm
  is  implemented  in  GAP  and  which parts you have to modify if you want to
  write your own backtrack routines.
  
  
  87.2-1 Internal representation of ordered partitions
  
  GAP  represents  an  ordered  partition  as  a  record  with  the  following
  components.
  
  points
        a  list of all points contained in the partition, such that the points
        of each cell from lie consecutively,
  
  cellno
        a  list  whose  ith entry is the number of the cell which contains the
        point i,
  
  firsts
        a  list such that points[firsts[j]] is the first point in points which
        is in cell j,
  
  lengths
        a list of the cell lengths.
  
  Some  of  the information is redundant, e.g., the lengths could also be read
  off the firsts list, but since this need not be increasing, it would require
  some  searching. Similar for cellno, which could be replaced by a systematic
  search  of  points, keeping track of what cell is currently being traversed.
  With  the  above  components,  the mth cell of a partition P is expressed as
  P.points{  [  P.firsts[m]  ..  P.firsts[m]  + P.lengths[m] - 1 ] }. The most
  important operations, however, to be performed upon P are the splitting of a
  cell  and the reuniting of the two parts. Following the strategy of J. Leon,
  this is done as follows:
  
  (1)
        The  points  which  make up the cell that is to be split are sorted so
        that  the  ones  that  remain inside occupy positions [ P.firsts[m] ..
        last ] in the list P.points (for a suitable value of last).
  
  (2)
        The points at positions [ last + 1 .. P.firsts[m] + P.lengths[m] - 1 ]
        will  form  the additional cell. For this new cell requires additional
        entries are added to the lists P.firsts (namely, last+1) and P.lengths
        (namely, P.firsts[m] + P.lengths[m] - last - 1).
  
  (3)
        The  entries  of  the  sublist  P.cellno{  [  last+1  .. P.firsts[m] +
        P.lengths[m]-1 ] } must be set to the number of the new cell.
  
  (4)
        The entry P.lengths[m] must be reduced to last - P.firsts[m] + 1.
  
  Then  reuniting  the  two  cells  requires only the reversal of steps 2 to 4
  above. The list P.points need not be rearranged.
  
  
  87.2-2 Functions for setting up an R-base
  
  This  subsection  explains some GAP functions which are local to the library
  file lib/stbcbckt.gi which contains the code for backtracking in permutation
  groups. They are mentioned here because you might find them helpful when you
  want  to  implement  you  own  backtracking  function based on the partition
  concept.  An  important  argument  to most of the functions is the R-base R,
  which  you  should regard as a black box. We will tell you how to set it up,
  how to maintain it and where to pass it as argument, but it is not necessary
  for you to know its internal representation. However, if you insist to learn
  the whole story: Here are the record components from which an R-base is made
  up:
  
  domain
        the set Ω on which the group G operates
  
  base
        the sequence (a_1, ..., a_r) of base points
  
  partition
        an ordered partition, initially Π_0, this will be refined to Π_1, ...,
        Π_r during the backtrack algorithm
  
  where
        a list such that a_i lies in cell number where[i] of Π_i
  
  rfm
        a  list  whose  ith  entry  is a list of refinements which take Σ_i to
        Σ_{i+1}; the structure of a refinement is described below
  
  chain
        a (copy of a) stabilizer chain for G (not if G is a symmetric group)
  
  fix
        only  if  G  is  a  symmetric  group:  a  list  whose i entry contains
        Fixcells( Π_i )
  
  level
        initially  equal  to  chain,  this  will  be changed to chains for the
        stabilizers  G_{a_1  ...  a_i}  for i = 1, ..., r during the backtrack
        algorithm;  if G is a symmetric group, only the number of moved points
        is stored for each stabilizer
  
  lev
        a  list  whose  ith  entry  remembers  the  level entry for G_{a_1 ...
        a_{i-1}}
  
  level2, lev2
        a  similar  construction  for  a  second  group  (used in intersection
        calculations),  false  otherwise. This second group H activated if the
        R-base is constructed as EmptyRBase( [ G, H ], Ω, Π_0 ) (if G = H, GAP
        sets level2 = true instead).
  
  nextLevel
        this is described below
  
  As  our  guiding  example,  we  present  code  for  the function Centralizer
  (35.4-4)  which  calculates  the centralizer of an element g in the group G.
  (The real code is more general and has a few more subtleties.)
  
  
    Pi_0 := TrivialPartition( omega );
    R := EmptyRBase( G, omega, Pi_0 );
    R.nextLevel := function( Pi, rbase )
    local  fix, p, q, where;
    NextRBasePoint( Pi, rbase );
    fix := Fixcells( Pi );
    for p  in fix  do
      q := p ^ g;
      where := IsolatePoint( Pi, q );
      if where <> false  then
        Add( fix, q );
        ProcessFixpoint( R, q );
        AddRefinement( R, "Centralizer", [ Pi.cellno[ p ], q, where ] );
        if Pi.lengths[ where ] = 1  then
          p := FixpointCellNo( Pi, where );
          ProcessFixpoint( R, p );
          AddRefinement( R, "ProcessFixpoint", [ p, where ] );
        fi;
      fi;
    od;
    end;
    
    return PartitionBacktrack(
      G,
      c -> g ^ c = g,
      false,
      R,
      [ Pi_0, g ],
      L, R );
  
  
  The list numbers below refer to the line numbers of the code above.
  
  1.
        omega  is  the set on which G acts and Pi_0 is the first member of the
        decreasing  sequence  of  partitions mentioned in 43.12. We set Pi_0 =
        omega, which is constructed as TrivialPartition( omega ), but we could
        have  started with a finer partition, e.g., into unions of g-cycles of
        the same length.
  
  2.
        This statement sets up the R-base in the variable R.
  
  3.-21.
        These  lines define a function R.nextLevel which is called whenever an
        additional  member  in  the sequence Pi_0 ≥ Π_1 ≥ ... of partitions is
        needed.  If  Π_i  does not yet contain enough base points in one-point
        cells,  GAP  will  call  R.nextLevel( Π_i, R ), and this function will
        choose  a  new  base  point  a_{i+1},  refine  Π_i to Π_{i+1} (thereby
        changing the first argument) and store all necessary information in R.
  
  5.
        This statement selects a new base point a_{i+1}, which is not yet in a
        one-point  cell of Π and still moved by the stabilizer G_{a_1 ... a_i}
        of  the  earlier  base  points.  If  certain points of omega should be
        preferred  as  base point (e.g., because they belong to long cycles of
        g),  a list of points starting with the most wanted ones, can be given
        as  an  optional  third  argument to NextRBasePoint (actually, this is
        done in the real code for Centralizer (35.4-4)).
  
  6.
        Fixcells(  Π  )  returns  the  list  of points in one-point cells of Π
        (ordered as the cells are ordered in Π).
  
  7.
        For every point p ∈ fix, if we know the image p^g under c ∈ C_G(e), we
        also  know  (  p^g )^c = ( p^c )^g. We therefore want to isolate these
        extra points in Π.
  
  9.
        This  statement  puts point q in a cell of its own, returning in where
        the  number  of the cell of Π from which q was taken. If q was already
        the only point in its cell, where = false instead.
  
  12.
        This  command  does the necessary bookkeeping for the extra base point
        q: It prescribes q as next base in the stabilizer chain for G (needed,
        e.g.,  in  line 5)  and  returns  false  if  q  was  already fixed the
        stabilizer of the earlier base points (and true otherwise; this is not
        used  here).  Another call to ProcessFixpoint like this was implicitly
        made by the function NextRBasePoint to register the chosen base point.
        By  contrast,  the point q was not chosen this way, so ProcessFixpoint
        must be called explicitly for q.
  
  13.
        This  statement  registers  the function which will be used during the
        backtrack search to perform the corresponding refinements on the image
        partition  Σ_i (to yield the refined Σ_{i+1}). After choosing an image
        b_{i+1}  for the base point a_{i+1}, GAP will compute Σ_i ∧ ({ b_{i+1}
        },  Ω  ∖ { b_{i+1} }) and store this partition in I.partition, where I
        is  a  black  box similar to R, but corresponding to the current image
        partition  (hence it is an R-image in analogy to the R-base). Then GAP
        will  call the function Refinements.Centralizer( R, I, Pi.cellno[ p ],
        p,  where  ),  with  the  then  current  values  of R and I, but where
        Π.cellno[ p ], p, where still have the values they have at the time of
        this  AddRefinement  command.  This  function call will further refine
        I.partition  to  yield  Σ_{i+1}  as  it  is programmed in the function
        Refinements.Centralizer,   which   is  described  below.  (The  global
        variable  Refinements  is  a  record  which  contains  all  refinement
        functions for all backtracking procedures.)
  
  14.-19.
        If  the  cell  from  which q was taken out had only two points, we now
        have  an  additional  one-point  cell.  This  condition  is checked in
        line 13  and  if it is true, this extra fixpoint p is taken (line 15),
        processed  like  q  before  (line 16)  and is then (line 17) passed to
        another  refinement  function  Refinements.ProcessFixpoint(  R,  I, p,
        where ), which is also described below.
  
  23.-29.
        This  command  starts  the  backtrack  search.  Its result will be the
        centralizer as a subgroup of G. Its arguments are
  
  24.
        the group we want to run through,
  
  25.
        the property we want to test, as a GAP function,
  
  26.
        false  if  we  are  looking  for  a  subgroup,  true  in the case of a
        representative search (when the result would be one representative),
  
  27.
        the R-base,
  
  28.
        a  list  of  data, to be stored in I.data, which has in position 1 the
        first  member  Σ_0  of  the  decreasing  sequence  of image partitions
        mentioned  in  43.12.  In the centralizer example, position 2 contains
        the element that is to be centralized. In the case of a representative
        search,  i.e., a conjugacy test g^c ?= h, we would have h instead of g
        here,  and  possibly  a Σ_0 different from Π_0 (e.g., a partition into
        unions of h-cycles of same length).
  
  29.
        two  subgroups L ≤ C_G(g) and R ≤ C_G(h) known in advance (we have L =
        R in the centralizer case).
  
  
  87.2-3 Refinement functions for the backtrack search
  
  The  last  subsection  showed how the refinement process leading from Π_i to
  Π_{i+1}  is  coded in the function R.nextLevel, this has to be executed once
  the  base  point  a_{i+1}. The analogous refinement step from Σ_i to Σ_{i+1}
  must  be  performed  for each choice of an image b_{i+1} for a_{i+1}, and it
  will  depend on the corresponding value of Σ_i ∧ ({b_{i+1}}, Ω ∖ {b_{i+1}}).
  But  before  we  can  continue  our  centralizer  example,  we must, for the
  interested  reader, document the record components of the other black box I,
  as we did above for the R-base black box R. Most of the components change as
  GAP walks up and down the levels of the search tree.
  
  data
        this will be mentioned below
  
  depth
        the level i in the search tree of the current node Σ_i
  
  bimg
        a list of images of the points in R.base
  
  partition
        the partition Σ_i of the current node
  
  level
        the stabilizer chain R.lev[i] at the current level
  
  perm
        a permutation mapping Fixcells( Π_i ) to Fixcells( Σ_i ); this implies
        mapping (a_1, ..., a_i) to (b_1, ..., b_i)
  
  level2, perm2
        a   similar  construction  for  the  second  stabilizer  chain,  false
        otherwise (and true if R.level2 = true)
  
  As  declared  in  the above code for Centralizer (35.4-4), the refinement is
  performed  by  the  function  Refinement.Centralizer(  R, I, Π.cellno[p], p,
  where  ).  The functions in the record Refinement always take two additional
  arguments  before  the  ones specified in the AddRefinement call (in line 13
  above),  namely  the R-base R and the current value I of the R-image. In our
  example,  p  is  a fixpoint of Π = Π_i ∧ ({ a_{i+1} }, Ω ∖ { a_{i+1} }) such
  that  where = Π.cellno[ p^g ]. The Refinement functions must return false if
  the  refinement  is  unsuccessful  (e.g., because it leads to Σ_{i+1} having
  different  cell  sizes  from  Π_{i+1})  and  true  otherwise. Our particular
  function looks like this.
  
  
    Refinements.Centralizer := function( R, I, cellno, p, where )
    local  Sigma, q;
    Sigma := I.partition;
    q := FixpointCellNo( Sigma, cellno ) ^ I.data[ 2 ];
    return IsolatePoint( Sigma, q ) = where and ProcessFixpoint( I, p, q );
    end;
  
  
  The  list  numbers  below  refer to the line numbers of the code immediately
  above.
  
  3.
        The  current  value  of Σ_i ∧ ({ b_{i+1} }, Ω ∖ { b_{i+1} }) is always
        found in I.partition.
  
  4.
        The image of the only point in cell number cellno = Π_i.cellno[ p ] in
        Σ under g = I.data[ 2 ] is calculated.
  
  5.
        The function returns true only if the image q has the same cell number
        in  Σ  as  p  had  in Π (i.e., where) and if q can be prescribed as an
        image  for  p  under the coset of the stabilizer G_{a_1 ... a_{i+1}}.c
        where  c  ∈  G is an (already constructed) element mapping the earlier
        base  points  a_1, ..., a_{i+1} to the already chosen images b_1, ...,
        b_{i+1}. This latter condition is tested by ProcessFixpoint( I, p, q )
        which,  if  successful,  also  does the necessary bookkeeping in I. In
        analogy  to  the remark about line 12 in the program above, the chosen
        image  b_{i+1}  for  the base point a_{i+1} has already been processed
        implicitly  by  the  function  PartitionBacktrack, and this processing
        includes the construction of an element c ∈ G which maps Fixcells( Π_i
        )  to  Fixcells(  Σ_i ) and a_{i+1} to b_{i+1}. By contrast, the extra
        fixpoints   p   and   q   in  Π_{i+1}  and  Σ_{i+1}  were  not  chosen
        automatically,  so  they  require an explicit call of ProcessFixpoint,
        which  replaces  the  element  c  by  some  c'.c (with c' ∈ G_{a_1 ...
        a_{i+1}})  which  in addition maps p to q, or returns false if this is
        impossible.
  
  You  should  now be able to guess what Refinements.ProcessFixpoint( R, I, p,
  where  )  does:  it  simply  returns  ProcessFixpoint(  I, p,FixpointCellNo(
  I.partition, where ) ).
  
  Summary.
  
  When  you  write your own backtrack functions using the partition technique,
  you  have  to  supply  an  R-base,  including a component nextLevel, and the
  functions  in  the Refinements record which you need. Then you can start the
  backtrack  by  passing  the  R-base  and  the  additional data (for the data
  component of the R-image) to PartitionBacktrack.
  
  
  87.2-4 Functions for meeting ordered partitions
  
  A kind of refinement that occurs in particular in the normalizer calculation
  involves  computing  the  meet of Π (cf. lines 6ff. above) with an arbitrary
  other partition Λ, not just with one point. To do this efficiently, GAP uses
  the following two functions.
  
  StratMeetPartition( R, Π, Λ [, g ] )
  
  MeetPartitionStrat( R, I{, Λ'}[, {g'}], strat )
  
  Such  a  StratMeetPartition  command  would typically appear in the function
  call  R.nextLevel(  Π,  R  ) (during the refinement of Π_i to Π_{i+1}). This
  command  replaces  Π  by  Π  ∧  Λ (thereby changing the second argument) and
  returns a meet strategy strat. This is (for us) a black box which serves two
  purposes:  First, it allows GAP to calculate faster the corresponding meet Σ
  ∧  Λ',  which  must  then  appear  in  a  Refinements  function  (during the
  refinement  of Σ_i to Σ_{i+1}). It is faster to compute Σ ∧ Λ' with the meet
  strategy  of  Π ∧ Λ because if the refinement of Σ is successful at all, the
  intersection  of  a  cell  from the left hand side of the ∧ sign with a cell
  from  the  right  hand side must have the same size in both cases (and strat
  records these sizes, so that only non-empty intersections must be calculated
  for  Σ  ∧  Λ').  Second,  if  there  is  a discrepancy between the behaviour
  prescribed  by  strat  and  the  behaviour  observed  when  refining  Σ, the
  refinement can immediately be abandoned.
  
  On  the  other  hand,  if  you  only want to meet a partition Π with Λ for a
  one-time   use,   without   recording   a  strategy,  you  can  simply  type
  StratMeetPartition(  Π,  Λ  )  as  in  the  following  example,  which  also
  demonstrates some other partition-related commands.
  
    Example  
    gap> P := Partition( [[1,2],[3,4,5],[6]] );;  Cells( P );
    [ [ 1, 2 ], [ 3, 4, 5 ], [ 6 ] ]
    gap> Q := Partition( OnTuplesTuples( last, (1,3,6) ) );;  Cells( Q );
    [ [ 3, 2 ], [ 6, 4, 5 ], [ 1 ] ]
    gap> StratMeetPartition( P, Q );
    [  ]
    gap> # The ``meet strategy'' was not recorded, ignore this result.
    gap> Cells( P );
    [ [ 1 ], [ 5, 4 ], [ 6 ], [ 2 ], [ 3 ] ]
  
  
  You can even say StratMeetPartition( Π, ∆ ) where ∆ is simply a subset of Ω,
  it will then be interpreted as the partition (∆, Ω ∖ ∆).
  
  GAP  makes  use  of  the  advantages  of  a  meet strategy if the refinement
  function in Refinements contains a MeetPartitionStrat command where strat is
  the  meet  strategy  calculated by StratMeetPartition before. Such a command
  replaces I.partition by its meet with Λ', again changing the argument I. The
  necessary  reversal  of  these  changes  when  backtracking from a node (and
  prescribing  the next possible image for a base point) is automatically done
  by the function PartitionBacktrack.
  
  In  all  cases,  an additional argument g means that the meet is to be taken
  not  with  Λ,  but  instead  with  Λ.{g^{-1}},  where  operation  on ordered
  partitions  is  meant  cellwise (and setwise on each cell). (Analogously for
  the primed arguments.)
  
    Example  
    gap> P := Partition( [[1,2],[3,4,5],[6]] );;
    gap> StratMeetPartition( P, P, (1,6,3) );;  Cells( P );
    [ [ 1 ], [ 5, 4 ], [ 6 ], [ 2 ], [ 3 ] ]
  
  
  Note that P.(1,3,6) = Q.
  
  
  87.2-5 Avoiding multiplication of permutations
  
  In  the  description  of  the  last  subsections,  the  backtrack  algorithm
  constructs an element c ∈ G mapping the base points to the prescribed images
  and  finally  tests  the  property  in question for that element. During the
  construction,  c  is  obtained as a product of transversal elements from the
  stabilizer  chain for G, and so multiplications of permutations are required
  for  every  c  submitted  to  the test, even if the test fails (i.e., in our
  centralizer  example, if g^c<>g). Even if the construction of c stops before
  images  for  all  base  points  have  been  chosen, because a refinement was
  unsuccessful,  several  multiplications  will already have been performed by
  (explicit  or implicit) calls of ProcessFixpoint, and, actually, the general
  backtrack procedure implemented in GAP avoids this.
  
  For this purpose, GAP does not actually multiply the permutations but rather
  stores  all  the  factors of the product in a list. Specifically, instead of
  carrying  out  the  multiplication  in  c ↦ c'.c mentioned in the comment to
  line 5  of the above program --- where c' ∈ G_{a_1 ... a_{i+1}} is a product
  of  factorized  inverse  transversal  elements, see 43.9 --- GAP appends the
  list  of  these  factorized  inverse transversal elements (giving c') to the
  list of factors already collected for c. Here c' is multiplied from the left
  and  is  itself  a  product  of  inverses of strong generators of G, but GAP
  simply  spares itself all the work of inverting permutations and stores only
  a  list  of  inverses,  whose  product is then (c'.c)^{-1} (which is the new
  value  of  c^{-1}).  The  list  of  inverses  is  extended this way whenever
  ProcessFixpoint is called to improve c.
  
  The  product  has  to  be  multiplied  out only when the property is finally
  tested   for  the  element  c.  But  it  is  often  possible  to  delay  the
  multiplication  even  further,  namely  until  after  the  test,  so that no
  multiplication  is  required  in  the case of an unsuccessful test. Then the
  test  itself  must be carried out with the factorized version of the element
  c.  For  this  purpose, PartitionBacktrack can be passed its second argument
  (the property in question) in a different way, not as a single GAP function,
  but  as  a  list like in lines 2–4 of the following alternative excerpt from
  the code for Centralizer (35.4-4).
  
  
    return PartitionBacktrack( G,
      [ g, g,
      OnPoints,
      c -> c!.lftObj = c!.rgtObj ],
      false, R, [ Pi_0, g ], L, R );
  
  
  The  test for c to have the property in question is of the form opr( left, c
  ) = right where opr is an operation function as explained in 41.12. In other
  words,  c  passes  the  test if and only if it maps a left object to a right
  object  under a certain operation. In the centralizer example, we have opr =
  OnPoints  and left = right = g, but in a conjugacy test, we would have right
  = h.
  
  2.
        Two first two entries (here g and g) are the values of left and right.
  
  3.
        The third entry (here OnPoints (41.2-1)) is the operation opr.
  
  4.
        The  fourth  entry  is  the  test to be performed upon the mapped left
        object  left  and preimage of the right object opr( right, c^-1). Here
        GAP  operates with the inverse of c because this is the product of the
        permutations  stored  in  the  list of inverses. The preimage of right
        under c is then calculated by mapping right with the factors of c^{-1}
        one  by  one, without the need to multiply these factors. This mapping
        of  right  is  automatically  done  by  the  ProcessFixpoint  function
        whenever c is extended, the current value of right is always stored in
        c!.rgtObj.  When  the  test  given  by  the  fourth  entry  is finally
        performed,  the  element  c  has  two  components  c!.lftObj= left and
        c!.rgtObj=  opr(  right,  c^-1),  which  must  be  used to express the
        desired  relation  as  a function of c. In our centralizer example, we
        simply have to test whether they are equal.
  
  
  87.3 Stabilizer Chains for Automorphisms Acting on Enumerators
  
  This  section  describes  a  way of representing the automorphism group of a
  group  as  permutation group, following [Sim97]. The code however is not yet
  included in the GAP library.
  
  In  this  section  we  present  an  example in which objects we already know
  (namely,   automorphisms   of   solvable   groups)  are  equipped  with  the
  permutation-like  operations  ^  and  /  for action on positive integers. To
  achieve  this,  we  must  define  a  new  type  of objects which behave like
  permutations  but  are represented as automorphisms acting on an enumerator.
  Our  goal is to generalize the Schreier-Sims algorithm for construction of a
  stabilizer chain to groups of such new automorphisms.
  
  
  87.3-1 An operation domain for automorphisms
  
  The  idea  we  describe  here  is  due  to C. Sims. We consider a group A of
  automorphisms  of  a group G, given by generators, and we would like to know
  its  order.  Of  course  we  could  follow the strategy of the Schreier-Sims
  algorithm  (described  in 43.6) for A acting on G. This would involve a call
  of  StabChainStrong(  EmptyStabChain( [], One( A ) ), GroupGenerators( A ) )
  where  StabChainStrong is a function as the one described in the pseudo-code
  below:
  
  
    StabChainStrong := function( S, newgens )
      Extend the Schreier tree of S with newgens.
      for sch  in  Schreier generators  do
        if not sch in S.stabilizer  then
          StabChainStrong( S.stabilizer, [ sch ] );
        fi;
      od;
    end;
  
  
  The  membership  test  sch  ∉  S.stabilizer  can  be  performed  because the
  stabilizer  chain of S.stabilizer is already correct at that moment. We even
  know  a  base  in  advance,  namely  any  generating  set  for G. Fix such a
  generating  set (g_1, ..., g_d) and observe that this base is generally very
  short  compared  to  the  degree  |G| of the operation. The problem with the
  Schreier-Sims algorithm, however, is then that the length of the first basic
  orbit g_1.A would already have the magnitude of |G|, and the basic orbits at
  deeper  levels  would not be much shorter. For the advantage of a short base
  we  pay  the high price of long basic orbits, since the product of the (few)
  basic  orbit lengths must equal |A|. Such long orbits make the Schreier-Sims
  algorithm  infeasible,  so  we  have  to look for a longer base with shorter
  basic orbits.
  
  Assume that G is solvable and choose a characteristic series with elementary
  abelian  factors.  For  the  sake  of  simplicity we assume that N < G is an
  elementary  abelian  characteristic  subgroup with elementary abelian factor
  group   G/N.  Since  N  is  characteristic,  A  also  acts  as  a  group  of
  automorphisms  on  the  factor  group  G/N,  but  of  course not necessarily
  faithfully.  To retain a faithful action, we let A act on the disjoint union
  G/N  with  G, and choose as base (g_1 N, ..., g_d N, g_1, ..., g_d). Now the
  first d basic orbits lie inside G/N and can have length at most [G:N]. Since
  the  base  points  g_1  N,  ...,  g_d N form a generating set for G/N, their
  iterated stabilizer A^(d+1) acts trivially on the factor group G/N, i.e., it
  leaves  the cosets g_i N invariant. Accordingly, the next d basic orbits lie
  inside g_i N (for i = 1, ..., d) and can have length at most |N|.
  
  Generalizing  this  method  to a characteristic series G = N_0 > N_1 > ... >
  N_l  =  {  1 } of length l > 2, we can always find a base of length l.d such
  that  each  basic  orbit is contained in a coset of a characteristic factor,
  i.e.  in  a  set  of  the  form  g_i  N_{j-1} / N_j (where g_i is one of the
  generators  of  G  and  1  ≤  j ≤ l). In particular, the length of the basic
  orbits  is  bounded by the size of the corresponding characteristic factors.
  To  implement  a Schreier-Sims algorithm for such a base, we must be able to
  let automorphisms act on cosets of characteristic factors g_i N_{j-1} / N_j,
  for  varying  i  and  j. We would like to translate each such action into an
  action on { 1, ..., [ N_{j-1}:N_j] }, because then we need not enumerate the
  operation  domain,  which  is the disjoint union of G / N_1, G / N_2 ... G /
  N_l, as a whole. Enumerating it as a whole would result in basic orbits like
  orbit⊆  { 1001, ..., 1100 } with a transversal list whose first 1000 entries
  would be unbound, but still require 4 bytes of memory each (see 43.9).
  
  Identifying  each  coset g_i N_{j-1} / N_j into { 1, ..., [N_{j-1}:N_j] } of
  course means that we have to change the action of the automorphisms on every
  level  of  the  stabilizer  chain.  Such  flexibility  is  not possible with
  permutations  because  their  effect  on positive integers is hardwired into
  them, but we can install new operations for automorphisms.
  
  
  87.3-2 Enumerators for cosets of characteristic factors
  
  So  far  we  have  not  used  the  fact  that the characteristic factors are
  elementary  abelian,  but  we  will do so from here on. Our first task is to
  implement  an  enumerator  (see  AsList (30.3-8) and 21.23) for a coset of a
  characteristic  factor  in a solvable group G. We assume that such a coset g
  N/M is given by
  
  (1)
        a pcgs for the group G (see Pcgs (45.2-1)), let n =Length( pcgs );
  
  (2)
        a  range range = [ start .. stop ] indicating that N = ⟨ pcgs{ [ start
        ..  n ] } ⟩ and M = ⟨ pcgs{ [ stop + 1 .. n ] } ⟩, i.e., the cosets of
        pcgs{ range } form a base for the vector space N/M;
  
  (3)
        the representative g.
  
  We first define a new representation for such enumerators and then construct
  them  by simply putting these three pieces of data into a record object. The
  enumerator  should  behave  as a list of group elements (representing cosets
  modulo M), consequently, its family will be the family of the pcgs itself.
  
    Example  
    DeclareRepresentation( "IsCosetSolvableFactorEnumeratorRep", IsEnumerator,
        [ "pcgs", "range", "representative" ] );
    
    EnumeratorCosetSolvableFactor := function( pcgs, range, g )
        return Objectify( NewType( FamilyObj( pcgs ),
                       IsCosetSolvableFactorEnumeratorRep ),
                       rec( pcgs := pcgs,
                           range := range,
                  representative := g ) );
    end;
  
  
  The  definition  of  the  operations  Length  (21.17-5),  \[\]  (21.2-1) and
  Position  (21.16-1)  is  now  straightforward.  The  code has sometimes been
  abbreviated and is meant cum grano salis, e.g., the declaration of the local
  variables has been left out.
  
    Example  
    InstallMethod( Length, [ IsCosetSolvableFactorEnumeratorRep ],
        enum -> Product( RelativeOrdersPcgs( enum!.pcgs ){ enum!.range } ) );
    
    InstallMethod( \[\], [ IsCosetSolvableFactorEnumeratorRep,
            IsPosRat and IsInt ],
        function( enum, pos )
        elm := ();
        pos := pos - 1;
        for i  in Reversed( enum!.range )  do
            p := RelativeOrderOfPcElement( enum!.pcgs, i );
            elm := enum!.pcgs[ i ] ^ ( pos mod p ) * elm;
            pos := QuoInt( pos, p );
        od;
        return enum!.representative * elm;
    end );
    
    InstallMethod( Position, [ IsCosetSolvableFactorEnumeratorRep,
            IsObject, IsZeroCyc ],
        function( enum, elm, zero )
        exp := ExponentsOfPcElement( enum!.pcgs,
                       LeftQuotient( enum!.representative, elm ) );
        pos := 0;
        for i  in enum!.range  do
            pos := pos * RelativeOrderOfPcElement( pcgs, i ) + exp[ i ];
        od;
        return pos + 1;
    end );
  
  
  
  87.3-3 Making automorphisms act on such enumerators
  
  Our next task is to make automorphisms of the solvable group pcgs!.group act
  on  [  1  ..Length( enum ) ] for such an enumerator enum. We achieve this by
  introducing  a  new  representation  of  automorphisms on enumerators and by
  putting  the  enumerator together with the automorphism into an object which
  behaves  like  a  permutation.  Turning an ordinary automorphism into such a
  special  automorphism  requires  then the construction of a new object which
  has  the  new  type.  We provide an operation PermOnEnumerator( model, aut )
  which  constructs  such  a  new  object  having  the same type as model, but
  representing  the  automorphism  aut.  So  aut  can  be  either  an ordinary
  automorphism or one which already has an enumerator in its type, but perhaps
  different from the one we want (i.e. from the one in model).
  
    Example  
    DeclareCategory( "IsPermOnEnumerator",
        IsMultiplicativeElementWithInverse and IsPerm );
    
    DeclareRepresentation( "IsPermOnEnumeratorDefaultRep",
        IsPermOnEnumerator and IsAttributeStoringRep,
        [ "perm" ] );
    
    DeclareOperation( "PermOnEnumerator",
        [ IsEnumerator, IsObject ] );
    
    InstallMethod( PermOnEnumerator,
        [ IsEnumerator, IsObject ],
        function( enum, a )
        SetFilterObj( a, IsMultiplicativeElementWithInverse );
        a := Objectify( NewKind( PermutationsOnEnumeratorsFamily,
                     IsPermOnEnumeratorDefaultRep ),
                     rec( perm := a ) );
        SetEnumerator( a, enum );
        return a;
    end );
    
    InstallMethod( PermOnEnumerator,
        [ IsEnumerator, IsPermOnEnumeratorDefaultRep ],
        function( enum, a )
        a := Objectify( TypeObj( a ), rec( perm := a!.perm ) );
        SetEnumerator( a, enum );
        return a;
    end );
  
  
  Next  we  have to install new methods for the operations which calculate the
  product of two automorphisms, because this product must again have the right
  type.  We  also have to write a function which uses the enumerators to apply
  such an automorphism to positive integers.
  
    Example  
    InstallMethod( \*, IsIdenticalObj,
        [ IsPermOnEnumeratorDefaultRep, IsPermOnEnumeratorDefaultRep ],
        function( a, b )
        perm := a!.perm * b!.perm;
        SetIsBijective( perm, true );
        return PermOnEnumerator( Enumerator( a ), perm );
    end );
    
    InstallMethod( \^,
        [ IsPosRat and IsInt, IsPermOnEnumeratorDefaultRep ],
        function( p, a )
        return PositionCanonical( Enumerator( a ),
                       Enumerator( a )[ p ] ^ a!.perm );
    end );
  
  
  How the corresponding methods for p / aut and aut ^ n look like is obvious.
  
  Now  we  can formulate the recursive procedure StabChainStrong which extends
  the  stabilizer  chain  by  adding  in  new  generators  newgens. We content
  ourselves  again  with pseudo-code, emphasizing only the lines which set the
  EnumeratorDomainPermutation.  We  assume  that  initially  S is a stabilizer
  chain  for  the  trivial  subgroup  with  a  level  for  each pair (range,g)
  characterizing  an  enumerator (as described above). We also assume that the
  identity  element  at  each level already has the type corresponding to that
  level.
  
  
    StabChainStrong := function( S, newgens )
      for i  in [ 1 .. Length( newgens ) ]  do
        newgens[ i ] := AutomorphismOnEnumerator( S.identity, newgens[ i ] );
      od;
      Extend the Schreier tree of S with newgens.
      for sch  in  Schreier generators  do
        if not sch in S.stabilizer  then
          StabChainStrong( S.stabilizer, [ sch ] );
        fi;
      od;
    end;
  
  

bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net