Skip to content

Commit 4dedfee

Browse files
ChrisJeffersonfingolfin
authored andcommitted
Improve random testing
1 parent 1a841f1 commit 4dedfee

2 files changed

Lines changed: 111 additions & 52 deletions

File tree

tst/testinstall/random.tst

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ gap> ReadGapRoot( "tst/testrandom.g" );
55
gap> randomTest([1,2,3], RandomList);
66
gap> randomTest([1..100], RandomList);
77
gap> randomTest("abcdef", RandomList);
8-
gap> randomTestForSizeOneCollection([1], RandomList);
8+
gap> randomTest(BlistList([1..100],[1,3..99]), RandomList);
9+
gap> randomTest([1], RandomList);
910

1011
#
1112
# fields and rings
@@ -27,7 +28,7 @@ gap> randomTest(GF(257^2), Random);
2728
gap> randomTest(GF(2^20), Random);
2829

2930
# ZmodnZ
30-
gap> randomTestForSizeOneCollection(Integers mod 1, Random);
31+
gap> randomTest(Integers mod 1, Random);
3132
gap> randomTest(Integers mod 4, Random);
3233
gap> randomTest(Integers mod 100, Random);
3334

@@ -43,14 +44,14 @@ gap> randomTest(FreeMagma(1), Random);
4344
gap> randomTest(FreeMagma(2), Random);
4445

4546
#
46-
gap> randomTestForSizeOneCollection(FreeMonoid(0), Random);
47+
gap> randomTest(FreeMonoid(0), Random);
4748
gap> randomTest(FreeMonoid(1), Random);
4849
gap> randomTest(FreeMonoid(2), Random);
4950

5051
#
5152
# permutation groups
5253
#
53-
gap> randomTestForSizeOneCollection(TrivialGroup(IsPermGroup), Random);
54+
gap> randomTest(TrivialGroup(IsPermGroup), Random);
5455

5556
#
5657
gap> randomTest(SymmetricGroup(2), Random);
@@ -71,15 +72,15 @@ gap> randomTest(PrimitiveGroup(5,3)*(1,4,6), Random);
7172
#
7273
# pc groups
7374
#
74-
gap> randomTestForSizeOneCollection(TrivialGroup(IsPcGroup), Random);
75+
gap> randomTest(TrivialGroup(IsPcGroup), Random);
7576
gap> randomTest(AbelianGroup(IsPcGroup, [2]), Random);
7677
gap> randomTest(AbelianGroup(IsPcGroup, [2,3,4,5]), Random);
7778

7879
#
7980
# fp groups
8081
#
81-
gap> randomTestForSizeOneCollection(TrivialGroup(IsFpGroup), Random);
82-
gap> randomTestForSizeOneCollection(FreeGroup(0), Random);
82+
gap> randomTest(TrivialGroup(IsFpGroup), Random);
83+
gap> randomTest(FreeGroup(0), Random);
8384
gap> randomTest(FreeGroup(1), Random);
8485
gap> randomTest(FreeGroup(2), Random);
8586
gap> randomTest(FreeGroup(infinity), Random);
@@ -88,9 +89,9 @@ gap> randomTest(DihedralGroup(IsFpGroup, 6), Random);
8889
#
8990
# matrix groups
9091
#
91-
gap> randomTestForSizeOneCollection(CyclicGroup(IsMatrixGroup, GF(2), 1), Random);
92-
gap> randomTestForSizeOneCollection(CyclicGroup(IsMatrixGroup, GF(9), 1), Random);
93-
gap> randomTestForSizeOneCollection(CyclicGroup(IsMatrixGroup, Rationals, 1), Random);
92+
gap> randomTest(CyclicGroup(IsMatrixGroup, GF(2), 1), Random);
93+
gap> randomTest(CyclicGroup(IsMatrixGroup, GF(9), 1), Random);
94+
gap> randomTest(CyclicGroup(IsMatrixGroup, Rationals, 1), Random);
9495

9596
#
9697
gap> randomTest(CyclicGroup(IsMatrixGroup, GF(2), 3), Random);
@@ -114,8 +115,11 @@ gap> randomTest(DoubleCoset(Group(()), (1,2), Group((1,2,3)) ), Random);
114115
gap> randomTest(DoubleCoset(Group((1,2),(3,4)), (), Group((1,2,3)) ), Random);
115116

