-
Notifications
You must be signed in to change notification settings - Fork 175
Allow optionally passing a random source to RandomList, and to Random for collections
#2204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,58 @@ | ||
| randomTest := function(collection, method, checkin...) | ||
| local test1, test2, test3, test4, test5, test6, localgen, checkmethod; | ||
| if Length(checkin) = 0 then | ||
| checkmethod := \in; | ||
| else | ||
| checkmethod := checkin[1]; | ||
| fi; | ||
| # Perform a variety of tests on Random Sources and functions which create | ||
| # random objects. | ||
| # | ||
| # This function is used for a variety is different tests: | ||
| # | ||
| # Test that 'Random(C)' and 'Random(GlobalMersenneTwister, C)' produce | ||
| # the same answer. | ||
| # | ||
| # Test that 'Random(rs,C)' only uses 'rs', and no other source of random | ||
| # | ||
| # Test Random and RandomList | ||
| # | ||
| # Where there is a global instance of a random source | ||
| # (GlobalMersenneTwister and GlobalRandomSource), they produce the same | ||
| # sequence of answers as a new instance of the same random source. | ||
|
|
||
| # filter: The type of random source we are testing. | ||
| # global_rs: A pre-existing object of type 'filter'. | ||
| # randfunc(rs, C): A two argument function which creates random elements of C using | ||
| # 'rs' as the source. | ||
| # global_randfunc(C): A one argument function which is equivalent to | ||
| # {x} -> rand(global_rs, x). This lets us check 'Random(C)' and | ||
| # 'Random(GlobalMersenneTwister,C)' produce the same answer when testing | ||
| # GlobalMersenneTwister. For other random sources, this can just | ||
| # be set to {x} -> rand(global_rs,x). | ||
| # collection: The object (usually a collection) to find random members of. | ||
| # checkin(e, C): returns if e is in C (usually checkin is '\in'). | ||
|
|
||
| randomTestInner := function(filter, global_rs, global_randfunc, randfunc, collection, checkin) | ||
| local test1, test2, test3, test4, test5, test6, local_rs; | ||
|
|
||
| # We do a single call first, to deal with calling Random causing extra attributes | ||
| # of 'collection' to be set, changing the dispatch | ||
| method(collection); | ||
| randfunc(collection); | ||
|
|
||
| # Firstly, we will generate a base list | ||
| Init(GlobalMersenneTwister, 6); | ||
| test1 := List([1..1000], x -> method(collection)); | ||
| # test2 should = test1 | ||
| Init(GlobalMersenneTwister, 6); | ||
| test2 := List([1..1000], x -> method(collection)); | ||
| Init(global_rs, 6); | ||
| test1 := List([1..1000], x -> global_randfunc(collection)); | ||
| # test2 should equal test1 | ||
| Init(global_rs, 6); | ||
| test2 := List([1..1000], x -> global_randfunc(collection)); | ||
| # test3 should also = test1 | ||
| Init(GlobalMersenneTwister, 6); | ||
| test3 := List([1..1000], x -> method(GlobalMersenneTwister, collection)); | ||
| Init(global_rs, 6); | ||
| test3 := List([1..1000], x -> randfunc(global_rs, collection)); | ||
| # test4 should be different (as it came from a different seed) | ||
| Init(GlobalMersenneTwister, 8); | ||
| test4 := List([1..1000], x -> method(collection)); | ||
| Init(global_rs, 8); | ||
| test4 := List([1..1000], x -> global_randfunc(collection)); | ||
| # test5 should be the same as test4, as it is made from seed 8 | ||
| # test6 should be the same as test1. Also, it checks that making test5 | ||
| # did not touch the global source at all. | ||
| Init(GlobalMersenneTwister, 8); | ||
| localgen := RandomSource(IsMersenneTwister, 6); | ||
| test5 := List([1..1000], x -> method(localgen, collection)); | ||
| test6 := List([1..1000], x -> method(collection)); | ||
| if ForAny(Concatenation(test1, test2, test3, test4, test5, test6), x -> not (checkmethod(x, collection)) ) then | ||
| Init(global_rs, 8); | ||
| local_rs := RandomSource(filter, 6); | ||
| test5 := List([1..1000], x -> randfunc(local_rs, collection)); | ||
| test6 := List([1..1000], x -> global_randfunc(collection)); | ||
| if ForAny(Concatenation(test1, test2, test3, test4, test5, test6), x -> not (checkin(x, collection)) ) then | ||
| Print("Random member outside collection: ", collection,"\n"); | ||
| fi; | ||
| if test1 <> test2 then | ||
|
|
@@ -45,49 +68,81 @@ randomTest := function(collection, method, checkin...) | |
| Print("Alt gen broken: ", collection, "\n"); | ||
| fi; | ||
| if test4 <> test6 then | ||
| Print("Random with a passed in seed affected the global generator: ", collection, "\n"); | ||
| Print("Random with a passed in seed affected the global source: ", collection, "\n"); | ||
| fi; | ||
| end;; | ||
|
|
||
|
|
||
| # A special test for collections of size 1 | ||
| randomTestForSizeOneCollection := function(collection, method) | ||
| local i, val, localgen, intlist1, intlist2; | ||
| if Size(collection) <> 1 then | ||
| Print("randomTestForSizeOneCollection is only for collections of size 1"); | ||
| return; | ||
| fi; | ||
| # Here we can't check different seeds produce different answers | ||
| # We do check that the random source is not used, for efficency. | ||
| randomTestForSizeOneCollectionInner := function(filter, global_rs, global_randfunc, randfunc, collection, checkin) | ||
| local i, val, local_rs, intlist1, intlist2; | ||
|
|
||
| val := Representative(collection); | ||
|
|
||
| Init(GlobalMersenneTwister, 6); | ||
| intlist1 := List([1..1000], x -> Random([1..10])); | ||
| Init(global_rs, 6); | ||
| intlist1 := List([1..10], x -> global_randfunc([1..1000])); | ||
|
|
||
| for i in [1..1000] do | ||
| if method(collection) <> val then | ||
| for i in [1..100] do | ||
| if global_randfunc(collection) <> val then | ||
| Print("Random returned something outside collection :", collection, ":", val); | ||
| fi; | ||
| od; | ||
|
|
||
| for i in [1..1000] do | ||
| if method(GlobalMersenneTwister, collection) <> val then | ||
| for i in [1..100] do | ||
| if randfunc(global_rs, collection) <> val then | ||
| Print("Random returned something outside collection :", collection, ":", val); | ||
| fi; | ||
| od; | ||
|
|
||
| localgen := RandomSource(IsMersenneTwister, 6); | ||
| local_rs := RandomSource(filter, 6); | ||
|
|
||
| Init(GlobalMersenneTwister, 6); | ||
| for i in [1..1000] do | ||
| if method(localgen, collection) <> val then | ||
| Init(global_rs, 6); | ||
| for i in [1..100] do | ||
| if randfunc(local_rs, collection) <> val then | ||
| Print("Random returned something outside collection :", collection, ":", val); | ||
| fi; | ||
| od; | ||
|
|
||
| # The previous loop should not have affected GlobalMersenneTwister, | ||
| # The previous loop should not have affected global_rs, | ||
| # so this should be the same as intlist1 | ||
| intlist2 := List([1..1000], x -> Random([1..10])); | ||
| intlist2 := List([1..10], x -> global_randfunc([1..1000])); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just query the state before and after, and compare it? Make an immutable copy to make sure it's not been modified?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I quite like testing the actual sequence. However, it might be nice to also test the states are equal as well! |
||
|
|
||
| if intlist1 <> intlist2 then | ||
| Print("Random read from local gen affected global gen: ", collection); | ||
| fi; | ||
| end;; | ||
|
|
||
|
|
||
| randomTest := function(collection, randfunc, checkin...) | ||
| local sizeone, randchecker; | ||
| if Length(checkin) = 0 then | ||
| checkin := \in; | ||
| else | ||
| checkin := checkin[1]; | ||
| fi; | ||
|
|
||
| # Make a best attempt to find if the collection is size 1. | ||
| # There are implementations of random for objects which do not support | ||
| # Size or IsTrivial, e.g. PadicExtensionNumberFamily | ||
| if IsList(collection) then | ||
| sizeone := (Size(collection) = 1); | ||
| elif IsCollection(collection) then | ||
| sizeone := IsTrivial(collection); | ||
| else | ||
| sizeone := false; | ||
| fi; | ||
|
|
||
| if sizeone then | ||
| randchecker := randomTestForSizeOneCollectionInner; | ||
| else | ||
| randchecker := randomTestInner; | ||
| fi; | ||
|
|
||
| randchecker(IsMersenneTwister, | ||
| GlobalMersenneTwister, x -> randfunc(x), randfunc, collection, checkin); | ||
| randchecker(IsGAPRandomSource, | ||
| GlobalRandomSource, x -> randfunc(GlobalRandomSource, x), randfunc, | ||
| collection, checkin); | ||
| end; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am sorry for being nitpicky, but: now that value
-8seems rather arbitrary, and I'd really prefer if there was a comment here (resp. before theInstallMethod...invocation) that explains why it has the value; i.e. to push it below the method forIsList?!?I wonder if this value could/should instead be computed? Like, using
RankFilter(IsList) - RankFilter(IsCollection and IsFinite) - 6instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, shouldn't those methods in
random.giuseIsList and IsFiniteinstead ofIsListanyway? They require finite lists, after all (ifLength(list)returnedinfinity, they'd have a problem). Wouldn't that also resolve the rank problem?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
IsList and IsFinitewould fix the generators provided in the library, but still cause an infinite loop in any other random generator (like the one in IO).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the comment already explain? We need to rank below Random(SomeRandomSource, IsList) for any SomeRandomSource.