Skip to content

Recognise SL2 in naming#457

Open
fingolfin wants to merge 2 commits into
masterfrom
codex/issue-345-degree2-sl
Open

Recognise SL2 in naming#457
fingolfin wants to merge 2 commits into
masterfrom
codex/issue-345-degree2-sl

Conversation

@fingolfin
Copy link
Copy Markdown
Member

Co-authored-by: Codex codex@openai.com

Fixes #345

But I am not sure we really want that... the method used here (RECOG.IsThisSL2Natural) seems to be quite different to what we do in naming otherwise.

Would be really good to discuss this one with @aniemeyer. I'll leave this PR unmerged for now.

(@Till-Eisen has recently looked a lot at constructive recognition of SL2 in natural rep and may have thoughts on this as well...)

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.08%. Comparing base (67ae22b) to head (7bec1f8).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #457      +/-   ##
==========================================
- Coverage   88.13%   88.08%   -0.05%     
==========================================
  Files          45       45              
  Lines       19023    19034      +11     
==========================================
+ Hits        16765    16767       +2     
- Misses       2258     2267       +9     
Files with missing lines Coverage Δ
gap/matrix/classical.gi 84.83% <100.00%> (+0.03%) ⬆️
gap/projective/classicalnatural.gi 69.19% <100.00%> (-0.13%) ⬇️

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@fingolfin
Copy link
Copy Markdown
Member Author

I talked to @aniemeyer . She sent me code written by a student of hers which implements the algorithm from https://doi.org/10.1112/plms/s3-65.3.555 for SL_2. We can compare it with what's in recog and then decide what to use.

@fingolfin
Copy link
Copy Markdown
Member Author

Here is the code:

#############################################################################
##
##  classical2.gi        recog package                 
##
##  This code is code originally written by Sabina Panneck
#############################################################################



##############################
##                          ##
## sg (functions for d = 2) ##
##                          ##
##############################

# generates the next random element and its char polynomial
RECOG.TestRandomElementCase2 := function ( recognise, grp )

    local g, porder;

    if recognise.d <> 2 then return false; fi;

    recognise.g := PseudoRandom(grp);
    recognise.cpol := CharacteristicPolynomial(recognise.g); 
    recognise.n := recognise.n + 1;

    g := recognise.g;

    if recognise.needPOrders then
        porder := ProjectiveOrder(g);
        AddSet( recognise.porders, porder );

	# for further calculations find h in G s.t. h^2 is not in Z(G)
        if porder[1] > 2 then
	    recognise.h := g;
	    recognise.hasExp2 := false;
	    recognise.needPOrders := false;
	fi;
    fi;

    return fail;
end;


# tests whether group is abelian
RECOG.IsAbelian := function ( recognise, grp )

    if recognise.d <> 2 then return false; fi;

    if IsAbelian( grp ) then
	recognise.isAbelian := true;
        # an abelian group does not contain SL(2,q)
        Info( InfoClassical, 2, 
        "The group is abelian and thus doesn't contain a classical group");
	recognise.IsSLContained := false;
	return true;
    fi;

    recognise.isAbelian := false;
    return false;
end;


# tests whether group modulo scalars has exp = 2
RECOG.HasExp2 := function ( recognise, grp )

    local generators, gen, porder;

    if recognise.d <> 2 then return false; fi;
    if recognise.hasExp2 <> "unknown" then return false; fi;

    generators := recognise.generators;

    # if testing random elements did not rule out
    # the case exp(G/Z) = 2 then test generators
    if recognise.n > 15 then
	# find projective order of generators and - if possible -
	# set h such that h^2 is not in Z(G)
	for gen in generators do
	    porder := ProjectiveOrder( gen );
	    if porder[1] > 2 then
	        recognise.h := gen;
	        recognise.hasExp2 := false;
	        return false;
	    fi;
	od;
	# if all generators have projective order <= 2 then 
	# g^2 in Z for all g in G and thus (gZ)^2 = Z
	# hence ord(gZ) = 2 for all gZ in G/Z, i.e. exp(G/Z) = 2
	recognise.hasExp2 := true;
        # a group with exp(G/Z) = 2 does not contain SL(2,q)
	Info( InfoClassical, 2,
        "The group modulo scalars has exponent 2 and thus doesn't contain a classical group");
	recognise.IsSLContained := false;
	return true;
    fi;

    return fail;
