diff --git a/doc/recog.bib b/doc/recog.bib index 20ffe346..56a41d39 100644 --- a/doc/recog.bib +++ b/doc/recog.bib @@ -599,6 +599,13 @@ @incollection {CLG98 NOTE = {https://doi.org/10.1017/CBO9780511565830.007}, } +@article{C12, + AUTHOR = {Conder, Jonathan}, + TITLE = {Algorithms for Permutation Groups}, + YEAR = {2012}, + NOTE = {TODO}, +} + @article {CLGO06, AUTHOR = {Conder, M. D. E. and Leedham-Green, C. R. and O'Brien, E. A.}, TITLE = {Constructive recognition of {${\rm PSL}(2,q)$}}, diff --git a/gap/generic/SnAnUnknownDegree.gi b/gap/generic/SnAnUnknownDegree.gi index 31106e85..aa6465cf 100644 --- a/gap/generic/SnAnUnknownDegree.gi +++ b/gap/generic/SnAnUnknownDegree.gi @@ -17,7 +17,81 @@ ## . ## ############################################################################# -# + +## General Strategy +## ---------------- +## +## Here we describe the general strategy that is used to recognise if a group +## is ismorphic to `An` or `Sn` for `n >= 7`. +## +## We have to be careful what we do for small degrees. +## If we pass an `A5`, `S5`, `A6`, `S6` into `SnAnUnknownDegree`, then it will not recognize it. +## If the input acts on a space with a large dimension, then this can take forever. +## +## - We assume that our input group `G` is a projective irreducible matrix group. +## - Deduce an upper bound `N` for the degree of `An`, `Sn` by using bounds provided in [KL90]. +## - Look at some orders and deduce a lower bound `M` for the degree. +## - If `M > N`, then we excluded `An`, `Sn`. +## - If `M <= 6`, then we return `TemporaryFailure`. +## - TODO: If `N > 20`, use Magma Code `GuessSnAnDegree` to guess the degree by element orders. +## - TODO: If `GuessSnAnDegree` was used, then we check if we can found +## an element of order in the interval `[N - m, N + m]`. +## - Otherwise, we continue. +## - Now we can assume for the degree `n >= 7`. We need to ensure this, since +## `SnAnUnknownDegree` cannot recognise `A5`, `S5`, `A6`, `S6` and could run +## for a considerable time. +## - Monte-Carlo: Try to compute standard generators and degree `n` of +## largest `An < G` via the algorithm in [JLNP13]. +## - Try to compute an isomorphism from `G` to `An` or `Sn`. +## - If `n < 11`, then use methods from Jonathan Conder (phd thesis) +## - Otherwise, we have `n >= 11` and use methods from `SnAnKnownDegree` in [BLGN+03] +## +## Changes +## ------- +## Here we collect the changes in `SnAnUnknownDegree` compared to a "vanilla" +## implementation of the algorithm according to the paper [JLNP13]. +## +## - Computation of upper bound `N` done as in `[L84]` and `[KL90]`. +## - Computation of lower bound `M` by element orders. +## - For each ThreeCycleCandidate `c` we check if `c^3 = 1` and we check for +## some random elements `r` if for the element `x := c * c^r` either `x^5 = 1` +## or `x^6 = 1` holds (The element `x` needs to have order `1`, `2`, `3` or `5`). +## - Use `RecogniseSnAnLazy` that caches iterators constructed by +## `ThreeCycleCandidatesIterator` and returns `TemporaryFailure` more +## quickly. +## - `ThreeCycleCandidatesIterator` uses a similar approach to that from the +## Magma Code `GetNextThreeCycle` and thus computes the elements in a +## different ordering to the paper. +## - We work in batches of at most `L = 10` involutions in a linear manner. +## We save all involutions considered so far. +## - For every involution, we consider only up to `K = 5` random +## conjugates. If none is successful, we move to the next involution. +## - If a batch of involutions reaches the last involution, i.e. the `B`-th +## one, we start with the first involution in the next round. +## - After a batch of involutions was completely processed, we return +## `SnAnTryLater` and exit the recognition method in the lazy variant. +## - However, as in the vanilla implementation, we return +## `TemporaryFailure` if for all `B` involutions either `C` conjugates have +## been tested in total or `T` conjugates were proven to commutate with the +## involution. +## - Use Conder's Thesis to compute images for degree `n <= 10`. + + +RECOG.SnAnDebug := false; + +BindGlobal("SnAnTryLater", MakeImmutable("SnAnTryLater")); + +RECOG.SnAnGetCache := function(ri) + if not IsBound(ri!.SnAnUnknownDegreeCache) then + ri!.SnAnUnknownDegreeCache := rec(); + fi; + return ri!.SnAnUnknownDegreeCache; +end; + +RECOG.SnAnResetCache := function(ri) + ri!.SnAnUnknownDegreeCache := rec(); +end; + # eps : real number, the error bound # N : integer, upper bound for the degree of G # @@ -37,12 +111,35 @@ RECOG.ThreeCycleCandidatesConstants := function(eps, N) B := Int(Ceil(13 * Log(Float(N)) * Log(3 / Float(eps)))), T := Int(Ceil(3 * Log(3 / Float(eps)))), C := Int(Ceil(Float(3 * N * ~.T / 5))), - logInt2N := LogInt(N, 2) + logInt2N := LogInt(N, 2), + K := 5, # this is an arbitrary number, max number for batch of conjugates per involution + L := 10, # this is an arbitrary number, max number for batch of involutions ); end; +# Return false if c can not be a three cycle. This uses cheap tests to +# determine this. It is based on the Magma function heuristicThreeCycleTest. +RECOG.HeuristicThreeCycleTest := function(ri, c, logInt2N, R) + local r, y, yTo5, k; + c := StripMemory(c); + if not isone(ri)(c ^ 3) then + return false; + fi; + for k in [1 .. logInt2N + 1] do + r := R[k]; + # c * c ^ r is a product of two three-cycles, so it should have order + # 1, 2, 3 or 5. + y := c * c ^ r; + yTo5 := y ^ 5; + if not isone(ri)(yTo5) and not isone(ri)(yTo5 * y) then + return false; + fi; + od; + return true; +end; + # ri : recognition node with group G -# constants : a record with components M, B, T, C, and logInt2N +# constants : a record with components M, B, T, C, K, L, and logInt2N # # The following algorithm constructs a set of possible 3-cycles. It is based # on the simple observation that the product of two involutions t1, t2, which @@ -51,6 +148,8 @@ end; # Creates and returns a function, here called oneThreeCycleCandidate. The # function oneThreeCycleCandidate returns one of the following: # - a three cycle candidate, i.e. an element of G +# - SnAnTryLater, if we tried a batch of at most L involutions, +# and for each of these we tried K conjugates # - TemporaryFailure, if we exhausted all attempts # - NeverApplicable, if we found out that G can't be an Sn or An # @@ -61,103 +160,180 @@ RECOG.ThreeCycleCandidatesIterator := function(ri, constants) t, # integers, controlling the number of iterations M, B, T, C, logInt2N, + # integers, making the algorithm give up quicker during iterations + K, L, + # list of random elements for heuristic three cycle test + R, + # list of involution candidates + involutions, + # counters + nrTriedConjugates, nrCommutatingConjugates, nrThreeCycleCandidates, # counters - nrInvolutions, nrTriedConjugates, nrThreeCycleCandidates, + Ki, Li, curInvolutionPos, # helper functions - tryThreeCycleCandidate, oneThreeCycleCandidate; + oneThreeCycleCandidate, + # used for debugging + cache; # Step 1: Initialization - # The current involution t_i - t := fail; - + ######################################################################### M := constants.M; B := constants.B; T := constants.T; C := constants.C; logInt2N := constants.logInt2N; + K := constants.K; + L := constants.L; + # list containing the constructed involutions t_i in steps 2 & 3 + involutions := EmptyPlist(B); + + R := List([1 .. logInt2N + 1], k -> StripMemory(RandomElm(ri, "SnAnUnknownDegree", true)!.el)); # Counters - # Counts the constructed involutions t_i in steps 2 & 3. - nrInvolutions := 0; + curInvolutionPos := 1; + Ki := Minimum(K, C); + Li := Minimum(L, B); # Counts the elements c in step 4 that we use to conjugate the current - # involution t_i. We initialize nrTriedConjugates to C such that "steps 2 - # & 3" in tryThreeCycleCandidate immediately construct an involution. - nrTriedConjugates := C; + # involution t_i. + nrTriedConjugates := []; # counts the size of the set Gamma_i in step 4 for the current involution # t_i - nrThreeCycleCandidates := 0; + nrCommutatingConjugates := []; + # counts the actual number of three cycle candidates considered + # which are filtered from the Gamma_i via heuristic order tests. + nrThreeCycleCandidates := []; + + # Used for Debugging + if RECOG.SnAnDebug then + cache := RECOG.SnAnGetCache(ri); + if not IsBound(cache.iteratorsLocalVars) then + cache.iteratorsLocalVars := []; + fi; + Add(cache.iteratorsLocalVars, rec( + involutions := involutions, + nrTriedConjugates := nrTriedConjugates, + nrCommutatingConjugates := nrCommutatingConjugates, + nrThreeCycleCandidates := nrThreeCycleCandidates + )); + fi; # Helper functions - # tryThreeCycleCandidate returns one of the following: + # oneThreeCycleCandidate returns one of the following: # - a three cycle candidate, i.e. an element of G - # - fail, if the random conjugate c from step 4 and t commute. Then we have - # to call tryThreeCycleCandidate again + # - TemporaryFailure, if we exhausted all attempts + # - SnAnTryLater, if we tried all involution candidates K times # - NeverApplicable, if G can not be an Sn or An - tryThreeCycleCandidate := function() + oneThreeCycleCandidate := function() local # integer, loop variable a, # elements, in G - r, tPower, tPowerOld, c; - # Steps 2 & 3: New involution - # Check if we either tried enough conjugates or constructed enough - # three cycle candidates for the current involution t. - # If this is the case, we need to construct the next involution - if nrTriedConjugates >= C or nrThreeCycleCandidates >= T then - r := RandomElm(ri, "SnAnUnknownDegree", true)!.el; - tPower := r ^ M; - # Invariant: tPower = (r ^ M) ^ (2 ^ a) - # We make a small improvement to the version described in - # . The order of r ^ M is a 2-power. - # It can be at most 2 ^ logInt2N. Thus, if we find an r such that - # (r ^ M) ^ (2 ^ logInt2N) is non-trivial, then we can return - # NeverApplicable. - for a in [1 .. logInt2N] do - tPowerOld := tPower; - tPower := tPower ^ 2; - if isone(ri)(tPower) then break; fi; - od; - if not isone(ri)(tPower) then - return NeverApplicable; + r, tPower, tPowerOld, c, + # the three cycle candidate + candidate; + + while true do + # Steps 2 & 3: New involution + ######################################################################### + # We consider at most B involutions. + if curInvolutionPos > B then + curInvolutionPos := 1; fi; - t := tPowerOld; - nrInvolutions := nrInvolutions + 1; - nrTriedConjugates := 0; - nrThreeCycleCandidates := 0; - fi; - # Steps 4 & 5: new three cycle candidate - # Try to construct a three cycle candidate via a conjugate of t. See - # the comment above this function. - nrTriedConjugates := nrTriedConjugates + 1; - c := t ^ RandomElm(ri, "SnAnUnknownDegree", true)!.el; - if not isequal(ri)(t * c, c * t) then - nrThreeCycleCandidates := nrThreeCycleCandidates + 1; - return (t * c) ^ 2; - else - # we have to call tryThreeCycleCandidate again - return fail; - fi; - end; - # construct the iterator - oneThreeCycleCandidate := function() - local candidate; - repeat - if nrInvolutions >= B - and (nrTriedConjugates >= C or nrThreeCycleCandidates >= T) + # We did not construct yet the involution to consider + if curInvolutionPos > Length(involutions) then + r := RandomElm(ri, "SnAnUnknownDegree", true)!.el; + # In the paper, we have t = r ^ M. + # Invariant: tPower = (r ^ M) ^ (2 ^ a) + tPower := r ^ M; + # We make a small improvement to the version described in + # . The order of r ^ M is a 2-power. + # It can be at most 2 ^ logInt2N. Thus, if we find an r such that + # (r ^ M) ^ (2 ^ logInt2N) is non-trivial, then we can return + # NeverApplicable. + for a in [1 .. logInt2N] do + tPowerOld := tPower; + tPower := tPower ^ 2; + if isone(ri)(tPower) then break; fi; + od; + if not isone(ri)(tPower) then + return NeverApplicable; + fi; + involutions[curInvolutionPos] := tPowerOld; + nrTriedConjugates[curInvolutionPos] := 0; + nrCommutatingConjugates[curInvolutionPos] := 0; + nrThreeCycleCandidates[curInvolutionPos] := 0; + fi; + # Check if we either tried enough conjugates or already found enough + # commutating conjugates for all involutions t. + # If this is the case, then we have exhausted all attempts. + if curInvolutionPos = B + and ForAll([1 .. B], i -> nrTriedConjugates[i] >= C or nrCommutatingConjugates[i] >= T) then - # With probability at least 1 - eps we constructed at least one - # three cycle with this iterator. - return fail; + return TemporaryFailure; fi; - candidate := tryThreeCycleCandidate(); - if candidate = NeverApplicable then - return NeverApplicable; + # If we either considered enough conjugates or already found enough + # commutating conjugates for the current involution t, + # we need to consider the next involution, or reached the end of our batch of involutions. + # Recall, that we work in batches for the lazy version. + # We work in batches of at most L involutions and for these, + # in batches of K conjugates. + if nrTriedConjugates[curInvolutionPos] >= Ki or nrCommutatingConjugates[curInvolutionPos] >= T then + if curInvolutionPos = B then + Li := L; + Li := Minimum(Li, B); + Ki := Ki + K; + Ki := Minimum(Ki, C); + curInvolutionPos := 1; + return SnAnTryLater; + elif curInvolutionPos = Li then + Li := Li + L; + Li := Minimum(Li, B); + curInvolutionPos := curInvolutionPos + 1; + return SnAnTryLater; + else + curInvolutionPos := curInvolutionPos + 1; + continue; # we have to start over + fi; + fi; + + # Steps 4 & 5: new three cycle candidate + ######################################################################### + # Try to construct a three cycle candidate via a conjugate of t. See + # the comment above this function. + t := involutions[curInvolutionPos]; + nrTriedConjugates[curInvolutionPos] := nrTriedConjugates[curInvolutionPos] + 1; + c := t ^ RandomElm(ri, "SnAnUnknownDegree", true)!.el; + if isequal(ri)(t * c, c * t) then + continue; # we have to start over + fi; + nrCommutatingConjugates[curInvolutionPos] := nrCommutatingConjugates[curInvolutionPos] + 1; + candidate := (t * c) ^ 2; + # We now use a one-sided heuristic to test whether candidate can be a + # three cycle, that is the heuristic can detect whether candidate can + # not be a three cycle, e.g. if it does not have order three. + if RECOG.HeuristicThreeCycleTest(ri, candidate, logInt2N, R) then + nrThreeCycleCandidates[curInvolutionPos] := nrThreeCycleCandidates[curInvolutionPos] + 1; + return candidate; fi; - until candidate <> fail; - return candidate; + + # if we get here, we'll loop around and start over + od; end; + return oneThreeCycleCandidate; end; +RECOG.SnAnCacheIterators := function(ri, T, N) + local cache, constants; + cache := RECOG.SnAnGetCache(ri); + if not IsBound(cache.iterators) then + constants := RECOG.ThreeCycleCandidatesConstants(1 / 4., N); + if RECOG.SnAnDebug then + cache.constants := constants; + fi; + cache.iterators := List([1 .. T], i -> RECOG.ThreeCycleCandidatesIterator(ri, constants)); + fi; +end; + # ri : recognition node with group G # c : element of G, # should be a 3-cycle @@ -604,7 +780,7 @@ end; # 3-cycle, a standard generator of An < G # - kTilde : integer, # degree of group An < G, that is generated by gTilde and cTilde -RECOG.StandardGenerators := function(ri, g, c, k, eps, N) +RECOG.SnAnStandardGenerators := function(ri, g, c, k, eps, N) local s, k0, c2, r, kTilde, gTilde, i, x, m, tmp, cTilde; s := One(g); k0 := k - 2; @@ -641,32 +817,165 @@ RECOG.StandardGenerators := function(ri, g, c, k, eps, N) fi; end; -# This function is an excerpt of the function RECOG.RecogniseSnAn in gap/SnAnBB.gi +# The following two functions RECOG.FindAnElementMappingIToJ and +# RECOG.FindImageSnAnSmallDegree are used for small degrees, 5 <= n < 11, to +# compute a monomorphism into Sn based on Jonathan Conder's thesis . +# +# In Conder's Thesis: Algorithm 7, ConjugateMap +# Returns an element c such that under the monomorphism Grp(ri) -> S_n given by +# s and t the image of c maps the point i to j. +RECOG.FindAnElementMappingIToJ := function(s, t, i, j) + if i < 3 then + if j < 3 then + return t^(j-i); + else + return t^(3-i)*s^(j-3); + fi; + else + return s^(j-i); + fi; +end; + +# In Conder's Thesis: Algorithm 10, ElementToSmallDegreePermutation, +# correctness see Theorem 3.5.2. +# Returns the image of g under the monomorphism Grp(ri) -> S_n given by s and +# t, for n >= 5. +# Under that monomorphism s is mapped to a long cycle, t to a three cycle, +# and the list e (E in Conder's Thesis) to [(1,2,3), (1,2,4), (1,2,5)]. +# Note that the arguments are in a different order than in the thesis, such +# that they are more consistent with the GAP function FindImageSn. +RECOG.FindImageSnAnSmallDegree := function(ri, n, g, s, t, e) + local T, L, H, i, j, k, l, c, h, h1, h2, h2h1, h1h2Comm, S, m, continueI, continueJ; + T := [ e[1], e[2], e[3], e[1] ^ 2 * e[2], e[1] ^ 2 * e[3], e[2] ^ 2 * e[3], e[1] * e[2] ^ 2, + e[1] * e[3] ^ 2, e[2] * e[3] ^ 2, e[2] * e[3] ^ 2 * e[1] * e[2] ^ 2 ]; + L := []; + # H = [ (1,2,3), (1,4,5) ] + H := [T[1], T[6]]; + for l in [1 .. n] do + for j in [1 .. n] do + continueJ := false; + if j = 1 then + c := One(Grp(ri)); + else + h := RECOG.FindAnElementMappingIToJ(s, t, j - 1, j); + c := c * h; + fi; + + for i in [1 .. Length(H)] do + continueI := false; + h1 := H[i] ^ g; + S := [1, 1]; + for k in [1 .. Length(S)] do + h2 := T[5 * k - 4] ^ c; + + h2h1 := h2 * h1; + if isone(ri)(h2h1) or isone(ri)(h2 * h2h1) then + continueI := true; # continue loop i + break; + fi; + h1h2Comm := Comm(h1, h2); + if isone(ri)(h1h2Comm) then + continueJ := true; # continue loop j + break; + elif isone(ri)(h1h2Comm ^ 2) then + S[k] := 2; + fi; + od; + # Jump to some outer loop. + if continueI then + continue; + elif continueJ then + break; + fi; + + if S[1] = S[2] then + if S[1] = 1 then + for k in [2 .. 5] do + if docommute(ri)(h1, T[k] ^ c) then + continueJ := true; # continue loop j + break; + fi; + od; + fi; + else + if S[1] > S[2] then + m := 6; + else + m := 8; + fi; + for k in [1 .. 2] do + if docommute(ri)(h1, T[m + k] ^ c) then + continueJ := true; # continue loop j + break; + fi; + od; + fi; + # Jump to some outer loop. + if continueJ then + break; + fi; + od; + + if continueJ then + continue; + else + Add(L, j); + break; + fi; + od; + + c := RECOG.FindAnElementMappingIToJ(s, t, l, l + 1); + for i in [1 .. Length(H)] do + H[i] := H[i] ^ c; + od; + od; + + return PermList(L); +end; + +# This function is based on the function RECOG.RecogniseSnAn in gap/SnAnBB.gi # ri : recognition node with group G, # n : degree # stdGensAnWithMemory : standard generators of An < G # -# Returns either fail or a record with components: -# [s, stdGens, xis, n], where: +# Returns either fail or a record with components type and isoData, where: # - type: the isomorphism type, that is either the string "Sn" or "An". -# - isoData: a list [stdGens, xis, n] where +# - isoData: a list [stdGens, filter, n] where # - stdGens are the standard generators of G. They do not have memory. -# - xis implicitly defines the isomorphism. It is used by RECOG.FindImageSn -# and RECOG.FindImageAn to compute the isomorphism. +# - filter implicitly defines the isomorphism. It is used by +# RECOG.FindImageSn, RECOG.FindImageAn, and RECOG.FindImageSnAnSmallDegree +# to compute the isomorphism. # - n is the degree of the group. -# - slpToStdGens: an SLP to stdGens. -# -# TODO: Image Computation requires n >= 11. +# - slpToStdGens: an SLP from the generators of G to stdGens. RECOG.ConstructSnAnIsomorphism := function(ri, n, stdGensAnWithMemory) - local stdGensAn, xis, gImage, foundOddPermutation, slp, eval, + local stdGensAn, filter, filterPart, gImage, foundOddPermutation, slp, eval, hWithMemory, bWithMemory, stdGensSnWithMemory, b, h, g; stdGensAn := StripMemory(stdGensAnWithMemory); - xis := RECOG.ConstructXiAn(n, stdGensAn[1], stdGensAn[2]); + # Construct filter. In filter is called E. + # In filter is called `xi`. + if n < 11 then + filterPart := [stdGensAn[2], (~[1] ^ stdGensAn[1]) ^ (2 * (n mod 2) - 1), + (~[2] ^ stdGensAn[1]) ^ (2 * (n mod 2) - 1)]; + filter := [[stdGensAn[1], stdGensAn[2]], filterPart]; + else + filter := RECOG.ConstructXiAn(n, stdGensAn[1], stdGensAn[2]); + fi; foundOddPermutation := false; + # For each generator, check whether its image und the monomorphism into the + # S_n has odd sign. If so, switch to recognizing S_n. + # For each generator also check whether the SLP for its image in the A_n + # was computed correctly. for g in ri!.gensHmem do - gImage := RECOG.FindImageAn(ri, n, StripMemory(g), - stdGensAn[1], stdGensAn[2], - xis[1], xis[2]); + if n < 11 then + gImage := RECOG.FindImageSnAnSmallDegree(ri, n, StripMemory(g), + filter[1][1], filter[1][2], + filter[2]); + else + gImage := RECOG.FindImageAn(ri, n, StripMemory(g), + stdGensAn[1], stdGensAn[2], + filter[1], filter[2]); + fi; if gImage = fail then return fail; fi; if SignPerm(gImage) = -1 then # we found an odd permutation, @@ -681,7 +990,7 @@ RECOG.ConstructSnAnIsomorphism := function(ri, n, stdGensAnWithMemory) od; if not foundOddPermutation then return rec(type := "An", - isoData := [[stdGensAn[1], stdGensAn[2]], xis, n], + isoData := [[stdGensAn[1], stdGensAn[2]], filter, n], slpToStdGens := SLPOfElms(stdGensAnWithMemory)); fi; # Construct standard generators for Sn: [bWithMemory, hWithMemory]. @@ -700,49 +1009,52 @@ RECOG.ConstructSnAnIsomorphism := function(ri, n, stdGensAnWithMemory) if not RECOG.SatisfiesSnPresentation(ri, n, b, h) then return fail; fi; - xis := RECOG.ConstructXiSn(n, b, h); + if n >= 11 then + filter := RECOG.ConstructXiSn(n, b, h); + fi; for g in ri!.gensHmem do - gImage := RECOG.FindImageSn(ri, n, StripMemory(g), - b, h, - xis[1], xis[2]); + if n < 11 then + gImage := RECOG.FindImageSnAnSmallDegree(ri, n, StripMemory(g), + filter[1][1], filter[1][2], + filter[2]); + else + gImage := RECOG.FindImageSn(ri, n, StripMemory(g), + b, h, + filter[1], filter[2]); + fi; if gImage = fail then return fail; fi; slp := RECOG.SLPforSn(n, gImage); eval := ResultOfStraightLineProgram(slp, [h, b]); if not isequal(ri)(eval, StripMemory(g)) then return fail; fi; od; return rec(type := "Sn", - isoData := [[b, h], xis, n], + isoData := [[b, h], filter, n], slpToStdGens := SLPOfElms(stdGensSnWithMemory)); end; -# This method is an implementation of . It is the main -# function of SnAnUnknownDegree. -# Note that it currently only works for 11 <= n. TODO: make it work with -# smaller n, that is include fixes from Jonathan Conder's B.Sc. -# Thesis "Algorithms for Permutation Groups", see PR #265. +# ri : recognition node with group G, +# T : integer, number of iterators +# N : integer, upper bound for the degree of G # -# From : -# RECOG.RecogniseSnAn is a one-sided Monte-Carlo algorithm with the following -# properties. It takes as input a black-box group G, a natural number -# N and a real number eps with 0 < eps < 1. If G is -# isomorphic to An or Sn for some 9 <= n <= N, it returns with -# probability at least 1 - eps the degree n and an -# isomorphism from G to An or Sn. -RECOG.RecogniseSnAn := function(ri, eps, N) - local T, foundPreImagesOfStdGens, constants, iterator, c, tmp, recogData, i; - T := Int(Ceil(Log2(1 / Float(eps)))); - foundPreImagesOfStdGens := false; - constants := RECOG.ThreeCycleCandidatesConstants(1. / 4., N); - for i in [1 .. T] do - iterator := RECOG.ThreeCycleCandidatesIterator(ri, constants); +# This is the main method used by RECOG.RecogniseSnAn and RECOG.RecogniseSnAnLazy. +# +# This function returns one of the following: +# - an isomorphism from G to Sn or An +# - SnAnTryLater, if we should try this method at a later point again +# - TemporaryFailure, if we exhausted all attempts +# - NeverApplicable, if we found out that G can't be an Sn or An +RECOG.RecogniseSnAnSingleIteration := function(ri, T, N) + local cache, iterators, iterator, c, tmp, recogData; + RECOG.SnAnCacheIterators(ri, T, N); + cache := RECOG.SnAnGetCache(ri); + iterators := cache.iterators; + # each iterator succeeds with probability at least 1/2, + # if we exhaust all attempts + for iterator in iterators do c := iterator(); - while c <> fail do - if c = NeverApplicable then return NeverApplicable; fi; - # This is a very cheap test to determine - # if our candidate c could be a three cycle. - if not isone(ri)(StripMemory(c) ^ 3) then - c := iterator(); - continue; + while c <> SnAnTryLater and c <> TemporaryFailure do + if c = NeverApplicable then + return c; fi; tmp := RECOG.ConstructLongCycle(ri, c, 1. / 8., N); if tmp = fail then @@ -752,7 +1064,7 @@ RECOG.RecogniseSnAn := function(ri, eps, N) # Now tmp contains [g, k] where # g corresponds to a long cycle # k is its length - tmp := RECOG.StandardGenerators(ri, tmp[1], c, tmp[2], 1. / 8., N); + tmp := RECOG.SnAnStandardGenerators(ri, tmp[1], c, tmp[2], 1. / 8., N); if tmp = fail then c := iterator(); continue; @@ -760,33 +1072,185 @@ RECOG.RecogniseSnAn := function(ri, eps, N) # Now tmp contains [g, c, n] where # g, c correspond to standard generators of An recogData := RECOG.ConstructSnAnIsomorphism(ri, tmp[3], tmp{[1,2]}); - if recogData = fail then continue; fi; - return recogData; + if recogData <> fail then + return recogData; + fi; od; od; - return TemporaryFailure; + return c; end; -#! @BeginChunk SnAnUnknownDegree -#! This method tries to determine whether the input group given by ri is -#! isomorphic to a symmetric group Sn or alternating group An with -#! 11 \leq n. -#! It is an implementation of . -#! -#! If Grp(ri) is a permutation group, we assume that it is primitive and -#! not a giant (a giant is Sn or An in natural action). -#! -#! @EndChunk -BindRecogMethod(FindHomMethodsGeneric, "SnAnUnknownDegree", -"method groups isomorphic to Sn or An with n >= 11", -function(ri, G) - local eps, N, p, d, recogData, isoData, degree, swapSLP; - #G := Grp(ri); - # TODO find value for eps - eps := 1 / 10^2; +# This method is an implementation of . +# From : +# RECOG.RecogniseSnAn is a one-sided Monte-Carlo algorithm with the following +# properties. It takes as input a black-box group G, a natural number +# N and a real number eps with 0 < eps < 1. If G is +# isomorphic to An or Sn for some 9 <= n <= N, it returns with +# probability at least 1 - eps the degree n and an +# isomorphism from G to An or Sn. +RECOG.RecogniseSnAn := function(ri, eps, N) + local T, tmp; + T := Int(Ceil(Log2(1 / Float(eps)))); + tmp := SnAnTryLater; + while tmp = SnAnTryLater do + tmp := RECOG.RecogniseSnAnSingleIteration(ri, T, N); + od; + return tmp; +end; + +RECOG.LowerBoundForDegreeOfSnAnViaOrders := function(ri) + local G, orders; + G := Grp(ri); + orders := Set(List( + [1..30], + i -> RandomElmOrd(ri, "LowerBoundForDegreeOfSnAnViaOrders", false).order + )); + return Maximum(List( + orders, + # For a given order, the sum over its factorisation into prime powers, + # gives a lower bound for the degree of the smallest symmetric group, + # that can contain an element of such an order. + o -> Sum(Collected(FactorsInt(o)), + tup -> tup[1] ^ tup[2]) + )); +end; + +# Inspired from Magma Code: GuessAltsymDegree, in magma/package/Group/GrpFin/SimpleRecog/altsym.m +# Returns a guess at alternating or symmetric and degree n +# (It won't work for Sym(3) or Sym(6)!) +# +# This function samples projective orders of elements, and attempts to guess +# degree n and whether it is Alternating or Symmetric. +# Returns a record with entries: +# - type : string "Alternating" or "Symmetric" +# - degree : integer n +# Returns fail if n<=6 or maxtries elements are sampled with +# no decision made. +# +# At least Max(mintries,fac*n*Log(n)) random elements are chosen without +# the answer changing, where mintries, fac can be given as an optional +# arguments. +# +# TODO: Investigate why Alt(9) and Sym(8) return fail +# TODO: Might be inspired from +# "Fast Constructive Recognition of a Black Box Group Isomorphic to Sn or An using Goldbach’s Conjecture" +# by Sergey Bratus and Igor Pak, +# in Chapter 9. "What To Do If n is Not Known?" +RECOG.GuessSnAnDegree := function(ri, optionlist...) + local G, r, options, mintries, maxtries, fac, mindego, mindege, ct, cto, cte, proc, g, o, mindeg, o_fact, mindegforg; + # mindego and mindege will be respectively the smallest possible + # degrees of symmetric groups that contain the elements of odd and + # even orders, in the random sample. + # If mindego > mindege we assume the group is alternating, otherwise + # that it is symmetric. + + G := Grp(ri); + if (IsPermGroup(G) and NrMovedPoints(G) <= 6) + or (IsMatrixGroup(G) and DimensionOfMatrixGroup(G) < 3) then + Print("GuessAltsymDegree works only for degree > 6\n"); + return fail; + fi; + + # Set options + options := rec( + mintries := 100, + maxtries := 5000, + fac := 4 + ); + + if Length(optionlist) > 0 then + for r in RecNames(optionlist[1]) do + if not IsBound(options.(r)) then + ErrorNoReturn("Invalid option to GuessSnAnDegree: ", r); + fi; + options.(r) := optionlist[1].(r); + od; + fi; + + mintries := options.mintries; + maxtries := options.maxtries; + fac := options.fac; + + # Init Loop + mindego := 0; + mindege := 0; + cto := 0; + cte := 0; + ct := 0; + mindeg := 0; + if mintries < 1 then + mintries := 1; + fi; + + # Main Loop + while (ct < Maximum(mintries, fac * mindeg * Int(Ceil(Log(Float(mindeg+1))))) + or mindego = mindege+1) and ct <= maxtries do + # The situation mindego = mindege+1 was responsible for most errors + # in the first version! Alt(n+1) was returned instead of Sym(n). + g := RandomElm(ri, "GuessSnAnDegree", false)!.el;; + o := OrderFunc(ri)(g); + ct := ct + 1; # counter of loop, as long as no new larger degree was detected + if o = 1 then + continue; + fi; + o_fact := Collected(Factors(o)); + mindegforg := Sum(o_fact, f -> f[1] ^ f[2]); # minimum degree is sum over all prime-powers in factorization + if o mod 2 = 0 then + cte := cte + 1; # counter for even orders + if mindegforg > mindege then + mindege := mindegforg; + if mindege > mindeg then + mindeg := mindege; + fi; + ct := 0; + # vprintf IsAltsym: "New E, E = %o, O = %o, elt order = %o, Randoms = %o\n", mindege, mindego, o_fact, cte+cto; + fi; + else + cto := cto + 1; # counter for odd orders + if mindegforg > mindego then + mindego := mindegforg; + if mindego > mindeg then + mindeg := mindego; + fi; + ct := 0; + # vprintf IsAltsym: "New O, E = %o, O = %o, elt order = %o, Randoms = %o\n", mindege, mindego, o_fact, cte+cto; + fi; + fi; + od; + + if ct > maxtries then + # vprintf IsAltsym: "maxtries exceeded - giving up!"; + return fail; + fi; + + # vprintf IsAltsym: "E = %o, O = %o, Randoms = %o\n", mindege, mindego, cte+cto; + + if mindego > mindege then + if mindego <= 6 then + # vprintf IsAltsym: "GuessAltsymDegree works only for degree > 6"; + return fail; + else + # vprintf IsAltsym: "Alternating of degree %o\n", mindego; + return rec(type := "Alternating", degree := mindego); + fi; + else + if mindege <= 6 then + # vprintf IsAltsym: "GuessAltsymDegree works only for degree > 6"; + return fail; + else + # vprintf IsAltsym: "Symmetric of degree %o\n", mindege; + return rec(type := "Symmetric", degree := mindege); + fi; + fi; +end; + +RECOG.SnAnUpperBoundForDegree := function(ri) + local G, N, p, d, M; + G := Grp(ri); + # N = upper bound for degree # Check magma if IsPermGroup(G) then - # We assume that G is primitive and not a giant. + # We assume that G is primitive and not a giant in natural representation. # The smallest non-natural primitive action of Sn or An induces # a large base group. Thus by [L84] its degree is smallest, when the # action is on 2-subsets. Thus its degree is at least n * (n-1) / 2. @@ -826,8 +1290,96 @@ function(ri, G) " , Grp() must be an IsPermGroup or an", " IsMatrixGroup"); fi; + # N = lower bound for degree + M := RECOG.LowerBoundForDegreeOfSnAnViaOrders(ri); + # Our upper bound is smaller than our lower bound. + if N < M then + return NeverApplicable; + fi; + # Lower bound does not exclude A5, S5, A6 or S6 + # If the input group is isomorphic to a symmetric or alternating group of + # degrees 5 or 6, then this method might not exit quickly. + if M <= 6 then + return TemporaryFailure; + fi; + + return N; +end; + +RECOG.SnAnCacheUpperBoundForDegree := function(ri) + local cache, N, degreeData; + cache := RECOG.SnAnGetCache(ri); + if IsBound(cache.N) then + return; + fi; + N := RECOG.SnAnUpperBoundForDegree(ri); + cache.N := N; + if not IsInt(N) then + return; + fi; + # This is usually much smaller than RECOG.SnAnUpperBoundForDegree. + # The number to compare N with was chosen arbitrarily as a "large" degree. + # if N > 20 then + # degreeData := RECOG.GuessSnAnDegree(ri); + # if degreeData = fail then + # cache.N := TemporaryFailure; + # return; + # fi; + # N := Minimum(N, degreeData.degree); + # cache.N := N; + # fi; +end; + +# See RECOG.RecogniseSnAn. The difference is, that we give up at an earlier +# point, i.e. we try out other recognition methods, before we continue. +# In order to achieve this, we cache some important values for further +# computations. It is the main function of SnAnUnknownDegree. +RECOG.RecogniseSnAnLazy := function(ri) + local a, cache, N, tmp; + RECOG.SnAnCacheUpperBoundForDegree(ri); + cache := RECOG.SnAnGetCache(ri); + N := cache.N; + if N = TemporaryFailure then + RECOG.SnAnResetCache(ri); + fi; + if not IsInt(N) then + return N; + fi; + tmp := RECOG.RecogniseSnAnSingleIteration(ri, 1, N); + if tmp = TemporaryFailure then + RECOG.SnAnResetCache(ri); + elif tmp = SnAnTryLater then + tmp := TemporaryFailure; + fi; + return tmp; +end; + +#! @BeginChunk SnAnUnknownDegree +#! This method tries to determine whether the input group given by ri is +#! isomorphic to a symmetric group Sn or alternating group An with +#! 9 \leq n. +#! It is an implementation of . +#! +#! If Grp(ri) is a permutation group, we assume that it is primitive and +#! not a giant (a giant is Sn or An in natural action). +#! +#! This method can also recognise a symmetric group Sn or alternating group An with +#! n = 7 or n = 8, but is not required to return a result with +#! the specified error probability. +#! +#! This method cannot recognise a symmetric group Sn or alternating group An with +#! n = 5 or n = 6, since it uses pre-bolstering elements, +#! which need at least 7 moved points. +#! If the input group is isomorphic to a symmetric or alternating group of +#! degrees 5 or 6, then this method might not exit quickly. +#! +#! @EndChunk +BindRecogMethod(FindHomMethodsGeneric, "SnAnUnknownDegree", +"method groups isomorphic to Sn or An with n >= 9", +function(ri, G) + local recogData, isoData, degree, swapSLP, t; # Try to find an isomorphism - recogData := RECOG.RecogniseSnAn(ri, eps, N); + recogData := RECOG.RecogniseSnAnLazy(ri); # RECOG.RecogniseSnAn returned NeverApplicable or TemporaryFailure if not IsRecord(recogData) then return recogData; @@ -851,13 +1403,41 @@ function(ri, G) CompositionOfStraightLinePrograms(swapSLP, recogData.slpToStdGens)); if recogData.type = "Sn" then - Setslpforelement(ri, SLPforElementFuncsGeneric.SnUnknownDegree); + if degree < 11 then + Setslpforelement(ri, SLPforElementFuncsGeneric.SnSmallDegree); + else + Setslpforelement(ri, SLPforElementFuncsGeneric.SnUnknownDegree); + fi; else - Setslpforelement(ri, SLPforElementFuncsGeneric.AnUnknownDegree); + if degree < 11 then + Setslpforelement(ri, SLPforElementFuncsGeneric.AnSmallDegree); + else + Setslpforelement(ri, SLPforElementFuncsGeneric.AnUnknownDegree); + fi; fi; return Success; end); +# The SLP function if G is isomorphic to Sn with small degree. +SLPforElementFuncsGeneric.SnSmallDegree := function(ri, g) + local isoData, degree, image; + isoData := ri!.SnAnUnknownDegreeIsoData; + degree := isoData[3]; + image := RECOG.FindImageSnAnSmallDegree(ri, degree, g, isoData[2][1][1], isoData[2][1][2], + isoData[2][2]); + return RECOG.SLPforSn(degree, image); +end; + +# The SLP function if G is isomorphic to An with small degree. +SLPforElementFuncsGeneric.AnSmallDegree := function(ri, g) + local isoData, degree, image; + isoData := ri!.SnAnUnknownDegreeIsoData; + degree := isoData[3]; + image := RECOG.FindImageSnAnSmallDegree(ri, degree, g, isoData[2][1][1], isoData[2][1][2], + isoData[2][2]); + return RECOG.SLPforAn(degree, image); +end; + # The SLP function if G is isomorphic to Sn. SLPforElementFuncsGeneric.SnUnknownDegree := function(ri, g) local isoData, degree, image; diff --git a/gap/matrix.gi b/gap/matrix.gi index 9d439291..3d5d9b1a 100644 --- a/gap/matrix.gi +++ b/gap/matrix.gi @@ -964,6 +964,8 @@ AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.DiagonalMatrices, 1100); AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.KnownStabilizerChain, 1175); +AddMethod(FindHomDbMatrix, FindHomMethodsGeneric.SnAnUnknownDegree, 1070);; + AddMethod(FindHomDbMatrix, FindHomMethodsGeneric.FewGensAbelian, 1050); AddMethod(FindHomDbMatrix, FindHomMethodsMatrix.ReducibleIso, 1000); diff --git a/gap/perm.gi b/gap/perm.gi index ec1dcbd5..47427e01 100644 --- a/gap/perm.gi +++ b/gap/perm.gi @@ -499,6 +499,8 @@ AddMethod(FindHomDbPerm, FindHomMethodsPerm.Imprimitive, 70); AddMethod(FindHomDbPerm, FindHomMethodsPerm.LargeBasePrimitive, 60); +AddMethod(FindHomDbPerm, FindHomMethodsGeneric.SnAnUnknownDegree, 58);; + AddMethod(FindHomDbPerm, FindHomMethodsPerm.StabilizerChainPerm, 55); AddMethod(FindHomDbPerm, FindHomMethodsPerm.StabChain, 50); diff --git a/gap/projective.gi b/gap/projective.gi index d46910c3..ff9601ef 100644 --- a/gap/projective.gi +++ b/gap/projective.gi @@ -311,6 +311,8 @@ AddMethod(FindHomDbProjective, FindHomMethodsMatrix.ReducibleIso, 1200); AddMethod(FindHomDbProjective, FindHomMethodsProjective.NotAbsolutelyIrred, 1100); +# AddMethod(FindHomDbProjective, FindHomMethodsGeneric.SnAnSmallUnknownDegree, 1075); + AddMethod(FindHomDbProjective, FindHomMethodsProjective.ClassicalNatural, 1050); AddMethod(FindHomDbProjective, FindHomMethodsProjective.Subfield, 1000); @@ -332,6 +334,8 @@ AddMethod(FindHomDbProjective, FindHomMethodsProjective.D247, 840); AddMethod(FindHomDbProjective, FindHomMethodsProjective.AltSymBBByDegree, 810); +AddMethod(FindHomDbProjective, FindHomMethodsGeneric.SnAnUnknownDegree, 805); + AddMethod(FindHomDbProjective, FindHomMethodsProjective.TensorDecomposable, 800); AddMethod(FindHomDbProjective, FindHomMethodsProjective.FindElmOfEvenNormal, 700); diff --git a/tst/working/quick/GenericSnAnUnknownDegree.tst b/tst/working/quick/GenericSnAnUnknownDegree.tst index 3848d74b..294aec2c 100644 --- a/tst/working/quick/GenericSnAnUnknownDegree.tst +++ b/tst/working/quick/GenericSnAnUnknownDegree.tst @@ -1,45 +1,64 @@ -#@local d, sets, SdOn2Sets, ri, success, x, slp, db # -# HACK to insert the method -gap> AddMethod(FindHomDbPerm, FindHomMethodsGeneric.SnAnUnknownDegree, 58);; -gap> AddMethod(FindHomDbMatrix, FindHomMethodsGeneric.SnAnUnknownDegree, 1070);; -gap> AddMethod(FindHomDbProjective, FindHomMethodsGeneric.SnAnUnknownDegree, 1220);; +# For each entry (d, k) we construct Sym(d)/Alt(d) acting on k-sets. +# For each entry (d, k), we must have 2 * k ^ 2 > d, +# otherwise LargeBasePrimitive recognises the group instead of SnAnUnknownDegree. +gap> dataPerm := [[5, 2], [7, 2], [8, 3], [9, 3], [10, 3], [11, 3], [12, 3], [13, 3]];; -# FindHomMethodsGeneric.SnAnUnknownDegree -# Sn -gap> for d in [11] do -> sets := Combinations([1 .. d], 2);; -> SdOn2Sets := Action(SymmetricGroup(d), sets, OnSets);; -> ri := RecogNode(SdOn2Sets);; -> success := FindHomMethodsGeneric.SnAnUnknownDegree(ri, SdOn2Sets); -> if not success or not Size(ri) = Factorial(d) then -> Print("wrong result! degree ", d, "\n"); -> fi; -> od; +# +# PermGroup action on k-sets +gap> PermOnKSets := function(G, k) +> local sets; +> sets := Combinations([1 .. NrMovedPoints(G)], k);; +> return Action(G, sets, OnSets);; +> end;; +gap> AltOnKSets := function(d, k) +> return PermOnKSets(AlternatingGroup(d), k); +> end;; +gap> SymOnKSets := function(d, k) +> return PermOnKSets(SymmetricGroup(d), k); +> end;; -# An -gap> for d in [11] do -> sets := Combinations([1 .. d], 2);; -> SdOn2Sets := Action(AlternatingGroup(d), sets, OnSets);; -> ri := RecogNode(SdOn2Sets);; -> success := FindHomMethodsGeneric.SnAnUnknownDegree(ri, SdOn2Sets); -> if not success or not Size(ri) = Factorial(d)/2 then -> Print("wrong result! degree ", d, "\n"); -> fi; -> od; +# +gap> altPermGroups := List(dataPerm, entry -> AltOnKSets(entry[1], entry[2]));; +gap> symPermGroups := List(dataPerm, entry -> SymOnKSets(entry[1], entry[2]));; + +# +gap> dataMat := [[5, 4], [7, 3], [8, 5], [11, 7]];; -# Check Slp function -gap> ri := RecogNode(SdOn2Sets);; -gap> FindHomMethodsGeneric.SnAnUnknownDegree(ri, SdOn2Sets); -true -gap> x := PseudoRandom(Grp(ri));; -gap> slp := SLPforElement(ri, x);; -gap> x = ResultOfStraightLineProgram(slp, NiceGens(ri)); -true +# +# Permutation Matrix Group +gap> PermMatGroup := function(G, q) return Group(List( +> GeneratorsOfGroup(G), +> x -> ImmutableMatrix(q, PermutationMat(x, NrMovedPoints(G), GF(q))) +> )); end;; +gap> AltMatGroup := function(n, q) return PermMatGroup(AlternatingGroup(n), q); +> end;; +gap> SymMatGroup := function(n, q) return PermMatGroup(SymmetricGroup(n), q); +> end;; + +# +gap> altMatGroups := List(dataMat, entry -> AltMatGroup(entry[1], entry[2]));; +gap> symMatGroups := List(dataMat, entry -> SymMatGroup(entry[1], entry[2]));; # -# Remove Hacky injection of our method -gap> for db in [FindHomDbPerm, FindHomDbMatrix, FindHomDbProjective] do -> Remove(db, -> PositionProperty(db, x -> Stamp(x.method) = "SnAnUnknownDegree"));; +gap> nonAltOrSymGroups := [ +> PSL(3, 5), +> SL(3, 5), +> Omega(1, 4, 3), +> ];; + +# Test +gap> for i in [1 .. Length(dataPerm)] do +> RECOG.TestGroup(altPermGroups[i], false, Factorial(dataPerm[i, 1])/2, rec()); +> RECOG.TestGroup(symPermGroups[i], false, Factorial(dataPerm[i, 1]), rec()); +> od; +gap> for i in [1 .. Length(dataMat)] do +> RECOG.TestGroup(altMatGroups[i], false, Factorial(dataMat[i, 1])/2, rec()); +> RECOG.TestGroup(symMatGroups[i], false, Factorial(dataMat[i, 1]), rec()); +> od; +gap> for i in [1 .. Length(nonAltOrSymGroups)] do +> ri := RecogNode(nonAltOrSymGroups[i]); +> if FindHomMethodsGeneric.SnAnUnknownDegree(ri, Grp(ri)) = Success then +> Print("ERROR: Recognised group [", i, "] wrongly as Sn/An!\n"); +> fi; > od; diff --git a/tst/working/quick/GenericSnAnUnknownDegreeHelpers.tst b/tst/working/quick/GenericSnAnUnknownDegreeHelpers.tst new file mode 100644 index 00000000..0d3360f8 --- /dev/null +++ b/tst/working/quick/GenericSnAnUnknownDegreeHelpers.tst @@ -0,0 +1,103 @@ +# RECOG.IsFixedPoint +gap> ri := RecogNode(SymmetricGroup(10));; +gap> g := (1,2,3,4,5,6,7,8);; +gap> c := (1,2,3);; +gap> r := (1,2)(4,5,6);; +gap> RECOG.IsFixedPoint(ri, g,c,r); +true +gap> r := (2,3,4);; +gap> RECOG.IsFixedPoint(ri, g,c,r); +false + +# RECOG.AdjustCycle +gap> ri := RecogNode(SymmetricGroup(10));; +gap> g := (1,2,3,4,5,6,7,8);; +gap> c := (1,2,3);; +gap> r := (4,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5) +gap> r := (3,4,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,4,5) +gap> r := (2,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5) +gap> r := (2,4,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5,4) +gap> r := (2,3,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,4,5) +gap> r := (1,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5) +gap> r := (1,4,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5,4) +gap> r := (1,3,4,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,6,4,5) +gap> r := (1,2,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5,4) +gap> r := (1,2,3,5);; +gap> RECOG.AdjustCycle(ri, g, c, r, 8); +(3,5,4,6) + +# RECOG.BuildCycle +gap> ri := RecogNode(SymmetricGroup(10));; + +# c = (u,v,w) +gap> c := (1,2,3);; + +# Form 1: x = (v,a_1,...,a_alpha) * (w,b_1,....,b_beta) * (...) +# alpha - beta = 0 +gap> x := (2,4,5,6)* (3,7,8,9);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,9), 9 ] + +# alpha - beta = -1 +gap> x := (2,4,5,6)* (3,7,8,9,10);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,9), 9 ] + +# alpha - beta = 1 +gap> x := (2,4,5,6,10)* (3,7,8,9);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,9), 9 ] + +# alpha - beta = -2 +gap> x := (2,4,5) * (3,7,8,9,10);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,10,9), 9 ] + +# alpha - beta = 2 +gap> x := (2,4,5,6,10) * (3,7,8);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,10), 9 ] + +# Form 2: x = (v,a_1,...,a_alpha,w,b_1,....,b_beta) * (...) +# alpha - beta = 0 +gap> x := (2,4,5,6,3,7,8,9);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,9), 9 ] + +# alpha - beta = -1 +gap> x := (2,4,5,6,3,7,8,9,10);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,9), 9 ] + +# alpha - beta = 1 +gap> x := (2,4,5,6,10,3,7,8,9);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,9), 9 ] + +# alpha - beta = -2 +gap> x := (2,4,5,3,7,8,9,10);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,10,9), 9 ] + +# alpha - beta = 2 +gap> x := (2,4,5,6,10,3,7,8);; +gap> RECOG.BuildCycle(ri, c, x, 10); +[ (1,2,3,4,7,5,8,6,10), 9 ] diff --git a/tst/working/slow/GenericSnAnUnknownDegree.tst b/tst/working/slow/GenericSnAnUnknownDegree.tst deleted file mode 100644 index 19a9c17f..00000000 --- a/tst/working/slow/GenericSnAnUnknownDegree.tst +++ /dev/null @@ -1,305 +0,0 @@ -#@local testFunction, IsBolsteringElement, data -#@local altGroups, symGroups, permMatGroup, altMatGroups, nonAltOrSymGroups -#@local ri, g, c, r, i, x, slp -#@local S11On2Sets, d, sets, SdOn2Sets, success, res, isoData, gens, g1, img1, g2, img2 -#@local SymOnKSets, AltOnKSets -#@local db -# -# HACK to insert the method -gap> AddMethod(FindHomDbPerm, FindHomMethodsGeneric.SnAnUnknownDegree, 58);; -gap> AddMethod(FindHomDbMatrix, FindHomMethodsGeneric.SnAnUnknownDegree, 1070);; -gap> AddMethod(FindHomDbProjective, FindHomMethodsGeneric.SnAnUnknownDegree, 1220);; - -# -# testing matrix: -# - isomorphic: yes, no -# different representations: -# - permutation groups: natural, on 2-subsets -# - finitely presented group -# - permutation matrices: compressed matrices, uncompressed matrices -# - projective matrix groups -# -# TODO: Use RECOG.TestGroup? -# TODO: better name for testFunction -# tests RECOG.ThreeCycleCandidatesIterator and RECOG.BolsteringElements -gap> testFunction := function(G, eps, N) -> local ri, iterator, candidate, i, j; -> ri := RecogNode(G); -> iterator := RECOG.ThreeCycleCandidatesIterator(ri, RECOG.ThreeCycleCandidatesConstants(eps, N)); -> for i in [1 .. 4] do -> candidate := iterator(); -> for j in [1 .. 4] do -> if candidate <> NeverApplicable -> and candidate <> TemporaryFailure then -> RECOG.BolsteringElements(ri, candidate, eps, -> N); -> fi; -> od; -> od; -> end;; - -# -# x : permutation -# c : permutation, -# should be a 3-cycle -# -# Returns true, if x is a bolstering element with respect to the 3-cycle c. -# -# Let c = (u, v, w). We call x a bolstering element with respect to c, if -# x = (v, a_1, ..., a_alpha)(w, b_1, ..., b_beta)(...) or -# x = (v, a_1, ..., a_alpha, w, b_1, ..., b_beta)(...) -# with u in fix(x) and alpha, beta >= 2. -gap> IsBolsteringElement := -> function(x, c) -> local suppC, dist, i, k, p, pos1; -> # suppC = [u, v, w] -> suppC := MovedPoints(c); -> if Size(suppC) <> 3 then return false; fi; -> # dist = [k_u, k_v, k_w], -> # where for each p in suppC, k_p is the minimal integer such that p^(x^k) in suppC -> dist := [0, 0, 0]; -> for i in [1..3] do -> k := 1; -> p := suppC[i]^x; -> while not p in suppC do -> k := k + 1; -> p := p ^ x; -> od; -> dist[i] := k; -> od; -> # One point of suppC is fixed by x -> pos1 := PositionProperty(dist, k -> k = 1); -> if pos1 = fail then return false; fi; -> Remove(dist, pos1); -> # The other two points of cuppC have distance at least 2 -> if ForAny(dist, k -> k < 2) then return false; fi; -> return true; -> end;; - -# For each entry (d, k) we construct Sym(d)/Alt(d) acting on k-sets. -# For each entry (d, k), we must have 2 * k ^ 2 > d, -# otherwise LargeBasePrimitive recognises the group instead of SnAnUnknownDegree. -gap> data := [[11, 3], [12, 3], [13, 3]];; - -# TODO: more non-isomorphic examples -# TODO: add projective groups -# -# SymmetricGroup action on k-sets -gap> SymOnKSets := function(d, k) -> local sets; -> sets := Combinations([1 .. d], k);; -> return Action(SymmetricGroup(d), sets, OnSets);; -> end;; - -# -# AlternatingGroup action on 2-sets -gap> AltOnKSets := function(d, k) -> local sets; -> sets := Combinations([1 .. d], k);; -> return Action(AlternatingGroup(d), sets, OnSets);; -> end;; - -# -gap> altGroups := List(data, entry -> AltOnKSets(entry[1], entry[2]));; -gap> symGroups := List(data, entry -> SymOnKSets(entry[1], entry[2]));; -gap> permMatGroup := G -> Group(List( -> GeneratorsOfGroup(G), -> x -> ImmutableMatrix(7, PermutationMat(x, NrMovedPoints(G), GF(7))) -> ));; -gap> altMatGroups := List([11], -> n -> permMatGroup(AlternatingGroup(n)));; -gap> nonAltOrSymGroups := [ -> DihedralGroup(IsPermGroup, 10), -> #DihedralGroup(IsPcGroup, 10), -> #DihedralGroup(IsPermGroup, 2000), -> #DihedralGroup(IsPcGroup, 2000), -> PSL(3, 5), -> SL(3, 5), -> Omega(-1, 4, 5), -> Omega(-1, 4, 3), -> Omega(+1, 4, 5), -> Omega(+1, 4, 3), -> Omega(+1, 8, 5), -> Omega(+1, 8, 3), -> Omega(0, 5, 5), -> Omega(0, 5, 3), -> ];; - -# Test -gap> for i in [1 .. Length(data)] do -> RECOG.TestGroup(altGroups[i], false, Factorial(data[i, 1])/2); -> RECOG.TestGroup(symGroups[i], false, Factorial(data[i, 1])); -> od; -gap> for i in [1 .. Length(altMatGroups)] do -> RECOG.TestGroup(altMatGroups[i], false, Size(altMatGroups[i])); -> od; -gap> for i in [1 .. Length(altMatGroups)] do -> RECOG.TestGroup(altMatGroups[i], true, Size(altMatGroups[i])); -> od; -gap> for i in [1 .. Length(nonAltOrSymGroups)] do -> if FindHomMethodsGeneric.SnAnUnknownDegree(RecogNode(nonAltOrSymGroups[i]), nonAltOrSymGroups[i]) = Success then -> Print("ERROR: Recognised group [", i, "] wrongly as Sn/An!\n"); -> fi; -> od; - -# RECOG.IsFixedPoint -gap> ri := RecogNode(SymmetricGroup(10));; -gap> g := (1,2,3,4,5,6,7,8);; -gap> c := (1,2,3);; -gap> r := (1,2)(4,5,6);; -gap> RECOG.IsFixedPoint(ri, g,c,r); -true -gap> r := (2,3,4);; -gap> RECOG.IsFixedPoint(ri, g,c,r); -false - -# RECOG.AdjustCycle -gap> ri := RecogNode(SymmetricGroup(10));; -gap> g := (1,2,3,4,5,6,7,8);; -gap> c := (1,2,3);; -gap> r := (4,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5) -gap> r := (3,4,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,4,5) -gap> r := (2,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5) -gap> r := (2,4,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5,4) -gap> r := (2,3,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,4,5) -gap> r := (1,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5) -gap> r := (1,4,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5,4) -gap> r := (1,3,4,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,6,4,5) -gap> r := (1,2,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5,4) -gap> r := (1,2,3,5);; -gap> RECOG.AdjustCycle(ri, g, c, r, 8); -(3,5,4,6) - -# RECOG.BuildCycle -gap> ri := RecogNode(SymmetricGroup(10));; - -# c = (u,v,w) -gap> c := (1,2,3);; - -# Form 1: x = (v,a_1,...,a_alpha) * (w,b_1,....,b_beta) * (...) -# alpha - beta = 0 -gap> x := (2,4,5,6)* (3,7,8,9);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,9), 9 ] - -# alpha - beta = -1 -gap> x := (2,4,5,6)* (3,7,8,9,10);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,9), 9 ] - -# alpha - beta = 1 -gap> x := (2,4,5,6,10)* (3,7,8,9);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,9), 9 ] - -# alpha - beta = -2 -gap> x := (2,4,5) * (3,7,8,9,10);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,10,9), 9 ] - -# alpha - beta = 2 -gap> x := (2,4,5,6,10) * (3,7,8);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,10), 9 ] - -# Form 2: x = (v,a_1,...,a_alpha,w,b_1,....,b_beta) * (...) -# alpha - beta = 0 -gap> x := (2,4,5,6,3,7,8,9);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,9), 9 ] - -# alpha - beta = -1 -gap> x := (2,4,5,6,3,7,8,9,10);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,9), 9 ] - -# alpha - beta = 1 -gap> x := (2,4,5,6,10,3,7,8,9);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,9), 9 ] - -# alpha - beta = -2 -gap> x := (2,4,5,3,7,8,9,10);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,10,9), 9 ] - -# alpha - beta = 2 -gap> x := (2,4,5,6,10,3,7,8);; -gap> RECOG.BuildCycle(ri, c, x, 10); -[ (1,2,3,4,7,5,8,6,10), 9 ] - -# Construct isomorphism between S_11 on 2-sets and S_11 in natural -# representation. -gap> sets := Combinations([1 .. 11], 2);; -gap> S11On2Sets := Action(SymmetricGroup(11), sets, OnSets);; -gap> ri := RecogNode(S11On2Sets);; -gap> FindHomMethodsGeneric.SnAnUnknownDegree(ri, S11On2Sets);; -gap> isoData := ri!.SnAnUnknownDegreeIsoData;; -gap> gens := GeneratorsOfGroup(S11On2Sets);; -gap> g1 := gens[1];; -gap> img1 := RECOG.FindImageSn(ri, 11, g1, isoData[1][1], isoData[1][2], -> isoData[2][1], isoData[2][2]);; -gap> CycleStructurePerm(img1); -[ ,,,,,,,,, 1 ] -gap> g2 := gens[2];; -gap> img2 := RECOG.FindImageSn(ri, 11, g2, isoData[1][1], isoData[1][2], -> isoData[2][1], isoData[2][2]);; -gap> CycleStructurePerm(img2); -[ 1 ] - -# FindHomMethodsGeneric.SnAnUnknownDegree -# Sn -gap> for d in [11 .. 14] do -> sets := Combinations([1 .. d], 2);; -> SdOn2Sets := Action(SymmetricGroup(d), sets, OnSets);; -> ri := RecogNode(SdOn2Sets);; -> success := FindHomMethodsGeneric.SnAnUnknownDegree(ri, SdOn2Sets); -> if not success or not Size(ri) = Factorial(d) then -> Print("wrong result! degree ", d, "\n"); -> fi; -> od; - -# An -gap> for d in [11 .. 14] do -> sets := Combinations([1 .. d], 2);; -> SdOn2Sets := Action(AlternatingGroup(d), sets, OnSets);; -> ri := RecogNode(SdOn2Sets);; -> success := FindHomMethodsGeneric.SnAnUnknownDegree(ri, SdOn2Sets); -> if not success or not Size(ri) = Factorial(d)/2 then -> Print("wrong result! degree ", d, "\n"); -> fi; -> od; - -# Check Slp function -gap> ri := RecogNode(S11On2Sets);; -gap> FindHomMethodsGeneric.SnAnUnknownDegree(ri, S11On2Sets); -true -gap> x := PseudoRandom(Grp(ri));; -gap> slp := SLPforElement(ri, x);; -gap> x = ResultOfStraightLineProgram(slp, NiceGens(ri)); -true - -# -# Remove Hacky injection of our method -gap> for db in [FindHomDbPerm, FindHomDbMatrix, FindHomDbProjective] do -> Remove(db, -> PositionProperty(db, x -> Stamp(x.method) = "SnAnUnknownDegree"));; -> od;