116117
#
117-
gap> randomTestForSizeOneCollection([1], Random);
118+
gap> randomTest([1], Random);
118119
gap> randomTest([1..10], Random);
120+
gap> randomTest([1..2], Random);
121+
gap> randomTest([0, 10..1000], Random);
122+
gap> randomTest("cheese", Random);
119123
gap> randomTest([1,-6,"cheese", Group(())], Random);
120124

121125
#

tst/testrandom.g

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,58 @@
1-
randomTest := function(collection, method, checkin...)
2-
local test1, test2, test3, test4, test5, test6, localgen, checkmethod;
3-
if Length(checkin) = 0 then
4-
checkmethod := \in;
5-
else
6-
checkmethod := checkin[1];
7-
fi;
1+
# Perform a variety of tests on Random Sources and functions which create
2+
# random objects.
3+
#
4+
# This function is used for a variety is different tests:
5+
#
6+
# Test that 'Random(C)' and 'Random(GlobalMersenneTwister, C)' produce
7+
# the same answer.
8+
#
9+
# Test that 'Random(rs,C)' only uses 'rs', and no other source of random
10+
#
11+
# Test Random and RandomList
12+
#
13+
# Where there is a global instance of a random source
14+
# (GlobalMersenneTwister and GlobalRandomSource), they produce the same
15+
# sequence of answers as a new instance of the same random source.
16+
17+
# filter: The type of random source we are testing.
18+
# global_rs: A pre-existing object of type 'filter'.
19+
# randfunc(rs, C): A two argument function which creates random elements of C using
20+
# 'rs' as the source.
21+
# global_randfunc(C): A one argument function which is equivalent to
22+
# {x} -> rand(global_rs, x). This lets us check 'Random(C)' and
23+
# 'Random(GlobalMersenneTwister,C)' produce the same answer when testing
24+
# GlobalMersenneTwister. For other random sources, this can just
25+
# be set to {x} -> rand(global_rs,x).
26+
# collection: The object (usually a collection) to find random members of.
27+
# checkin(e, C): returns if e is in C (usually checkin is '\in').
28+
29+
randomTestInner := function(filter, global_rs, global_randfunc, randfunc, collection, checkin)
30+
local test1, test2, test3, test4, test5, test6, local_rs;
831

932
# We do a single call first, to deal with calling Random causing extra attributes
1033
# of 'collection' to be set, changing the dispatch
11-
method(collection);
34+
randfunc(collection);
1235

1336
# Firstly, we will generate a base list
14-
Init(GlobalMersenneTwister, 6);
15-
test1 := List([1..1000], x -> method(collection));
16-
# test2 should = test1
17-
Init(GlobalMersenneTwister, 6);
18-
test2 := List([1..1000], x -> method(collection));
37+
Init(global_rs, 6);
38+
test1 := List([1..1000], x -> global_randfunc(collection));
39+
# test2 should equal test1
40+
Init(global_rs, 6);
41+
test2 := List([1..1000], x -> global_randfunc(collection));
1942
# test3 should also = test1
20-
Init(GlobalMersenneTwister, 6);
21-
test3 := List([1..1000], x -> method(GlobalMersenneTwister, collection));
43+
Init(global_rs, 6);
44+
test3 := List([1..1000], x -> randfunc(global_rs, collection));
2245
# test4 should be different (as it came from a different seed)
23-
Init(GlobalMersenneTwister, 8);
24-
test4 := List([1..1000], x -> method(collection));
46+
Init(global_rs, 8);
47+
test4 := List([1..1000], x -> global_randfunc(collection));
2548
# test5 should be the same as test4, as it is made from seed 8
2649
# test6 should be the same as test1. Also, it checks that making test5
2750
# did not touch the global source at all.
28-
Init(GlobalMersenneTwister, 8);
29-
localgen := RandomSource(IsMersenneTwister, 6);
30-
test5 := List([1..1000], x -> method(localgen, collection));
31-
test6 := List([1..1000], x -> method(collection));
32-
if ForAny(Concatenation(test1, test2, test3, test4, test5, test6), x -> not (checkmethod(x, collection)) ) then
51+
Init(global_rs, 8);
52+
local_rs := RandomSource(filter, 6);
53+
test5 := List([1..1000], x -> randfunc(local_rs, collection));
54+
test6 := List([1..1000], x -> global_randfunc(collection));
55+
if ForAny(Concatenation(test1, test2, test3, test4, test5, test6), x -> not (checkin(x, collection)) ) then
3356
Print("Random member outside collection: ", collection,"\n");
3457
fi;
3558
if test1 <> test2 then
@@ -45,49 +68,81 @@ randomTest := function(collection, method, checkin...)
4568
Print("Alt gen broken: ", collection, "\n");
4669
fi;
4770
if test4 <> test6 then
48-
Print("Random with a passed in seed affected the global generator: ", collection, "\n");
71+
Print("Random with a passed in seed affected the global source: ", collection, "\n");
4972
fi;
5073
end;;
5174