end;


# tests whether G is a subgroup of GammaL(1,q^2)
RECOG.IsSubgroupOfGammaL := function ( recognise, grp )

    local h, q, gen, generators, x, x2, charPol;

    if recognise.d <> 2 then return false; fi;

    # in case we haven't found h yet try again later
    if recognise.h  = fail then return fail; fi;

    h := recognise.h;
    q := recognise.q;
    charPol := CharacteristicPolynomial(h^2);
    x := h^4;
    generators := recognise.generators;

    # if h^2 is reducible then G is not conjugate to a subgroup of GammaL
    if not IsIrreducible(charPol) then
	recognise.isSubgroupOfGammaL := false;
	return false;
    fi;

    # if h^2 is not 'primitive', i.e a ppd(2,q;2)-element, then find a new h
    if ( Order( h^2 ) in Factors( q^2-1 ) ) = false or
     Order( h^2 ) in Factors( q-1 ) then
        recognise.needPOrders := true;
        return fail;
    fi;

    for gen in generators do
	x2 := x^gen;
	if (x2 * x <> x * x2) then
	    recognise.isSubgroupOfGammaL := false;
            return false;
      	fi;
    od;

    recognise.isSubgroupOfGammaL := true;
    # a subgroup of GammaL does not contain SL(2,q)
    Info( InfoClassical, 2, 
    "The group is conjugate to a subgroup of GammaL(1,", q, ") and thus doesn't contain a classical group");
    recognise.IsSLContained := false;
    return true;
end;


# tests whether G is imprimitive
RECOG.IsImprimitive := function ( recognise, grp )

  local h, f, gen, genNew, generators, eigenvectors;

    if recognise.d <> 2 then return false; fi;

    # in case we haven't found h yet try again later
    if recognise.h = fail then return fail; fi;
 
    h := recognise.h;
    f := recognise.field;
    generators := recognise.generators;
    eigenvectors := Eigenvectors( f, h^2 );

    # if there are not two distict eigenspaces then G is primitive
    if Length(eigenvectors) <> 2 then
	recognise.isImprimitive := false;
	return false;
    fi;

    # consider the list of eigenvectors as a matrix
    for gen in generators do
    genNew := gen^eigenvectors;
	# if genNew is not monomial, then G is primitive
        if not IsMonomialMatrix( genNew ) then
	    recognise.isImprimitive := false;
	    return false;
        fi;

    od;
    recognise.isImprimitive := true;
    # an imprimitive group does not contain SL(2,q)
    Info( InfoClassical, 2, 
    "The group is imprimitive and thus doesn't contain a classical group");
    recognise.IsSLContained := false;
    return true;
end;


## JUST Add this to IsNotAlternating for d = 3.
# tests if group is isomorphic to Alt(5), Alt(4), Sym(4)
RECOG.IsAlt5Alt4Sym4 := function ( recognise, grp )

    local pgrp, q;
 
    if recognise.d <> 2 then return false; fi;

    pgrp := Image(ProjectiveActionHomomorphismMatrixGroup(grp));
    recognise.pgrp := pgrp;
    q := recognise.q;

    if NrMovedPoints( pgrp ) <= 5 then
      # check if G/Z is isomorphic to Alt4, Alt5 or Sym4
      # and is not one of the exceptions ( GL(2,3), SL(2,3),
      # GL(2,4), SL(2,4), SL(2,5), <SL(2,3), Z(GL(2,5))> )
      # if so, then G does not contain SL(2,q)
      if (Size( pgrp ) = 12 and q <> 3) or 
         (Size( pgrp ) = 24 and q <> 3) or
         (Size( pgrp ) = 60 and q <> 4 and q <> 5) then
        if IsAlternatingGroup(pgrp) or IsSymmetricGroup(pgrp) then
          recognise.isNotAlternating := false;
   	  Info( InfoClassical, 2,
          "The group modulo scalars is isomorphic to Alt5, ALt4 or Sym4");
     	  recognise.IsSLContained := false;
    	  return true;
        fi;
      fi;
    fi;

   Info( InfoClassical, 2,
     "The group modulo scalars is not isomorphic to Alt5, Alt4 or Sym4");
    recognise.isNotAlternating := true;
    return false;
