-
Notifications
You must be signed in to change notification settings - Fork 161
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
FIX: (Maximal) subgroups computation. #2488
Conversation
lib/grp.gd
Outdated
@@ -1202,7 +1202,11 @@ DeclareAttribute( "MaximalSubgroups", IsGroup ); | |||
## | |||
DeclareAttribute("MaximalSubgroupClassReps",IsGroup); | |||
|
|||
# utility attribute: Allow use with limiting options, so could hold `fail'. | |||
DeclareAttribute("TryMaximalSubgroupClassReps",IsGroup); |
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.
What I don't understand about this is why TryMaximalSubgroupClassReps
even is an attribute, and not just an operation, as its value may depend on options, and there is no way for which options the set attribute value was computed, is there?
(Perhaps also a mutable attribute would be a choice, so I don't quite see why/how)?
Mind you, I am not saying this is wrong, just that I don't understand the rationale, and would appreciate some illuminating words. Thanks!
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.
@fingolfin
I'm still trying to get the tests run through, so it might be worth to wait with reviewing until the checks show green.
The reason for making Try... an attribute is that a calculation might (in fact that happens when calculating double cosets) several times ask for cheap maximal subgroups of the same group.
Codecov Report
@@ Coverage Diff @@
## master #2488 +/- ##
==========================================
+ Coverage 74.71% 75.34% +0.63%
==========================================
Files 480 443 -37
Lines 242188 228948 -13240
==========================================
- Hits 180945 172504 -8441
+ Misses 61243 56444 -4799
|
@fingolfin [Moved comment out of `outdated' bit. |
lib/grp.gi
Outdated
|
||
end); | ||
|
||
InstallGlobalFunction(TryMaxSubgroupTainter,function(G) |
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.
Hmm... This whole maxsubtrytaint
approach seems brittle to me. Forget a call to TryMaxSubgroupTainter
in one place, and boom, things go wrong, possibly in a hard to track way.
Instead of using an approach were a failure to comply with the (undocumented!) leads to mathematically wrong results, I'd prefer if we could use one were failure to comply just leads to a slow down.
E.g. instead of requiring every TryMaximalSubgroupClassReps
method to invoke TryMaxSubgroupTainter
, let's instead require them to invoke Set(MaxSubgroupTainter
before returning, if applicable.
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.
Things get even simpler if you also add an operation TryMaximalSubgroupClassRepsOp
, and move most current MaximalSubgroupClassReps
resp. TryMaximalSubgroupClassReps
methods to that operation. Then, both MaximalSubgroupClassReps
resp. TryMaximalSubgroupClassReps
can dispatch to that operation. This would also remove the need to create a temporary "copy" of the group, and later transfer subgroups back to the original group.
And the main (only?) TryMaximalSubgroupClassReps
method can look roughly like this:
function(G)
local maxs;
if HasMaximalSubgroupClassReps(G) then return MaximalSubgroupClassReps(G); fi;
maxs := TryMaximalSubgroupClassRepsOp(G);
if NO-BAD-OPTIONS-SET then
SetMaximalSubgroupClassReps(G, maxs);
fi;
return maxs`
end;
That's still slightly brittle, because some TryMaximalSubgroupClassRepsOp
might accept more options in the future, and if we forget to update the above method, we have a problem... But at least we won't be off worse than we are now.
(BTW, I'd actually turn those options into explicit arguments to TryMaximalSubgroupClassRepsOp
, but that's probably a matter of taste)
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.
PS: of course one could also try to come up with a better name... perhaps the second attribute should be SomeMaximalSubgroupClassReps
, and the operation then could be TryMaximalSubgroupClassReps
, or ComputeSomeMaximalSubgroupClassReps
, or ... but that's of course in the end irrelevant, the functionality is what counts.
# compute anew for new group to avoid taint | ||
H:=Group(GeneratorsOfGroup(G)); | ||
for i in [Size,IsNaturalAlternatingGroup,IsNaturalSymmetricGroup] do | ||
if Tester(i)(G) then Setter(i)(H,i(G));fi; |
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.
So how do we ensure that these three attributes are the only relevant ones for computations of maximal subgroups? What about e.g. IsSolvableGroup
? Or information about stabilizer chains, etc. etc.?
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.
They are currently the only two for which special methods for maximal subgroups are installed.
return c^k; | ||
# use maximals, use `Try` as we call with limiting options | ||
IsNaturalAlternatingGroup(G); | ||
IsNaturalSymmetricGroup(G); |
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 above two lines are new. I assume they are meant to ensure better methods are applied, if possible? I.e. an optimization, but not a vital part of the fix? (Just trying to understand).
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.
Yes. But this optimization is checked for in manual and test examples.
IsNaturalAlternatingGroup(G); | ||
IsNaturalSymmetricGroup(G); | ||
m:=TryMaximalSubgroupClassReps(G:cheap,intersize:=intersize,nolattice); | ||
if m<>fail and Length(m)>0 then |
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.
Was the nolattice
option added here on purpose? Just wondering, as I don't see this part of the change mentioned in the commit message.
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.
Yes.
# observation by S.Alavi with IntermediateGroup | ||
|
||
gap> g:= AtlasGroup( "U4(4)" );; Size( g ); | ||
1018368000 |
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 atlasrep package may not be available (or at least not loaded) when running the tests. Could this perhaps be changed to g:=SU(IsPermGroup,4,4);
?
gap> g:= AtlasGroup( "U4(4)" );; Size( g ); | ||
1018368000 | ||
gap> h:= Image( IsomorphismPermGroup( PSL(2,16) ) );; | ||
gap> l:= IsomorphicSubgroups( g, h );; |
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.
This computation can take minutes when I try it here. Since only one of those groups is needed, can't we just explicitly describe it? Or, just switch to a smaller example, like this (I verified that it fails in master
, too):
gap> g:=SU(IsPermGroup,3,4);
Perm_SU(3,4)
gap> h:=PSL(IsPermGroup, 2, 4);;
gap> l:= IsomorphicSubgroups( g, h );; Length(l);
1
gap> s1:= Image( l[1] );; Size( s1 );
60
gap> n1:= Normalizer( g, s1 );; Size( n1 );
300
gap> int:=IntermediateGroup(g,s1);;
gap> IsGroup(int);
true
That way, the bugfix test suite does not get slowed down, nor does it require the atlasrep package.
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.
Yes, the IsomorphicSubgroups
can go and has done so. The issue of the bug was that it was a (comparatively) small index subgroup in a large group, so large indeed that it is unlikely to be added to the table of marks library anytime soon.
My personal inclination is at this time to have the original bug, but if someone wants to change it afterwards for speed reasons I have no objection.
@@ -1342,6 +1342,50 @@ end); | |||
## | |||
#M MaximalSubgroups( <G> ) | |||
## | |||
InstallMethod(MaximalSubgroupClassReps,"default, catch dangerous options", |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/grp.gi
Outdated
|
||
# now we know list is untained, store | ||
Unbind(G!.maxsubtrytaint); | ||
G!.TryMaximalSubgroupClassReps:=l; |
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.
So you bypass the setter function. Any particular reason for that? It also means that Has TryMaximalSubgroupClassReps
won't be set.
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.
In this situation the attribute is already set with a list that is not guaranteed to be complete. We also have just computed a complete list and we store it in place. Can I use a setter to change the value?
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.
For mutable attributes: yes!
gap> G:=SymmetricGroup(5);
Sym( [ 1 .. 5 ] )
gap> SetIsFoo(G, "foo");
gap> IsFoo(G);
"foo"
gap> SetIsFoo(G, "nar");
gap> IsFoo(G);
"nar"
As far as I'm concerned this fix is complete. If someone wants to make additions to it after merging (or propose a different fix) feel free to do so. I am happy to help with integrating it into 4.9, but not so much in discussing possible other code structures or function names. I have included the suggestions that to me made sense as part of a fix. I have not done code restructuring or manual additions. Let me explain why: This is a fix that should be back ported to 4.9. For this it is unfortunate that it requires a new attribute, but still, the changes ought to be as small as possible. This is the reason for not doing structural changes. As for undocumented options, these serve two purposes:
In the end, I hope we will have (as a port of the Magma functionality that I am working on) this lacking functionality and at that point both the I am also happy to look, for future releases, at changes for the attribute to clean up the |
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.
While I still am not really happy with TryMaxSubgroupTainter
(because I think it's very easy to produce incorrect code with this approach; while the alternative I suggested should not require more changes, in fact, I think it needs fewer), I also appreciate that reworking this requires time, effort and nerves, and this fix is definitely much better than no fix! We can indeed (if we want to) improve on it later.
I am slightly unsure about backporting this to GAP 4.9, given the magnitude of the change. But if others are fine with it... @alex-konovalov could this still make GAP 4.9.2 (I am not quite sure about the release schedule for that).
BTW, note that our plan is that GAP 4.10 will branch around September 1st, and 4.10.0 be released around October 1st (whether this will be already public, or again "just" a beta as before, with the first public release 4.10.1. on 1st of November, still needs to be discussed.
For record, the cryst
package also installs two methods for MaximalSubgroupClassReps
which are checking various options (though I have not looked further into it, i.e. I don't know if these options affect the result). So I guess once this PR is merged, we should contact @gaehler to discuss with him whether cryst needs an update, too?
# slow otherwise. Also construct the smaller subgroup s1 directly. | ||
# Finally do not slow down with assertions that don't need testing here | ||
|
||
gap> SetAssertionLevel(0);; |
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.
If you turn off assertions here, this will also turn them off in subsequent tests -- until some test uses START_TEST/STOP_TEST
, which not all .tst files do right now.
So please either manually save/restore the assertion level, or else (probably simpler) add START_TEST
and STOP_TEST
calls.
Alternatively, somebody else can add this after this PR gets merged.
@@ -0,0 +1,7 @@ | |||
# #2586 |
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.
If we ever switch away from GitHub, this reference will be completely inscrutable. Could you perhaps change it to e.g. this?
# Verify that ConjugacyClassesSubgroups returns classes with representatives that actually are
# contained in the group (bug #2586 on GitHub)
Or if you want to stay brief (as the test actually is quite self-explanatory), perhaps at least
# verify fix for bug #2586 on GitHub
len:=LengthsTom(tom); | ||
sub:=List([1..Length(len)],x->RepresentativeTom(tom,x)); | ||
Identifier(tom[2])," from table of marks"); | ||
len:=LengthsTom(tom[2]); |
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.
This looks plausible to me, thanks.
Quick remark on the commit message: It contains the string "partially fixes #2586" -- GitHub sees that, but only the "fixes #2586" bit, it doesn't care about the "partially". As a result, if this commit gets merged, GitHub will close issue #2586.
One workaround is to rephrase this to e.g. fixes issue #2586
(I think). Well, or we wait for you to also fix the second bug, then closing the issue is fine, after all ;-)
gap> cl:=ConjugacyClassesSubgroups(g);; | ||
gap> ForAll(cl,x->IsSubset(g,Representative(x))); | ||
true | ||
|
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.
This test file actually fails the test due to this extra new line at the end. Removing it should be enough to fix the test.
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.
Subject to the tests passing this looks good to me.
About backporting - ideally, I'd like to see only the fix for #2586 backported to stable-4.9 - that is easy to do if it's in separate commit(s). But is it independent of the other changes in this branch? The milestone for 4.9.2 has the date 8th July, and that seems realistic - I expect that the changes overview will be merged and most of the rest will be reassigned to GAP 4.9.3 (like it was reassigned to 4.9.2 earlier). We will have PGTC 2018 (http://www-groups.mcs.st-and.ac.uk/~pgtc2018/) in St Andrews in mid-July and would like to have 4.9.2 prior to that. |
The routine for `MaximalSubgroupClassReps allows options to calculate maximal subgroups only up to specified limits, or if it is not too difficult. This causes problems is such a partial list is stored as attribute. Thus separate into two attributes: One to calculate the guaranteed full list, one to calculate a potentially partial list subject to restrictions. Also make sure that limiting options do not get accidentally inherited. (In the big scheme of things we might want to revisit the question of ``cheap attributes'' more generally, as composition tree uses similar paradigms.) Finally allow a soft fallback to old intermediate subgroup routines, if maximal subgroups are not available. (This can go away once proper maximal subgroups code is available.) Also added test file In the best of all worlds this should be backported to 4.9 Reworked the example. Avoiding the AtlasGroup command makes it much harder as SU gives a group on >>325 points. Also construct the subgroup directly to avoid homomorphism search.
The two fixes now are in two separate, non-interleaved commits. |
Is the intention for this to be merged once the tests pass, then? |
@alex-konovalov Just in case this is relevant information, this first fix (for MaximalSubgroups) resolves a bug reported by S. Alavi from Iran on 5/24 (subject Intermediate subgroup-Gap 4.9.1). |
This bug likely did not show up earlier since the TOM library was not loaded by default. Even an example is provided, the latter thrice as error does not arise every time. This fixes gap-system#2586
Re: the bug report by S. Alavi not having been replied to: that's of course unfortunate. But it's the usual problem when people start replying to the report, but not to the reporter, and then nobody remembers to reply with at least an "we are looking into it". |
As to backporting: I think only the first commit can and should be backported -- the second fixes a regression that does not affect stable-4.9, after all (and modifies code that's not even on that branch, so it wouldn't even apply). |
I'll reply to S. Alavi |
@fingolfin |
I still do not understand what the impact of this change on the cryst package is. According to the manual, attribute values computed from a call with more than one argument are never stored. Is this still true? If so, no changes to cryst are necessary. Otherwise, I do have a problem... For affine crystallographic groups, MaximalSubgroupClassReps must always be called with two arguments, because the total number of classes is generally not finite. This is checked, and an error is returned otherwise. I hope this will just continue to work, without using TryMaximalSubgroupClassReps. |
I have to correct myself. A method for one argument is actually missing, and the default one won't work unless the group is finite. Maybe I should just add a method that catches the finite case, and returns an error with explanation otherwise. |
The routine for `MaximalSubgroupClassReps allows options to calculate
maximal subgroups only up to specified limits, or if it is not too
difficult. This causes problems is such a partial list is stored as
attribute. Thus separate into two attributes: One to calculate the
guaranteed full list, one to calculate a potentially partial list subject to
restrictions. Also make sure that limiting options do not get accidentally
inherited.
Finally allow a soft fallback to old intermediate subgroup routines, if
maximal subgroups are not available. (This can go away once proper maximal
subgroups code is available.)
This fixes a bug reported (Subject:
Intermediate subgroup-Gap 4.9.1
) on the support email list.Included is also a fix for the main bug in #2586
Also added test files.
In the best of all worlds this should be backported to 4.9