75+
5276
# A special test for collections of size 1
53-
randomTestForSizeOneCollection := function(collection, method)
54-
local i, val, localgen, intlist1, intlist2;
55-
if Size(collection) <> 1 then
56-
Print("randomTestForSizeOneCollection is only for collections of size 1");
57-
return;
58-
fi;
77+
# Here we can't check different seeds produce different answers
78+
# We do check that the random source is not used, for efficency.
79+
randomTestForSizeOneCollectionInner := function(filter, global_rs, global_randfunc, randfunc, collection, checkin)
80+
local i, val, local_rs, intlist1, intlist2;
5981

6082
val := Representative(collection);
6183

62-
Init(GlobalMersenneTwister, 6);
63-
intlist1 := List([1..1000], x -> Random([1..10]));
84+
Init(global_rs, 6);
85+
intlist1 := List([1..10], x -> global_randfunc([1..1000]));
6486

65-
for i in [1..1000] do
66-
if method(collection) <> val then
87+
for i in [1..100] do
88+
if global_randfunc(collection) <> val then
6789
Print("Random returned something outside collection :", collection, ":", val);
6890
fi;
6991
od;
7092

71-
for i in [1..1000] do
72-
if method(GlobalMersenneTwister, collection) <> val then
93+
for i in [1..100] do
94+
if randfunc(global_rs, collection) <> val then
7395
Print("Random returned something outside collection :", collection, ":", val);
7496
fi;
7597
od;
7698

77-
localgen := RandomSource(IsMersenneTwister, 6);
99+
local_rs := RandomSource(filter, 6);
78100

79-
Init(GlobalMersenneTwister, 6);
80-
for i in [1..1000] do
81-
if method(localgen, collection) <> val then
101+
Init(global_rs, 6);
102+
for i in [1..100] do
103+
if randfunc(local_rs, collection) <> val then
82104
Print("Random returned something outside collection :", collection, ":", val);
83105
fi;
84106
od;
85107

86-
# The previous loop should not have affected GlobalMersenneTwister,
108+
# The previous loop should not have affected global_rs,
87109
# so this should be the same as intlist1
88-
intlist2 := List([1..1000], x -> Random([1..10]));
110+
intlist2 := List([1..10], x -> global_randfunc([1..1000]));
89111

90112
if intlist1 <> intlist2 then
91113
Print("Random read from local gen affected global gen: ", collection);
92114
fi;
93115
end;;
116+
117+
118+
randomTest := function(collection, randfunc, checkin...)
119+
local sizeone, randchecker;
120+
if Length(checkin) = 0 then
121+
checkin := \in;
122+
else
123+
checkin := checkin[1];
124+
fi;
125+
126+
# Make a best attempt to find if the collection is size 1.
127+
# There are implementations of random for objects which do not support
128+
# Size or IsTrivial, e.g. PadicExtensionNumberFamily
129+
if IsList(collection) then
130+
sizeone := (Size(collection) = 1);
131+
elif IsCollection(collection) then
132+
sizeone := IsTrivial(collection);
133+
else
134+
sizeone := false;
135+
fi;
136+
137+
if sizeone then
138+
randchecker := randomTestForSizeOneCollectionInner;
139+
else
140+
randchecker := randomTestInner;
141+
fi;
142+
143+
randchecker(IsMersenneTwister,
144+
GlobalMersenneTwister, x -> randfunc(x), randfunc, collection, checkin);
145+
randchecker(IsGAPRandomSource,
146+
GlobalRandomSource, x -> randfunc(GlobalRandomSource, x), randfunc,
147+
collection, checkin);
148+
end;

0 commit comments

Comments
 (0)