end;


# tests whether a subgroup of GL(2,q) contains SL(2,q)
RECOG.IsSL2Contained := function( recognise, grp )

    if recognise.d <> 2 then return false; fi;
    if recognise.isReducible = true then return false; fi;

    # if G has passed all the tests so far then it is either representable
    # over a proper subfield modulo scalars or it contains SL(2,q)
    if (recognise.isReducible = false and
        recognise.isAbelian = false and
        recognise.hasExp2 = false and
        recognise.isSubgroupOfGammaL = false and
        recognise.isImprimitive = false and
        recognise.isNotAlternating = true) then

       # if G is transitive on the q+1 1-dim subspaces of the underlying
       # vector space then it is not realizable over a proper subfield
       if IsTransitive( recognise.pgrp ) then
          recognise.isRepresentableOverSubfield := false;
          Info(InfoClassical,2,"The group is not generic");
          Info(InfoClassical,2,"and contains SL(", 2, ", ", recognise.q, ");");
          recognise.IsSLContained := true;
          return true;
       fi;

       recognise.isRepresentableOverSubfield := true;
       Info( InfoClassical, 2, 
       "The group is representable over a proper subfield and thus doesn't contain a classical group");
       recognise.IsSLContained := false;
       return true;
    fi;

    return fail;
end;



#sg (methods for d = 2)

    AddMethod( ClassicalMethDb, RECOG.TestRandomElementCase2, 99,
	   "TestRandomElementCase2",
           "makes new random element and stores it and its char poly, case d=2" );

    AddMethod( ClassicalMethDb, RECOG.IsAbelian, 69, "IsAbelian",
           "tests whether group is abelian" );

    AddMethod( ClassicalMethDb, RECOG.HasExp2, 68, "HasExp2",
           "tests for a subgroup G of GL(2,q) whether G/Z has exponent 2" );

    AddMethod( ClassicalMethDb, RECOG.IsSubgroupOfGammaL, 67, "IsSubgroupOfGammaL",
           "tests whether group is conjugate to a subgroup of GammaL(1,q^2)" );

    AddMethod( ClassicalMethDb, RECOG.IsImprimitive, 66, "IsImprimitive",
           "tests whether group is imprimitive" );

    AddMethod( ClassicalMethDb, RECOG.IsAlt5Alt4Sym4, 65, "IsAlt5Alt4Sym4",
           "tests whether grp modulo scalars is isomorhic to Alt5, Alt4 or Sym4" );

    AddMethod( ClassicalMethDb, RECOG.IsSL2Contained, 15, "IsSL2Contained",
           "tests whether group contains SL(2,q)" );
#sg



#sg
		   isAbelian := "unknown",
		   h := fail,
		   hasExp2 := "unknown",
		   isSubgroupOfGammaL := "unknown",
		   isImprimitive := "unknown",
		   generators := GeneratorsOfGroup( grp ),
		   isAlt5Alt4Sym4 := "unknown",
		   isRepresentableOverSubfield := "unknown",
		   pgrp := "unknown",
#sg

@fingolfin fingolfin closed this May 6, 2026
@fingolfin fingolfin reopened this May 26, 2026
fingolfin and others added 2 commits May 27, 2026 15:55
@fingolfin fingolfin force-pushed the codex/issue-345-degree2-sl branch from a491933 to 7bec1f8 Compare May 27, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RecogniseClassical(SL(2,q)).isSLContained does not give true

1 participant