-
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
Add IsAbelian filters to IsSolvableGroup and DerivedSeries #706
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 |
---|---|---|
|
@@ -417,6 +417,10 @@ InstallImmediateMethod( IsPerfectGroup, | |
0, | ||
grp -> not IsAbelian( grp ) ); | ||
|
||
InstallMethod( IsPerfectGroup, "for groups having abelian invariants", | ||
[ IsGroup and HasAbelianInvariants ], | ||
grp -> Length( AbelianInvariants( grp ) ) = 0 ); | ||
|
||
InstallMethod( IsPerfectGroup, | ||
"method for finite groups", | ||
[ IsGroup and IsFinite ], | ||
|
@@ -533,13 +537,22 @@ InstallMethod( IsSolvableGroup, | |
"generic method for groups", | ||
[ IsGroup ], | ||
function ( G ) | ||
local S; # derived series of <G> | ||
local S, # derived series of <G> | ||
isAbelian, # true if <G> is abelian | ||
isSolvable; # true if <G> is solvable | ||
|
||
# compute the derived series of <G> | ||
S := DerivedSeriesOfGroup( G ); | ||
|
||
# the group is solvable if the derived series reaches the trivial group | ||
return IsTrivial( S[ Length( S ) ] ); | ||
isSolvable := IsTrivial( S[ Length( S ) ] ); | ||
|
||
# set IsAbelian filter | ||
isAbelian := isSolvable and Length( S ) <= 2; | ||
Assert(3, IsAbelian(G) = isAbelian); | ||
SetIsAbelian(G, isAbelian); | ||
|
||
return isSolvable; | ||
end ); | ||
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. This seems correct and fine, but I found it a bit hard to read and verify. How about something like this instead?
I find that a lot easier to understand and verify, and it reduces code duplication. |
||
|
||
|
||
|
@@ -900,16 +913,35 @@ InstallMethod( DerivedSeriesOfGroup, | |
D := DerivedSubgroup( G ); | ||
|
||
while | ||
# we don't know that the group has no generators | ||
(not HasGeneratorsOfGroup(S[Length(S)]) or | ||
Length(GeneratorsOfGroup(S[Length(S)]))>0) and | ||
( (not HasAbelianInvariants(S[Length(S)]) and D <> S[ Length(S) ]) or | ||
Length(AbelianInvariants(S[Length(S)]))>0) do | ||
(not HasIsTrivial(S[Length(S)]) or | ||
not IsTrivial(S[Length(S)])) and | ||
( | ||
(not HasIsPerfectGroup(S[Length(S)]) and | ||
not HasAbelianInvariants(S[Length(S)]) and D <> S[ Length(S) ]) or | ||
(HasIsPerfectGroup(S[Length(S)]) and not IsPerfectGroup(S[Length(S)])) | ||
or (HasAbelianInvariants(S[Length(S)]) | ||
and Length(AbelianInvariants(S[Length(S)])) > 0) | ||
) do | ||
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. Ugh, this makes my head hurt... :). But it seems valid, and the code it replaces is not nicer, so no fundamental objection. Simplifying that doesn't seem easy... Hmmm Perhaps we should add some functions like these: IsAttributeKnownAndEqualTo := function(obj, attribute, value)
return Tester(attribute)(obj) and attribute(obj) = value;
end;
IsKnownToBeTrivial := obj -> IsAttributeKnownAndEqualTo(obj, IsTrivial, true); Then the code here could be changed to this (I made another change to avoid writing S :=[];
D := G;
while not IsKnownToBeTrivial(D) and
not IsAttributeKnownAndEqualTo(D, IsPerfectGroup, true) and
not IsAttributeKnownAndEqualTo(D, AbelianInvariants, [] do
Add( S, D );
Info( InfoGroup, 2, "DerivedSeriesOfGroup: step ", Length(S) );
D := DerivedSubgroup( D );
od;
Add( S, D ); I think there are other places in the GAP library which would become more readable with 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. There is one thing I wonder about, though: What if the group 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. @fingolfin BTW, your code really is not entirely the same as the original, but I get the idea. In any case, the code runs fine for e.g. |
||
Add( S, D ); | ||
Info( InfoGroup, 2, "DerivedSeriesOfGroup: step ", Length(S) ); | ||
D := DerivedSubgroup( D ); | ||
od; | ||
|
||
# set filters if the last term is known to be trivial | ||
if HasIsTrivial(S[Length(S)]) and IsTrivial(S[Length(S)]) then | ||
SetIsSolvableGroup(G, true); | ||
if Length(S) <=2 then | ||
Assert(2, IsAbelian(G)); | ||
SetIsAbelian(G, true); | ||
fi; | ||
fi; | ||
|
||
# set IsAbelian filter if length of derived series is more than 2 | ||
if Length(S) > 2 then | ||
Assert(2, not IsAbelian(G)); | ||
SetIsAbelian(G, false); | ||
fi; | ||
|
||
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. How about simplifying this chunk similar to what I proposed for 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. It is not entirely the same, because here we do not want to force an IsAbelian or IsSolvable check. The only simplification I see now (it is late, though....) is to use |
||
# return the series when it becomes stable | ||
return S; | ||
end ); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
gap> START_TEST("IsSolvableGroup.tst"); | ||
gap> List(AllGroups(120), IsSolvableGroup); | ||
[ true, true, true, true, false, true, true, true, true, true, true, true, | ||
true, true, true, true, true, true, true, true, true, true, true, true, | ||
true, true, true, true, true, true, true, true, true, false, false, true, | ||
true, true, true, true, true, true, true, true, true, true, true ] | ||
gap> List(AllTransitiveGroups(DegreeAction, 8), IsSolvable); | ||
[ true, true, true, true, true, true, true, true, true, true, true, true, | ||
true, true, true, true, true, true, true, true, true, true, true, true, | ||
true, true, true, true, true, true, true, true, true, true, true, true, | ||
false, true, true, true, true, true, false, true, true, true, true, false, | ||
false, false ] | ||
gap> IsSolvable(DihedralGroup(24)); | ||
true | ||
gap> IsSolvable(DihedralGroup(IsFpGroup,24)); | ||
true | ||
gap> DerivedSeries(Group(())); | ||
[ Group(()) ] | ||
gap> G := Group((6,7,8,9,10),(8,9,10),(1,2)(6,7),(1,2,3,4)(6,7,8,9));; | ||
gap> Length(DerivedSeriesOfGroup(G)); | ||
4 | ||
gap> HasIsSolvableGroup(G) and not IsSolvable(G) and HasIsAbelian(G) and not IsAbelian(G); | ||
true | ||
gap> IsSolvableGroup(AbelianGroup([2,3,4,5,6,7,8,9,10])); | ||
true | ||
gap> HasIsSolvableGroup(AbelianGroup(IsFpGroup,[2,3,4,5,6,7,8,9,10])); | ||
true | ||
gap> IsSolvableGroup(AbelianGroup(IsFpGroup,[2,3,4,5,6,7,8,9,10])); | ||
true | ||
gap> IsSolvableGroup(Group(())); | ||
true | ||
gap> A := AbelianGroup([3,3,3]);; H := AutomorphismGroup(A);; | ||
gap> B := SylowSubgroup(H, 13);; G := SemidirectProduct(B, A);; | ||
gap> HasIsSolvableGroup(G) and IsSolvable(G); | ||
true | ||
|
||
## some fp-groups | ||
## The following four tests check whether the current IsSolvable method using | ||
## DerivedSeriesOfGroup indeed adds IsAbelian whenever it is appropriate. If | ||
## later new methods for IsSolvable are added, these tests may fail. Then | ||
## these four tests need to be modified accordingly. | ||
gap> F := FreeGroup("r", "s");; r := F.1;; s := F.2;; | ||
gap> G := F/[s^2, s*r*s*r];; | ||
gap> IsSolvable(G) and HasIsAbelian(G) and not IsAbelian(G); | ||
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. This line of the test, and the three similar ones following below, seem not quite right to me: they implicitly assume that So, wouldn't it be better to explicitly call 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. You are right that if a new method comes, then these tests may fail. However, after I checked the examples, I am now pretty confident that I wanted to check whether the method in this PR adds the appropriate I would rather prefer to write a comment into the test file about these examples explaining this situation. But of course if you still prefer otherwise, I can run 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. A comment would be fine. Just so that if it ever fails due to another change (and it may never do so), then whoever has to fix the test at least understands why it is the way it is. Thanks! 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. Added comments to these tests. |
||
true | ||
gap> F := FreeGroup("a", "b", "c", "d");; a := F.1;; b := F.2;; c := F.3;; d:= F.4;; | ||
gap> G := F/[ a^2, b^2, a*b*a^(-1)*b^(-1), c, d ];; | ||
gap> IsSolvable(G) and HasIsAbelian(G) and IsAbelian(G); | ||
true | ||
gap> G := F/[ a^2, b^2, c^2, (a*b)^3, (b*c)^3, (a*c)^2, d ];; | ||
gap> IsSolvable(G) and HasIsAbelian(G) and not IsAbelian(G); | ||
true | ||
gap> G := F/[ a^2, b^2, c^2, d^2, (a*b)^3, (b*c)^3, (c*d)^3, (a*c)^2, (a*d)^2, (b*d)^2 ];; | ||
gap> not IsSolvable(G) and HasIsAbelian(G) and not IsAbelian(G); | ||
true | ||
gap> STOP_TEST("IsSolvableGroup.tst", 10000); | ||
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. Tests, yay!!! |
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 like a clear win to me.
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 function is not currently covered by the tests.
Indeed, I just looked into it and noticed that there is an almost identical method being installed for groups in the filter
IsSubgroupFpGroup
in grpf.gi, line 910.The difference if of course that this method gets invoked even if they are not yet computed.
Which leaves me wondering how to create a group for which this method gets triggered...
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 Ok, so we need a group that is not created as a subgroup of an fp-group, does not know if it is finite but does know its abelian invariants. Perhaps some PSL group constructed by generators over some infinite field? I am not sure how to do that in GAP.
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 did some poking around in the library. As far as I can tell, currently
AbelianInvariants
can only be computed for (subgroups of) fp-groups or for all groups, but then anIsFinite
check is immediately forced. (Or it can be computed for Pcp-groups, but that uses the polycyclic package.) So currently I do not see a way to define a group for which we can computeAbelianInvariants
and this line would run before any other forIsPerfectGroup
.