loops/gap/random.gi

209 lines
7.5 KiB
Plaintext

#############################################################################
##
#W random.gi Random loops [loops]
##
#H @(#)$Id: random.gi, v 2.1.0 2008/12/08 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
##
#O RandomQuasigroup( n, iter )
##
## Returns a random quasigroup of order <n> using <iter> random steps to move into an
## initial position in the Jacobson & Matthews method.
## (MATH) This is an implementation of the Jacobson & Matthews random walk method with
## some ad hoc mixing parameters. We always start with the cyclic group of order n.
## It is proved in Jacobson & Matthews that a random walk in the graph visits all
## latin squares uniformly. (But the problem is how to move to an initial position.)
InstallMethod( RandomQuasigroup, "for two integers",
[ IsInt, IsInt ],
function( n, iter )
local f, x, y, z, is_proper, xx, yy, zz, random_walk_step, i, ct;
if not ( n > 0 and iter > 0 ) then
Error("LOOPS: the arguments must be positive integers.");
fi;
# n=1 is a special case
if n=1 then
return LoopByCayleyTable( [[1]] );
fi;
# initializing funciton for proper and improper Latin squares
# the meaning of f(x,y,z)=1 is that there is symbol z in row x and column y
f := List([1..n], i -> List([1..n], j -> 0*[1..n]));
# cyclic group of order n on symbols [1..n]
for x in [1..n] do for y in [1..n] do
z := x+y-1;
if z > n then
z := z - n;
fi;
f[x][y][z] := 1;
od; od;
is_proper := true; # proper latin square to start with
# one random walk step
random_walk_step := function()
local x, y, z, triples, x2, y2, z2, triple;
if is_proper then
repeat
x := Random([1..n]);
y := Random([1..n]);
z := Random([1..n]);
until f[x][y][z]=0;
fi;
if not is_proper then # use unique point with f(x,y,z)=-1
x := xx;
y := yy;
z := zz;
fi;
# find all suitable triples
x2 := Filtered( [1..n], a -> f[a][y][z] = 1 );
y2 := Filtered( [1..n], a -> f[x][a][z] = 1 );
z2 := Filtered( [1..n], a -> f[x][y][a] = 1 );
# pick a random suitable triple
x2 := Random( x2 );
y2 := Random( y2 );
z2 := Random( z2 );
# shuffle values
f[x][y][z] := f[x][y][z] + 1;
f[x][y2][z2] := f[x][y2][z2] + 1;
f[x2][y][z2] := f[x2][y][z2] + 1;
f[x2][y2][z] := f[x2][y2][z] + 1;
f[x2][y][z] := f[x2][y][z] - 1;
f[x][y2][z] := f[x][y2][z] - 1;
f[x][y][z2] := f[x][y][z2] - 1;
f[x2][y2][z2] := f[x2][y2][z2] - 1;
# determine properness
if f[x2][y2][z2] = 0 then
is_proper := true;
else
is_proper := false;
xx := x2;
yy := y2;
zz := z2;
fi;
end;
# moving into an initial point in the graph
for i in [1..iter] do
random_walk_step();
od;
# finding a proper square nearby
while not is_proper do
random_walk_step();
od;
# constructing the multiplication table from the function
ct := List([1..n], i->[1..n]);
for x in [1..n] do for y in [1..n] do
ct[x][y] := Filtered([1..n], z -> f[x][y][z] = 1)[ 1 ];
od; od;
return QuasigroupByCayleyTable( ct );
end);
#############################################################################
##
#O RandomQuasigroup( n )
##
## Returns random quasigroup of order <n> using n^3 steps to move into an
## initial position in the Jacobson & Matthews algorithm.
InstallOtherMethod( RandomQuasigroup, "for a positive integer",
[ IsInt ],
function( n )
return RandomQuasigroup( n, n^3 );
end);
#############################################################################
##
#O RandomLoop( n, iter )
##
## Returns a normalized random quasigroup of order <n>.
InstallOtherMethod( RandomLoop, "for two positive integers",
[ IsInt, IsInt ],
function( n, iter )
local Q;
Q := RandomQuasigroup( n, iter );
return LoopByCayleyTable( NormalizedQuasigroupTable( CayleyTable( Q ) ) );
end);
#############################################################################
##
#O RandomLoop( n )
##
## Returns random loop of order <n> using n^3 steps to move into an
## initial position in the Jacobson & Matthews algorithm.
InstallOtherMethod( RandomLoop, "for a positive integer",
[ IsInt ],
function( n )
local Q;
Q := RandomQuasigroup( n );
return LoopByCayleyTable( NormalizedQuasigroupTable( CayleyTable( Q ) ) );
end);
#############################################################################
##
#O RandomNilpotentLoop( lst )
##
## lst must be a list of positive integers and/or finite abelian groups.
## If lst = [n] and n is an integer, returns a random abelian group of order n.
## If lst = [A] and A is an abelian group, returns AsLoop( A ).
## If lst = [a1,..,am] and a1 is an integer, returns a central extension
## of an abelian group of order a1 by RandomNilpotentLoop( [a2,...,am] ).
## If lst = [a1,..,am] and a1 is a group, returns a central extension
## of a1 by RandomNilpotentLoop( [a2,...,am] ).
## To determine the nilpotency class CL of the resulting loop, assume that
## lst has length at least 2, contains only integers bigger than 1 (the "1" entries are trivial),
## and let m be the last entry of lst. If m>2 then CL=Length(lst), else CL = Length(lst)-1.
InstallMethod( RandomNilpotentLoop, "for a list of abelian groups and positive integers",
[ IsList ],
function( lst )
local n, K, F, f, theta, i, j, phi;
if IsEmpty( lst ) then
Error("LOOPS: the argument must be a list of finite abelian groups and/or positive integers.");
fi;
n := lst[1];
if not ( IsPosInt( n ) or (IsGroup( n ) and IsAbelian( n ) and IsFinite( n ) ) ) then
Error("LOOPS: the argument must be a list of finite abelian groups and/or positive integers.");
fi;
# central subloop
if IsInt( n ) then # first argument is a positive integer
K := IntoLoop( Random( AllGroups( n, IsAbelian ) ) );
else # first argument is an abelian group
K := IntoLoop( n );
fi;
# factor loop
if Length( lst ) = 1 then # trivial factor
F := LoopByCayleyTable( [ [ 1 ] ] );
else
F := RandomNilpotentLoop( lst{[2..Length(lst)]} );
fi;
# cocycle (random)
f := Size( F );
theta := List([1..f], i->[1..f]);
for i in [2..f] do
theta[1][i]:=1;
od;
for i in [2..f] do for j in [2..f] do
theta[i][j] := Random( [1..Size(K)] );
od; od;
# To guarantee that the resulting loop has maximal nilpotency class,
# it suffices to make sure that theta is not symmetric.
if f>2 then
i := Random([2..f]);
j := Random( Difference( [2..f], [i] ) );
theta[i][j] := Random( Difference( [1..Size(K)], [ theta[j][i] ] ) );
fi;
# trivial action
phi := List( [1..f], i->() );
# the loop
return LoopByExtension( K, F, phi, theta );
end);