Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
#############################################################################
##
## 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 Stephen Howe, Maska Law, 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
##
##
## This file contains an implementation of an algorithm for recognising
## An or Sn acting as matrix groups on their fully deleted permutation
## modules. The particular algorithm is described in the paper [BLGN+05].
##
#############################################################################
## General utility functions
RECOG.LogRat := function(rat, base)
# LogRat(<rat>, <base>) is an upper bound
# on log <rat> to base <base>.
return (LogInt(NumeratorRat(rat), base) + 1)
- LogInt(DenominatorRat(rat), base);
end;
RECOG.IterateWithRandomElements := function(func, times, group)
# IterateWithRandomElements(<func>, <times>, <group>) randomly
# selects up to <times> random elements (from <group>) to find
# an element such that <func> (with input being this random element)
# does not return 'fail'. The function <func> must take a group
# element as its only argument.
local itr, # number of random elements selected
re, # a random element
ret; # the result of func(re)
itr := 1;
while itr <= times do
# select random element
re := PseudoRandom(group);
# call func with random element
ret := func(re);
# if function successful
if ret <> fail then
return ret;
fi;
# else try again
itr := itr + 1;
od;
return fail;
end;
## Functions for efficient matrix operations
RECOG.Commute := function(g, h);
# Commute(g,h) returns true if and only if
# gh = hg
ConvertToMatrixRep(g);
ConvertToMatrixRep(h);
return g*h = h*g;
end;
RECOG.Conj := function(A, B)
# Conj(A, B) returns the conjugation of
# A by B (where A and B are two matrices)
# That is Conj(A, B) = B^-1*A*B
ConvertToMatrixRep(B);
return A^B;
end;
RECOG.ConjInv := function(A, B)
# ConjInv(A, B) returns the conjugation of
# A by B^-1 (where A and B are two matrices)
# That is ConjInv(A, B) = B*A*B^-1
ConvertToMatrixRep(B);
return B*A*B^-1;
end;
## Finding a 3-cycle or double transposition
RECOG.LinearFactors := function(poly, field)
# LinearFactors(<poly>, <field>) returns a list with two
# elements when given a univariate polynomial <poly> with
# coefficients from the finite field <field>. The first
# element is a list of field elements [c1,...,cn] where
# t - ci divides <poly> for i = 1,...,n. The second element
# is a function from the multiplicative group of <field> to
# the integers such that: for every non-zero field element c,
# the image of c is the multiplicity of t - c in <poly>.
local linearFactors, # [c1,...,cn] s.t. t - ci is a factor
ix, # list index
z, # primitive root of field, i.e., z = Z(q)
multList, # a list of multiplicities where t - Z(q)^(i-1)
# has multiplicity multList[i]
Multiplicity; # function mapping non-zero field elem. c to
# mult. of t - c
# find the linear factors of poly (as polynomials),
# so linearFactors = [[ai*t + bi, mi]] where ai, bi are
# field elements and mi are the multiplicities
linearFactors := Filtered(Collected(Factors(PolynomialRing(field), poly)),
f -> DegreeOfLaurentPolynomial(f[1]) = 1);
# put linearFactors into form [[ci, mi]] where mi is
# multiplicity of t - ci
Apply(linearFactors,
f -> [-Iterated(CoefficientsOfUnivariatePolynomial(f[1]), \/),
f[2]]);
# create the function Multiplicity
z := PrimitiveRoot(field); # find Z(q) where q is size of field
# create list s.t. the ith entry is the multiplicity
# of t - Z(q)^(i-1) in poly
multList := [];
for ix in [1..Length(linearFactors)] do
multList[LogFFE(linearFactors[ix][1], z) + 1] := linearFactors[ix][2];
od;
Multiplicity := function(ffe)
local ffeIndex;
# calc index for ffe
ffeIndex := LogFFE(ffe, z) + 1;
# if t - Z(q)^i is not a factor of poly
# then multList[i] is not bound
if IsBound(multList[ffeIndex]) then
return multList[ffeIndex];
else
return 0;
fi;
end;
return [List(linearFactors, f -> f[1]), Multiplicity];
end;
RECOG.IsPreDoubleTransposition := function(charPoly, t, linearFactors,
fdpmDim, nAmbig, field)
# IsPreDoubleTransposition(<charPoly>, <t>, <linearFactors>,
# <fdpmDim>, <nAmbig>, <field>)
# determines whether some scalar multiple of the matrix described
# by <charPoly> (with indeterminate <t>) represents a
# pre-double-transposition. If so the scalar multiple is returned,
# otherwise 'false' is returned.
# <linearFactors> - describes the linear factors of <charPoly>
# <fdpmDim> - the dimension of the matrices described
# <nAmbig> - true when there are two possible values for n
# <field> - the field of <charPoly>
# This function works for any field with odd characteristic.
# Note: requires n >= 8 or n = 7 and p <> 7, <=> fdpmDim >= 6
local b, c, # field elements
tpList, # list of elements c s.t. t^2 - c^2 | charPoly
r; # a prime integer
if Length(linearFactors[1]) < 2 then
# we must have t + c and t - c as linear factors
# for some c
return false;
else
tpList := [];
# we expect tpList to only have two elements
# at the end of this loop
for c in linearFactors[1] do
# we add to tpList in (+/-) pairs
# so check c is not -d for some d in tpList
if c in tpList then
continue;
elif linearFactors[2](-c) > 0 then
# t^2 - c^2 divides charPoly
if not IsEmpty(tpList) then
# tpList would have more than 2 elements
return false;
else
Add(tpList, c);
Add(tpList, -c);
fi;
fi;
od;
fi;
# determine whether tpList is of the form { c, -c } where
# t + c has multiplicity 2
if IsEmpty(tpList) then
return false;
elif linearFactors[2](tpList[1]) = 2 then
# (t - tpList[1]) has multiplicity 2
c := -tpList[1];
elif linearFactors[2](tpList[2]) = 2 then
# (t - tpList[2]) has multiplicity 2
c := -tpList[2];
else
return false;
fi;
# determine the scalar b associated with the matrix
# whose characteristic polynomial we have
if linearFactors[2](c) <> 2 then
b := c;
# some extra cases: Assuming s = 2 and 2 <= r < n/2 in proof,
# so there may be a (unique) odd prime r >= n/2
if IsPrime(fdpmDim - 2)
and charPoly = (t^2 - b^2)*(t^(fdpmDim - 2) + b^(fdpmDim - 2)) then
# corresponds to permutation with cycle type 1^1 2^1 (n-3)^1
return false;
elif nAmbig
and fdpmDim - 4 <> 2 and IsPrime(fdpmDim - 4) # fdpmDim-4 is odd prime
and charPoly = (t - b)^3*(t+b)*(t^(fdpmDim - 4) + b^(fdpmDim - 4)) then
# corresponds to permutation with cycle type 2^3 (n-6)^1
return false;
fi;
elif fdpmDim mod 2 = 0 then # also mult (t - c) = 2
# get coeffs of t^(fdpmDim - 1), t^(fdpmDim - 2), and t^(fdpmDim - 3)
# in charPoly
c := CoefficientsOfUnivariatePolynomial(charPoly){
[fdpmDim - 3 .. fdpmDim - 1] + 1};
# Check case 3 (ii) and first part of 3 (iii) together (they are
# identical in terms of fdpmDim = n - delta). That is,
# check t^2-c[3]^2 | charPoly and
# charPoly = (t-c[3])(t+c[3])^2(t^(fdpmDim-3)-c[3]^(fdpmDim - 3))
if c[3] in tpList
and charPoly = (t - c[3]) * (t + c[3])^2 *
(t^(fdpmDim - 3) - c[3]^(fdpmDim - 3)) then
# since we know the form of the characteristic polynomial we don't
# need to check t^r + b^r does not divide charPoly for r etc.
return c[3];
elif nAmbig then # do the rest of 3 (iii) only if delta could be 2
# expect coeffs of t^(fdpmDim-1) and t^(fdpmDim - 2) are
# 2b and b^2 respectively with b in tpList
if c[3]/2 in tpList and c[2] = (c[3]/2)^2 then
# if c[1] <> 0 then we must have cycle type 2^2 3^1 (n-7)^1
# if c[1] = 0 then we just set b
b := c[3]/2;
if not IsZero(c[1])
and charPoly = (t + b)^2 * (t^3 - b^3) *
(t^(fdpmDim - 5) - b^(fdpmDim - 5)) then
# we can return b since we have check the form
# of charPoly
return b;
fi;
else
return false; # c[3]/2 is not in tpList
fi;
else
return false; # n not ambiguous
fi;
else
return false; # no special cases for this value of fdpmDim
fi; # we have now either returned or found a possible scalar b
# check there is no prime r = 2 or 5 <= r < n/2 with
# t^r + b^r dividing charPoly
r := 2;
while r < QuoInt(fdpmDim + 2, 2) do
if r <> Characteristic(field)
and IsZero(QuotientRemainder(charPoly, t^r + b^r)[2]) then
return false;
fi;
r := NextPrimeInt(r);
od;
return b;
end;
RECOG.IsPre3Cycle :=
function(charPoly, t, linearFactors, fdpmDim, nAmbig, field)
# IsPre3Cycle(<charPoly>, <t>, <linearFactors>, <fdpmDim>, <nAmbig>, <field>)
# determines whether some scalar multiple of the matrix described
# by <charPoly> (with indeterminate <t>) represents a
# pre-3-cycle. If so the scalar multiple is returned,
# otherwise 'false' is returned.
# <linearFactors> - describes the linear factors of <charPoly>
# <fdpmDim> - the dimension of the matrices described
# <nAmbig> - true when there are two possible values for n
# <field> - the field of <charPoly>
# Note: requires n >= 5 or n = 5 and p <> 5, <=> fdpmDim >= 4
local tcList, # list of field elems c s.t. t^3 - c^3 | charPoly
lMult, # a list of multiplicities
r, # a prime
b, c, # field elements
y; # field element of order 3
if Size(field) mod 3 = 2 then
# find all c s.t. t^3 - c^3 | charPoly
tcList := Filtered(linearFactors[1],
c -> IsZero(QuotientRemainder(charPoly,
t^3 - c^3)[2]));
if Length(tcList) = 1 then
# check whether tcList = {c} where t^2+ct+c^2 has mult 1
if IsZero(QuotientRemainder(charPoly,
(t^2 + tcList[1]*t + tcList[1]^2)^2)[2]) then
# t^2+ct+c^2 has mult > 1
return false;
else
# set scalar
b := tcList[1];
fi;
elif IsEmpty(tcList) and nAmbig and (fdpmDim + 2) mod 3 <> 0 then
if Characteristic(field) <> 2 then # if char of field is odd
# coeff of t^(fdpmDim - 1) in charPoly is 2b
c := CoefficientsOfUnivariatePolynomial(charPoly)[(fdpmDim - 1)+1]/2;
else
# coeff of t^(fdpmDim - 2) in charPoly is b^2
c := CoefficientsOfUnivariatePolynomial(charPoly)[(fdpmDim - 2)+1];
c := SquareRoots(field, c)[1];
fi;
if charPoly = (t^2 + c*t + c^2)*(t^(fdpmDim-1) - c^(fdpmDim-1))
/ (t - c) then
# return the scalar since we know the form of charPoly
return c;
else
return false;
fi;
else # Length(tcList) >= 2
return false;
fi;
else # q mod 3 = 1 since we assume q mod 3 <> 0
# calc field element of order 3
y := PrimitiveRoot(field)^((Size(field)-1)/3);
# find all elements c such that t-c | charPoly and t-cy^i | charPoly
# for i = 1 or 2
tcList := Filtered(linearFactors[1],
c -> linearFactors[2](c*y) > 0
or linearFactors[2](c*y^2) > 0);
if Length(tcList) = 2
and Order(tcList[1]*tcList[2]^-1) = 3
and linearFactors[2](tcList[1]) = 1
and linearFactors[2](tcList[2]) = 1 then
# set the scalar
b := tcList[1]^2*tcList[2]^-1;
elif Length(tcList) = 3 and ForAll(tcList, c -> y*c in tcList) then
# check whether y*tcList = tcList (setwise)
# find elements of tcList that have mult > 1
lMult := Filtered(tcList, c -> linearFactors[2](c) > 1);
if Length(lMult) = 1 then
# set the scalar
b := lMult[1];
elif IsEmpty(lMult) then
# get coeffs of t^(fdpmDim-1), t, and t^0
c := CoefficientsOfUnivariatePolynomial(charPoly){[1,2,(fdpmDim-1)+1]};
# check for pre-3-cycles with cycle type 3^1 (n-3)^1, if delta = 1,
# or cycle type 1^1 3^1 (n-4)^1, if delta = 2. The tests are
# identical when written in terms of n - delta.
if (fdpmDim + 1) mod 3 <> 0
and c[3] in tcList
and charPoly = (t^2 + c[3]*t + c[3]^2)
*(t^(fdpmDim - 2) - c[3]^(fdpmDim - 2)) then
# return the scalar since we know the form of charPoly
return c[3];
elif nAmbig # this case only occurs when delta = 2
and Characteristic(field) >= 5
and c[3]/2 in tcList
and c[1] = -(c[3]/2)^fdpmDim # check coeff of t^0
and c[2] = -2*(c[3]/2)^(fdpmDim-1) then # check coeff of t
# set the scalar
b := c[3]/2;
else
return false;
fi;
else # Length(lMult) > 1
return false;
fi;
else # Length(tcList) <> 2 or 3
return false;
fi;
fi; # end of q mod 3 = 1 case
# check there are no primes r (<= n/3, r <> p) such that
# t^2r + b^r*t^r + b^2r | charPoly
r := 2;
while r <= QuoInt(fdpmDim + 2, 3) do
if r <> Characteristic(field)
and IsZero(QuotientRemainder(charPoly,
t^(2*r) + b^r*t^r + b^(2*r))[2]) then
return false;
fi;
r := NextPrimeInt(r);
od;
return b;
end;
RECOG.Construct3Cycle := function(mat, fdpm)
# Construct3Cycle(<mat>, <fdpm>) attempts
# to construct a matrix representing a 3-cycle from
# <mat>. If it is successful the new matrix is
# returned in a record, otherwise fail is returned.
# The record has two components; the first is
# 'matrix' and stores the matrix, the second is 'order'
# and stores the order of the matrix i.e. 3.
local charPoly, # the characteristic polynomial of mat
t, # the indeterminate of charPoly
linearFactors, # the linear factors of charPoly
b, # a field element
order; # order of the matrix
# calculate the characteristic polynomial and its linear factors
charPoly := CharacteristicPolynomial(mat);
t := IndeterminateOfLaurentPolynomial(charPoly);
# calculate the linear factors of charPoly
linearFactors := RECOG.LinearFactors(charPoly, fdpm.field);
b := RECOG.IsPre3Cycle(charPoly, t, linearFactors,
fdpm.dim, fdpm.nAmbig, fdpm.field);
if b <> false then
mat := b^-1*mat;
# check the order
order := Order(mat);
if order < (fdpm.dim + 2)^(18*RECOG.LogRat(fdpm.dim + 2, 2))
and order mod 3 = 0 then
ConvertToMatrixRep(mat);
mat := mat^(order / 3);
return rec( matrix := mat, order := 3 );
fi;
fi;
return fail;
end;
RECOG.ConstructDoubleTransposition := function(mat, fdpm)
# ConstructDoubleTransposition(<mat>, <fdpm>)
# attempts to construct a matrix representing a double
# transposition from <mat>. If it is successful the new
# matrix is returned in a record, otherwise 'fail' is
# returned. The record has two components; the first is
# 'matrix' and stores the matrix, the second is 'order'
# and stores the order of the matrix i.e. 2.
local charPoly, # the characteristic polynomial of mat
t, # the indeterminate of charPoly
linearFactors, # the linear factors of charPoly
b, # a field element
order; # order of the matrix
# calculate the characteristic polynomial and its linear factors
charPoly := CharacteristicPolynomial(mat);
t := IndeterminateOfLaurentPolynomial(charPoly);
# calculate the linear factors of charPoly
linearFactors := RECOG.LinearFactors(charPoly, fdpm.field);
b := RECOG.IsPreDoubleTransposition(charPoly, t, linearFactors,
fdpm.dim, fdpm.nAmbig, fdpm.field);
if b <> false then # mat is a pre-double-transposition
mat := b^-1*mat;
# check the order
order := Order(mat);
if order < (fdpm.dim + 2)^(18*RECOG.LogRat(fdpm.dim + 2, 2))
and order mod 2 = 0 then
ConvertToMatrixRep(mat);
mat := mat^(order / 2);
return rec( matrix := mat, order := 2 );
fi;
fi;
return fail;
end;
## Functions for finding the first element of our linked basis
RECOG.DoubleAndShrink := function(g, group, n)
# DoubleAndShrink(<g>, <group>, <n>) returns a conjugate
# h of <g> such that <g> and h have only 1 moved
# point in common where <group> is some matrix representation of
# a finite symmetric group of order <n>, and <g> moves between
# 3 and sqrt(<n>) points.
local gs, # list of generated elements
rs, # list of random elements, their inverses,
# and the conjugations
ri, # the current random element
gi, # the current generated element
temp, # temp storage for some element
gconjs, # gs[j-1]^s
gconjrs, # gs[j-1]^(r[j-1]s) respectively
i, j, # counters
s, # conjugating element, g^s is returned
maxItr; # maximum number of random element selections
# initialise variables
maxItr := RECOG.LogRat(n, 2);
gs := [g];
gi := g;
# get random element and calculate its inverse
# and gi^ri
ri := PseudoRandom(group);
ConvertToMatrixRep(ri);
temp := ri^-1;
rs := [[ri, temp, temp*gi*ri]];
i := 1;
# find elements that do not commute
while RECOG.Commute(gi, rs[i][3]) do
if i >= maxItr then
return fail;
fi;
# generate next element
gi := gi * rs[i][3];
Add(gs, gi);
# get next random element
ri := PseudoRandom(group);
temp := ri^-1;
Add(rs, [ri, temp, temp * gi * ri]);
i := i + 1;
od;
# go back through the generated elements to
# get a conjugating element s such that g
# and g^s do not commute
s := rs[i][1];
for j in [i,i-1..2] do
# calc gs[j-1]^s
gconjs := RECOG.Conj(gs[j-1], s);
# determine new s
if RECOG.Commute(gs[j-1], gconjs) then
# calc gs[j-1]^(rs[j-1]*s)
gconjrs := RECOG.Conj(rs[j-1][3], s);
if not RECOG.Commute(gs[j-1], gconjrs) then
s := rs[j-1][1] * s;
elif not RECOG.Commute(rs[j-1][3], gconjs) then
s := s * rs[j-1][2];
else
# s := s^(rs[j-1][1]^-1)
s := rs[j-1][1] * s * rs[j-1][2];
fi;
# else
# s stays the same
fi;
od;
return RECOG.Conj(g, s);
end;
RECOG.FixedPointSubspace := function(mat, k)
# FixedPointSubspace(<mat>, <k>) for a matrix <mat> with
# order <k> returns [F, W] where
# - F is a list of vectors that form a basis
# for the fixed point subspace of <mat>, and
# - W is a list of vectors that form a basis for
# the complement W(<mat>) of F such that for all w
# in W(<mat>), w+w<mat>+w<mat>^2+...+w<mat>^(k-1) = 0
# This function uses the row echelon form of a matrix in order
# to compute the relevent bases. Requires k mod p <> 0 where
# p is the characteristic of the field.
local moveLT, trans;
# linear transformation mapping elements of the
# vector space to elements of W(<mat>), takes
# a matrix as its argument
moveLT := function(v)
local sum, i;
# calculate (((v*mat + v)*mat + v)*mat + v)...)*mat + v
sum := v;
for i in [1..k-1] do
sum := sum * mat + v;
od;
return k*v - sum;
end;
# SemiEchelonMatTransformation(m)
# returns rec ( heads, vectors, coeffs, relations )
# where the rank of m is the length of coeffs
# and coeffs*m are the row vectors of m in row echelon form
# In particular [ coeffs ] is the echelonizing matrix
# [ relations ]
# v is a fixed point of mat iff v is in the null space of Xmat
# compute the transformation taking 'mat - One(mat)' to row echelon form
trans := SemiEchelonMatTransformation(mat - One(mat));
return [trans.relations, moveLT(trans.coeffs)];
end;
RECOG.SubspaceIntersection := function(basisA, basisB)
# SubspaceIntersection(basisA, basisB) returns a vector in the intersection
# of the subspaces <basisA[1], basisA[2]> and <basisB[1], basisB[2]>
# We assume that basisA and basisB are both lists of two linearly
# independent vectors.
local mat, # matrix formed from rows of basisA and basisB
row, # a row vector in the intersection
nullv; # basis for nullspace of mat
# check we are given lists of length 2
if Length(basisA) <> 2 or Length(basisB) <> 2 then
# this function only works for intersection of subspaces
# of dimension 2
return fail;
fi;
# create the matrix whose rows are basisA[1], basisA[2],
# basisB[1], and basisB[2]
mat := [ShallowCopy(basisA[1]),
ShallowCopy(basisA[2]),
ShallowCopy(basisB[1]),
ShallowCopy(basisB[2])];
# find the null space of the matrix
nullv := SemiEchelonMatTransformation(mat).relations;
if Length(nullv) <> 1 or ( IsZero(nullv[1][1])
and IsZero(nullv[1][2])) then
# the two subspaces don't intersect in a one dimensional
# subspace
return fail;
else
# calculate vector in subspace intersection
ConvertToVectorRep(mat[1]);
MultRowVector(mat[1], nullv[1][1]);
AddRowVector(mat[1], mat[2], nullv[1][2]);
return mat[1];
fi;
end;
RECOG.FindBasisElement := function(g, group, epsilon, fdpm)
# FindBasisElement(<g>, <group>, <epsilon>, <fdpm>) returns,
# with probability 1 - <epsilon>, a vector representing a basis
# element of the fully deleted permutation module, that is,
# a vector representing b(e_i - e_j) + (W \cap E) for some
# non-zero field element b and integers i and j. A matrix
# representing a 3-cycle or a double transposition is passed
# in the record <g>. The record has two components; the first is
# 'matrix' and stores the matrix, the second is 'order' and stores
# the order of the matrix (from which we can determine whether
# the permutation type the matrix represents).
local itr, h, gh, temp;
itr := 1;
# Check the bounds
while itr < RECOG.LogRat(epsilon^-1, 2)*7 do # 1/log(10/9) < 7
# get random conjugate
h := RECOG.DoubleAndShrink(g.matrix, group, fdpm.dim + 2);
# check if it has exactly one moved point in common
if h <> fail then
gh := g.matrix * h;
if ( (g.order = 2 and Order(gh) = 6)
or (g.order = 3 and Order(gh) = 5)) then
# return intersection of the totally moved subspaces
ConvertToMatrixRep(h);
temp := h^-1;
return RECOG.SubspaceIntersection(
RECOG.FixedPointSubspace(g.matrix, g.order)[2],
RECOG.FixedPointSubspace(temp * gh, g.order)[2]);
fi;
fi;
itr := itr + 1;
od;
return fail;
end;
## Functions for finding a linked sequence of vectors
RECOG.Power := function(xs, k)
# Power(<xs>, <k>) computes <xs>[1]^<k> in
# log k time where <xs> is the list
# a list [x, x^2, x^4, ...., x^(2^m)] for some
# matrix x. If k >= 2^(m+1) then the list will
# be updated
local q, r, prod, xn;
q := k;
xn := 1; # list index x[xn] = x^(2^(xn-1))
prod := One(xs[1]); # partial product which will finish as x^k
while q > 0 do
# extend the list if we have to
if xn > Length(xs) then # we have xn = Length(xs) + 1
ConvertToMatrixRep(xs[xn - 1]);
Add(xs, xs[xn - 1]^2);
fi;
# if this bit in the binary expansion of k is 1
if RemInt(q, 2) = 1 then
# update the product
prod := prod * xs[xn];
fi;
xn := xn + 1;
# move to the next bit
q := QuoInt(q, 2);
od;
return prod;
end;
RECOG.VectorImages := function(v, xs)
# VectorImages(<v>, <xs>) returns the list
# [<v>, <v>*x, ..., <v>*x^(2^(k+1)-1)]
# where xs = [x,x^2,x^4,...,x^(2^k)].
# The lists are computed in O(logm) time (wrt matrix mult).
local k, # counter
vxs; # the list [v,vx,...,vx^2logm]
# calc v,vx,...,vx^2logm
vxs := [v];
for k in [1..Length(xs)] do
Append(vxs, vxs * xs[k]);
od;
return vxs;
end;
RECOG.VectorImagesUnder := function(v, h, k)
# VectorImagesUnder(<v>,<h>, <k>) returns
# the list [<v>, <v>*h, ..., <v>*<h>^(2^(LogInt(k,2)+1)-1)]
local hs, p;
hs := [h];
for p in [1..LogInt(k, 2)] do
ConvertToMatrixRep(hs[p]);
Add(hs, hs[p]^2);
od;
return RECOG.VectorImages(v, hs);
end;
RECOG.MoreBasisVectors := function(x, g, v, fdpm)
# MoreBasisVectors(<x>, <g>, <v>, <fdpm>), where
# <g> is a record and <g>.matrix represents a 3-cycle
# or double transposition, <g>.order is
# the order of <g>.matrix, and <x> is a random group element;
# returns a set of linked basis vectors of prime length r where
# 0.6n+0.4 < r < 0.95n - 0.85
local i, # the number of vectors (obtained from v) we consider
vs, # the list [vg^i | i = 0, 1, 2, if g reps. a 3 cycle
# i = 0, if g reps. a dt ]
xs, # the list [x,x^2,x^4,...,x^(2^k)]
# where 2^k <= n < 2^(k+1)
vBases, # a list of Basis objects for the vector spaces
# spanned by the vectors in vs
vImgs, # images of vectors in vs under x, ..., x^(n-1)
rs, # [r(vg^i,x) | i = 0, 1, 2, if g reps. a 3 cycle
# i = 0, if g reps. a dt ]
R, # returns r(v, x)
lList, # { l : 1 <= l < r, vx^l <> vx^lg }
mat, # temporary matrix for calc. vImgsG
vImgsG, # [ vg, vxg, vx^2g, ..., vx^(n-1)g ]
u, # a vector
d, # u = dv or u = dvg
h, # a matrix rep. a (large) prime cycle
k; # loop counter
R := function(vBasis, vxs)
# R(vBasis, vxs) returns r(v, x) where
# vBasis is a Basis object for the vector space
# spanned by v and vxs is the list of images of v
# under x, ..., x^(n-1)
local r;
# find least +ve integer k such that vx^k in
# the vector space spanned by v
# Note: opened up the bounds a bit so it works for
# n < 13
r := NextPrimeInt(Int(6*(fdpm.dim + 1)/10 + 4/10) - 1);
# we can just check primes since, if r = r(v, x), and
# vx^s \in <v> then r divides s. If we are given a
# scalar multiple of the identity matrix then
# we will fail when considering the list
# { l : 1 <= l < r, vx^l <> vx^lg } as v <> vg
while r < (19*(fdpm.dim + 2) - 17)/20 do
# check if vx^r = dv for some field element d
if Coefficients(vBasis, vxs[r+1]) <> fail then
# check r is prime and in the right range
return r;
fi;
r := NextPrimeInt(r);
od;
return fail;
end;
# Determine how many vectors (all obtained from v) that
# we will consider. This number depends on whether we
# have a 3-cycle or double transposition
if g.order = 3 then
i := 3;
else
i := 1;
fi;
# Calculate the vectors we will consider
ConvertToMatrixRep(g.matrix);
vs := [];
for k in [1..i] do
Add(vs, v);
v := v*g.matrix;
od;
# Construct the matrices [x,x^2,x^4,...,x^(2^k)]
# where 2^k <= n < 2^(k+1) in log n time
xs := [x];
for k in [1..Int(LogInt(fdpm.dim + 2, 2))] do
ConvertToMatrixRep(xs[k]);
Add(xs, xs[k]^2);
od;
# Calculate Basis objects for the vector spaces
# spanned by the vectors of vs. We can use these
# Basis objects to determine whether a vector lies
# in the corresponding vector space (which is need
# for calculating r(x,vg^i)).
vBases := List(vs, b -> BasisNC(VectorSpace(fdpm.field, [b]), [b]));
# Construct the images of the vectors in vs under
# x, ..., x^(n-1) (in log n time). Then we will have
# the vectors vg^ix, ..., vg^ix^(n-1) for i = 0, 1, 2,
# if g rep. a 3-cycle and i = 0, otherwise
ConvertToMatrixRep(vs);
vImgs := TransposedMat(RECOG.VectorImages(vs, xs));
# Calculate r(vg^i, x) for i = 0,1,2 if
# g is a 3-cycle and, i = 0, if g is a double
# transposition
rs := ListN(vBases, vImgs, R);
# for all vg^i such that r(vg^i, x) = r <> fail we check
# whether the list { l | 1 <= l < r, vx^l <> vx^lg } = 2.
# If the list does have length two then we can compute
# a linked sequence of vectors using vg^i
for k in [1..i] do
# check that r(vg^k, x) succeeded
if rs[k] = fail then
continue;
fi;
# calculate vx^lg for l = 1,..., r-1
mat := vImgs[k]{[1..rs[k]]};
vImgsG := mat * g.matrix;
# calculate { l | 1 <= l < r, vx^l <> vx^lg }
lList := Filtered([1..rs[k]-1], l -> vImgs[k][l+1] <> vImgsG[l+1]);
if Length(lList) = 2 then
# try to construct linked sequence of vectors
u := vImgs[k][lList[1]+1] - vImgsG[lList[1]+1];
if i = 3 then # if g is a 3-cycle
# check whether u in span of vs[k]
d := Coefficients(vBases[k], u);
if d <> fail then
h := -d[1]^-1*RECOG.Power(xs, lList[1]);
return RECOG.VectorImagesUnder(-vs[k], h, rs[k]-1){[1..rs[k]-1]};
fi;
# if u not in span of vs[k] then check
# whether u in span of vs[k]*g = vs[(k + 1) mod 3 + 1]
d := Coefficients(vBases[k^(1,2,3)], u);
if d <> fail then
h := d[1]^-1*RECOG.Power(xs, lList[1]);
return RECOG.VectorImagesUnder(vs[k], h, rs[k]-1){[1..rs[k]-1]};
fi;
# if neither then fail
return fail;
else # if g is a double transposition
# check whether u in span of vs[k]
d := Coefficients(vBases[k], u);
if d <> fail then
h := -d[1]^-1*RECOG.Power(xs, lList[1]);
return RECOG.VectorImagesUnder(vs[k], h, rs[k]-1){[1..rs[k]-1]};
fi;
# otherwise fail
return fail;
fi;
fi;
od;
return fail;
end;
## Functions for finding a basis from the linked sequence of
## vectors found by MoreBasisVectors
RECOG.RowSpaceBasis := function(mat)
# RowSpaceGenerators(<mat>) returns the rows of
# <mat> that form a basis for the row space
# of <mat>. This code borrows from the
# implementation of SemiEchcelonMatDestructive.
local basis, # vectors of mat forming a basis
# for the row space of mat
nzheads, # columns with leading 1 in the
# row echelon form of mat
vectors, # vectors spanning subspace of row
# space of mat
row, # current row of mat
nrows, # number of rows of mat
ncols, # number of columns of mat
x, # a field element
r, c; # row and column indexes
# calc size of matrix
nrows := Length(mat);
ncols := Length(mat[1]);
# list of the non-zero heads, that is, nzheads[i]
# is the first non-element of vectors[i]
nzheads := [];
# vectors contains a linearly independent
# set of vectors spanning a subset of the row-space
vectors := [];
# basis contains vectors that span the same subspace
# spanned by vectors, but the vectors in basis are
# from mat
basis := [];
for r in [1..nrows] do
row := ShallowCopy(mat[r]);
# reduce row using vectors
for c in [1..Length(nzheads)] do
x := row[nzheads[c]];
# if row does not already have a zero in this
# column
if not IsZero(x) then
AddRowVector(row, vectors[c], -x);
fi;
od;
# check row is not the zero row
c := PositionNonZero(row);
if c <= ncols then
# mult row by row[c]^-1 so leading coeff. is 1
MultRowVector(row, row[c]^-1);
Add(vectors, row);
Add(nzheads, c);
# add mat[r] to basis
Add(basis, mat[r]);
fi;
od;
return basis;
end;
RECOG.ExtendToBasisEchelon := function(partial, d, field)
# ExtendToBasisEchelon(<partial>, <d>, <field>)
# returns a basis for <field>^<d> such that <partial>
# is a prefix.
local vectors, v;
# copy partial
vectors := ShallowCopy(partial);
# append identity matrix
Append(vectors, IdentityMat(d, field));
# return row space basis
return RECOG.RowSpaceBasis(vectors);
end;
RECOG.IsIntervalVector := function(v, s)
# IsIntervalVector(<v>, <s>)
# returns [b, supp] if <v> is zero except
# for a uninterrupted sequence of b's within the
# first s positions. If v is not an interval
# vector then fail is returned
local col, b, i, j;
# find first non-zero entry in v
col := 1;
while col <= s and IsZero(v[col]) do
col := col + 1;
od;
# check first non-zero entry is within first s coords
if col > s then
return fail;
fi;
# else set i and b
i := col;
b := v[i];
# find first entry not equal to b
while col <= s and v[col] = b do
col := col + 1;
od;
# set j
j := col;
# check the rest of the entries are zero
while col <= Length(v) do
if not IsZero(v[col]) then
return fail;
fi;
col := col + 1;
od;
return [b, [i, j]];
end;
RECOG.FindBasis := function(x, vs, us, fdpm)
# FindBasis(<x>, <vs>, <us>, <fdpm>) extends the list
# of vectors in <vs> (and in <us>) to a linked basis
# for the fully deleted permutation module, described
# by <fdpm>, when given a random group element <x>.
# The lists are updated as a side effect. If a basis
# is found during the execution of this procedure
# then the basis is returned, otherwise 'fail' is
# returned.
local s, # the length of our linked sequence
P, # change of basis matrix
Pinv, # the inverse of P, if it exists
vsx, # the vectors vis*x w.r.t. basis vs
vsh, # vs*h
inti, # the interval of vi*x
ints, # interval of a sum of vi*x
i, j, # integers
ih, # the image of i under h where x = bh
vsum, # sum of vectors
vsumh, # sum of vectors in vsh
temp, # a row vector
mone, # -One(field)
b; # non-zero field element
s := Length(vs); # Length(us) should be s + 1
# calc vi*x w.r.t. basis vi: for finding interval vectors
P := RECOG.ExtendToBasisEchelon(vs, fdpm.dim, fdpm.field);
ConvertToMatrixRep(P);
Pinv := P^-1;
if Pinv = fail then
return fail;
else
vsx := P * x * Pinv;
ConvertToMatrixRep(vsx);
fi;
# Find scalar b and i,j such that x = bh and j = i^h
# find i <= s such that vix is an interval vector in <vs>
i := 1;
inti := RECOG.IsIntervalVector(vsx[i], s);
while inti = fail and i < s do
i := i + 1;
inti := RECOG.IsIntervalVector(vsx[i], s);
od;
# check whether we found an i
if inti <> fail then # we have; now we find the image of i
ih := fail; # we have not found the image of i yet
vsum := ShallowCopy(vsx[i]);
ConvertToVectorRep(vsum);
# find j s.t. vi*x + ... + vj*x is an interval vector in
# span of vs
for j in [i+1..s] do
AddRowVector(vsum, vsx[j]);
# find interval (if any) of vsum
ints := RECOG.IsIntervalVector(vsum, s);
if ints <> fail then
# we can find the image of i under h and
# the scalar b s.t. x = bh for h in H0
ih := Intersection(inti[2], ints[2]);
if Length(ih) <> 1 then
return fail; # won't happen if input group is Sn or An
else
ih := ih[1];
fi;
if inti[2][1] = ih then
b := inti[1];
else
b := -inti[1];
fi;
break; # leave the for loop
fi;
od;
# if we haven't found i^h try another way
if ih = fail then
vsum := ShallowCopy(vsx[i]);
ConvertToVectorRep(vsum);
# find j s.t. vj*x + ... + vi*x is an interval vector
for j in [i-1,i-2..1] do
AddRowVector(vsum, vsx[j]);
# find interval (if any) of vsum
ints := RECOG.IsIntervalVector(vsum, s);
if ints <> fail then # we can find (i+1)^h and so i^h and b
ih := Difference(inti[2], ints[2]);
if Length(ih) <> 1 then
return fail; # only happens when group not rep. of Sn or An
else
ih := ih[1];
fi;
if inti[2][1] = ih then
b := inti[1];
else
b := -inti[1];
fi;
break; # leave the for loop
fi;
od;
# check we have found ih
if ih = fail then
return fail; # if we have found it now we never will
fi;
fi;
else # look for i < s such that vi*x + v(i+1)*x is an interval vector
i := 1;
temp := ShallowCopy(vsx[i]);
ConvertToVectorRep(temp);
AddRowVector(temp, vsx[i+1]);
inti := RECOG.IsIntervalVector(temp, s);
while inti = fail and i < s - 1 do
i := i + 1;
temp := ShallowCopy(vsx[i]);
ConvertToVectorRep(temp);
AddRowVector(temp, vsx[i+1]);
inti := RECOG.IsIntervalVector(temp, s);
od;
# check whether we found an i
if inti <> fail then # we have; now we find the image of i
ih := fail; # we have not found the image of i yet
# we only try this for i <= s - 2
if i = s - 1 then
vsum := ShallowCopy(vsx[i]);
ConvertToVectorRep(vsum);
AddRowVector(vsum, vsx[i+1]);
# find j s.t. vi*x + v(i+1)^x + ... + vj*x is an interval vector
for j in [i+2..s] do
AddRowVector(vsum, vsx[j]);
# find interval (if any) of vsum
ints := RECOG.IsIntervalVector(vsum, s);
if ints <> fail then # we can find i^h and b
ih := Intersection(inti[2], ints[2]);
if Length(ih) <> 1 then
return fail; # only when group not rep. of Sn or An
else
ih := ih[1];
fi;
if inti[2][1] = ih then
b := inti[1];
else
b := -inti[1];
fi;
break; # leave the for loop
fi;
od;
fi;
# if we haven't found ih try another way
if ih = fail then
vsum := ShallowCopy(vsx[i]);
ConvertToVectorRep(vsum);
AddRowVector(vsum, vsx[i+1]);
# find j s.t. vj*x + ... + vi*x + v(i+1)*x is an interval vector
for j in [i-1,i-2..1] do
AddRowVector(vsum, vsx[j]);
# find interval (if any) of vsum
ints := RECOG.IsIntervalVector(vsum, s);
if ints <> fail then # we can find (i+1)^h and so i^h and b
ih := Difference(inti[2], ints[2]);
if Length(ih) <> 1 then
return fail; # only when group not rep. of Sn or An
else
ih := ih[1];
fi;
if inti[2][1] = ih then
b := inti[1];
else
b := -inti[1];
fi;
break; # leave the for loop
fi;
od;
# check we have found ih
if ih = fail then
return fail; # if we have found it now we never will
fi;
fi;
else
return fail;
fi;
fi;
# found ih and b
# calc -1 in field, for use with AddRowVector
mone := -One(fdpm.field);
# calc vi*h w.r.t. basis vs
vsx := b^-1 * vsx;
# calc vi*h not w.r.t. basis vs
vsh := vs * b^-1*x;
ConvertToMatrixRep(vsh);
# extend linked sequence
vsum := ShallowCopy(vsx[i]);
ConvertToVectorRep(vsum);
vsumh := ShallowCopy(vsh[i]);
ConvertToVectorRep(vsumh);
for j in [i+1..s] do
# calc vi*h + v(i+1)*h + ... + vj*h w.r.t to basis vs
AddRowVector(vsum, vsx[j]);
# calc vi*h + v(i+1)*h + ... + vj*h w.r.t. to standard basis
AddRowVector(vsumh, vsh[j]);
if RECOG.IsIntervalVector(vsum,s) = fail then
# vsum is not an interval vector so vsumh is an interval
# vector not in the span of vs
temp := ShallowCopy(vsumh);
ConvertToVectorRep(temp);
AddRowVector(temp, us[ih]);
Add(us, temp); # us[s+2] := vi*h + ... + vj*h + us[ih]
temp := ShallowCopy(us[s+2]);
ConvertToVectorRep(temp);
AddRowVector(temp, us[s+1], mone);
Add(vs, temp); # vs[s+1] := us[s+2] - us[s+1]
s := s + 1;
# if vs and us span the fully deleted permutation module
if s = fdpm.dim then
return [vs, us{[2..s+1]}];
fi;
fi;
od;
if i <> 1 then
# vsum = v1*h + ... + v(i-1)*h
vsum := ShallowCopy(vsx[1]);
ConvertToVectorRep(vsum);
for temp in [2..i-1] do
AddRowVector(vsum, vsx[temp]);
od;
vsumh := ShallowCopy(vsh[1]);
ConvertToVectorRep(vsumh);
for temp in [2..i-1] do
AddRowVector(vsumh, vsh[temp]);
od;
j := 0;
repeat
if RECOG.IsIntervalVector(vsum, s) = fail then
# vsum is an interval vector not in the span of vs
temp := ShallowCopy(us[ih]);
ConvertToVectorRep(temp);
AddRowVector(temp, vsumh, mone);
Add(us, temp);
temp := ShallowCopy(us[s+2]);
ConvertToVectorRep(temp);
AddRowVector(temp, us[s+1], mone);
Add(vs, temp);
s := s + 1;
if s = fdpm.dim then
# return vs and us if they span the fully deleted permutation
# module. Note: us[1] = u1 = 0
ConvertToMatrixRep(vs);
return [vs, us{[2..s+1]}];
fi;
fi;
j := j + 1;
# calc vj*h + ... + v(i-1)*h
AddRowVector(vsum, vsx[j], mone);
AddRowVector(vsumh, vsh[j], mone);
until j = i - 1;
fi;
# we have not found a basis for the fully deleted permutation
# module yet
return fail;
end;
## Functions for finding the permutation and scalar represented
## by a matrix given the vectors u_2,...,u_(n-delta+1)
RECOG.PositionsNot := function(list, elem)
# PositionsNot(<list>, <elem>) returns a
# list of all the positions in <list> whose
# value is not <elem>.
local pos, posList, zero;
# find position of first element <> elem
posList := [];
pos := PositionNot(list, elem);
# find subsequent positions
while pos <= Length(list) do
Add(posList, pos);
pos := PositionNot(list, elem, pos);
od;
return posList;
end;
RECOG.ExpectedForm := function(row, fdpmDim, nAmbig)
# ExpectedForm(<row>, <fdpmDim>, <nAmbig>) checks
# that <row> is of the form b*(u_ix - u_1x)
# for some b and {ix, 1x}; if so either [b, [ix, 1x]] is
# returned or [-b, [1x, ix]] is returned.
local posNonZero, # the positions of row whose values
# are non-zero
posNotB, # the positions of row whose values
# are not equal to b
b; # a (non-zero) field element
# find the non-zero positions of row
posNonZero := RECOG.PositionsNot(row, Zero(row[1]));
if Length(posNonZero) = 1 then
# if only one position is non-zero then
# 1x = 1 or ix = 1.
return [row[posNonZero[1]], [posNonZero[1] + 1, 1]];
elif Length(posNonZero) = 2 then
# check row = [..b..-b..] for some b
if row[posNonZero[1]] = -row[posNonZero[2]] then
return [row[posNonZero[1]], posNonZero + 1];
else
return fail;
fi;
elif Length(posNonZero) >= fdpmDim - 1 and nAmbig then # only if delta = 2
# determine +/- b: find two entries that are the same
if row[1] = row[2] or row[1] = row[3] then
b := row[1];
elif row[2] = row[3] then
b := row[2];
else # we have three different field elems when there
# can be only two
return fail;
fi;
# find positions in row that are not b
posNotB := RECOG.PositionsNot(row, b);
if IsEmpty(posNotB) then
# {ix,1x} = {1,n} so b(u_ix - u_1x) = bu_ix
return [b, [1, fdpmDim + 2]];
elif Length(posNotB) = 1 then
# {ix, 1x} = {j,n} with j <> 1 and
# row[k] = b for k <> j - 1 and row[j-1] = 2b
if row[posNotB[1]] = 2*b then
return [b, [posNotB[1]+1, fdpmDim + 2]];
fi;
else # there are three different field elements in row
return fail;
fi;
else # wrong number positions with non-zero entries
return fail;
fi;
end;
RECOG.FindPermutation := function(rs, fdpm)
# FindPermutation(<rs>, <fdpmDim>) returns the scalar
# and permutation associated with a given matrix. If
# the matrix is b*x (for some scalar b) then <rs> contains
# the row vectors ui*b*x (where ui = e1 - ei) written
# with respect to the vectors ui. If the matrix is not
# a representation of a permutation then fail is
# returned.
local perm, # a permutation
permList, # the image list of the perm represented
r, # a row
oneIm, # the image of 1 under the permutation
imgsA, # image of ui under bx
imgsB, # image of uj under bx
FindB, # function to determine b from image of ui
b, # the scalar that the matrix represents
pts; # a list of images of points under a permutation
FindB := function(pmb, imgs);
# input is b, [u_ix, 1x] or
# -b and [1_x, u_ix]
if oneIm = imgs[1] then
return -pmb;
else
return pmb;
fi;
end;
# determine +/- b and [1x,2x]
imgsA := RECOG.ExpectedForm(rs[1], fdpm.dim, fdpm.nAmbig);
# determine +/- b and [1x,3x]
imgsB := RECOG.ExpectedForm(rs[2], fdpm.dim, fdpm.nAmbig);
if imgsA <> fail and imgsB <> fail then
# determine 1x
oneIm := Intersection(imgsA[2], imgsB[2]);
if Length(oneIm) = 1 then
oneIm := oneIm[1];
else
return fail;
fi;
permList := [oneIm];
# determine b
b := CallFuncList(FindB, imgsA);
else
return fail;
fi;
# build up permList by looking at rs[i] = u_(i+1)x (w.r.t to basis
# us) for i = 2,...,n-delta
for r in rs do
# check whether rs[i] in is an expected form
imgsB := RECOG.ExpectedForm(r, fdpm.dim, fdpm.nAmbig);
if imgsB = fail then
return fail;
else # rs[i] is in an expected form so imgsB[2] = [1x, (i+1)x]
pts := Difference(imgsB[2], [oneIm]);
# check (i+1)x has been defined uniquely and b is correct
if Length(pts) = 1 and b = CallFuncList(FindB, imgsB) then
Append(permList, pts);
else
return fail;
fi;
fi;
od;
# convert permList into permutation
# if delta = 2 then n will appear in permList but Length(permList) = n - 1
# so we need to append the missing point to permList
Append(permList, Difference([1..Maximum(permList)], permList));
perm := PermListList([1..Length(permList)], permList);
if perm = fail then
return fail;
fi;
return [b, perm];
end;
## The main function that puts all the above together to
## recognise our matrix group:
RECOG.RecogniseFDPM := function(group, field, eps)
# RecogniseFDPM(<group>, <field>, <eps>) is an example
# of using the algorithm described in ' ' to recognise
# a matrix group representation of An or Sn acting on
# their fully deleted permutation module. It actually computes
# data to apply RECOG.FindPermutation.
local fdpm, # a record containing information on
# the fully deleted permutation module
g, # group element representing 3-cycle or double trans
v, # a vector from a linked basis
k, # a counter
vs, # a linked sequence of vectors
us, # the alterate basis
uvs, # output of CompleteBasis
cob, # change of basis matrix, to the basis u_i
# for i = 2,...,n-delta+1
cobi, # inverse matrix
mgens; # the matrix generators
# construct matrix group
mgens := GeneratorsOfGroup(group);
# information on the fully deleted permutation module
fdpm := rec( field := field,
fieldChar := Characteristic(field),
dim := DimensionOfMatrixGroup(group),
nAmbig := (DimensionOfMatrixGroup(group) + 2) mod
Characteristic(field) = 0
);
# check the dimensions allow for the possibility that group is a
# representation of An or Sn on its fully deleted permutation module
if (fdpm.dim + 1) mod fdpm.fieldChar = 0 then
return fail;
fi;
Info(InfoFDPM, 2, "Searching for 3-cycle or double-transposition.");
# find 3-cycle or double transposition
if fdpm.fieldChar = 3 then
# check fdpm.dim is largest for IsPreDoubleTransposition to
# work
if fdpm.dim < 6 then
ErrorNoReturn( "This function only works for matrix groups of dimension",
" at least 6 over a field of this characteristic.");
return fail;
fi;
# search for double transpositions
g := RECOG.IterateWithRandomElements(
m -> RECOG.ConstructDoubleTransposition(m, fdpm),
RECOG.LogRat(eps^-1, 2)*(RootInt(fdpm.dim + 2) + 1)*41,
# 1/0.0249 < 41
group);
else
# check fdpm.dim is largest for IsPre3Cycle to work
if fdpm.dim < 4 then
ErrorNoReturn( "This function only works for matrix groups of dimension",
" at least 4 over a field of this characteristic.");
return fail;
fi;
# search for a 3-cycle
g := RECOG.IterateWithRandomElements(
m -> RECOG.Construct3Cycle(m, fdpm),
RECOG.LogRat(eps^-1, 2)*(RootInt(fdpm.dim+2,3) + 1)*15,
# 1/0.07 < 15
group);
fi;
# Note: when the field characteristic is >= 5 we could search
# for both 3-cycles and double transpositions. However we would still
# require about as many random element selections as if we searched
# only for 3-cycles and, if we found a double transposition, we would
# require many more random element selections for MoreBasisVectors.
# This is especially important when group is not a representation of
# An or Sn.
# check we found a 3-cycle or double transposition
if g = fail then
Info(InfoFDPM, 2, "Could not find 3-cycle or double-transposition.");
return fail;
fi;
if g.order = 3 then
Info(InfoFDPM, 2, "Found 3-cycle.");
else
Info(InfoFDPM, 2, "Found double-transposition.");
fi;
# find first basis vector
v := RECOG.FindBasisElement(g, group, eps, fdpm);
if v = fail then
Info(InfoFDPM, 2, "Could not find basis element.");
return fail;
fi;
Info(InfoFDPM, 2, "Found first basis element.");
# extend basis
if g.order = 3 then
vs := RECOG.IterateWithRandomElements(
m -> RECOG.MoreBasisVectors(m, g, v, fdpm),
34*RECOG.LogRat(eps^-1, 2)*RECOG.LogRat(fdpm.dim + 2, 2),
# 1/0.03 < 34
group);
else
vs := RECOG.IterateWithRandomElements(
m -> RECOG.MoreBasisVectors(m, g, v, fdpm),
2000*RECOG.LogRat(eps^-1, 2)*RECOG.LogRat(fdpm.dim + 2, 2),
group);
fi;
if vs = fail then
Info(InfoFDPM, 2, "Could not find linked sequence of vectors.");
return fail;
fi;
Info(InfoFDPM, 2, "Found linked sequence of vectors.");
# complete basis
# compute start of alternate basis
us := [Zero(vs[1])];
for k in [1..Length(vs)] do
Add(us, us[k] + vs[k]);
od;
ConvertToMatrixRep(us);
uvs := RECOG.IterateWithRandomElements(
m -> RECOG.FindBasis(m, vs, us, fdpm),
RECOG.LogRat(fdpm.dim + 2, 2)*( RECOG.LogRat(eps^-1, 2)
+ RECOG.LogRat(fdpm.dim + 2, 2)),
group);
if uvs = fail then
Info(InfoFDPM, 2, "Could not complete basis.");
return fail;
fi;
Info(InfoFDPM, 2, "Found change of basis matrix.");
# determine permutations and scalars: uvs[2] contains the
# change of basis matrix that takes group elements into
# our prefered form
cob := uvs[2];
ConvertToMatrixRep(cob);
cobi := cob^-1;
if cobi = fail then
return fail;
fi;
return rec( cob := cob, cobi := cobi, fdpm := fdpm );
end;