Skip to content

Add EnumeratorOfCombinations( mset, k ) and speed up orbits of permutation groups on sets and tuples - #6756

Open
fingolfin wants to merge 2 commits into
masterfrom
mh-claude/combinations-enumerator
Open

fingolfin wants to merge 2 commits into
masterfrom
mh-claude/combinations-enumerator

Conversation

@fingolfin

Copy link
Copy Markdown
Member

EnumeratorOfCombinations takes an optional k, like IteratorOfCombinations. With k it lists the combinations in lexicographic order; for a set it ranks a combination with k binomial coefficients.

DomainForAction for permutations on lists of integers returned the placeholder ["BoundedTuples", [1..m], k] for OnSets, which SparseIntKey recognised by comparing strings. It now returns EnumeratorOfCombinations([1..m], k) for OnSets and EnumeratorOfTuples([1..m], k) for OnTuples and OnPairs, which had no domain and fell back to a sort dictionary. Dictionaries index these enumerators by position, or above blistlimit hash them with HashKeyBag. The old hash read the length word and dropped the last entry, so k-sets that differed only in their largest point collided. EnumeratorOfTuples no longer allocates a list in NumberElement.

A position dictionary keeps its values in a list indexed by domain position, which grows to the largest position used, so a small orbit in a domain of up to 2^25 points would allocate hundreds of MB. The orbit has at most Size(G) points, so OrbitStabilizer now hashes domains with more than 8*Size(G) points, including vector spaces for matrix groups.

Before:

gap> G:=SymmetricGroup(100);;
gap> Orbit(G,[1,2,3],OnSets);; time;
3461
gap> Orbit(G,[1,2,3],OnTuples);; time;
40960
gap> OrbitStabilizer(G,[1,2,3],OnTuples);; time;
45121

After:

gap> G:=SymmetricGroup(100);;
gap> Orbit(G,[1,2,3],OnSets);; time;
257
gap> Orbit(G,[1,2,3],OnTuples);; time;
1281
gap> OrbitStabilizer(G,[1,2,3],OnTuples);; time;
4294

Assisted-by: Generative AI

EnumeratorOfCombinations takes an optional k, like
IteratorOfCombinations. With k it lists the combinations in
lexicographic order; for a set it ranks a combination with k binomial
coefficients.

DomainForAction for permutations on lists of integers returned the
placeholder ["BoundedTuples", [1..m], k] for OnSets, which SparseIntKey
recognised by comparing strings. It now returns
EnumeratorOfCombinations([1..m], k) for OnSets and
EnumeratorOfTuples([1..m], k) for OnTuples and OnPairs, which had no
domain and fell back to a sort dictionary. Dictionaries index these
enumerators by position, or above blistlimit hash them with HashKeyBag.
The old hash read the length word and dropped the last entry, so k-sets
that differed only in their largest point collided. EnumeratorOfTuples
no longer allocates a list in NumberElement.

A position dictionary keeps its values in a list indexed by domain
position, which grows to the largest position used, so a small orbit in
a domain of up to 2^25 points would allocate hundreds of MB. The orbit
has at most Size(G) points, so OrbitStabilizer now hashes domains with
more than 8*Size(G) points, including vector spaces for matrix groups.

Before:

    gap> G:=SymmetricGroup(100);;
    gap> Orbit(G,[1,2,3],OnSets);; time;
    3461
    gap> Orbit(G,[1,2,3],OnTuples);; time;
    40960
    gap> OrbitStabilizer(G,[1,2,3],OnTuples);; time;
    45121

After:

    gap> G:=SymmetricGroup(100);;
    gap> Orbit(G,[1,2,3],OnSets);; time;
    257
    gap> Orbit(G,[1,2,3],OnTuples);; time;
    1281
    gap> OrbitStabilizer(G,[1,2,3],OnTuples);; time;
    4294

Assisted-by: Generative AI
@fingolfin fingolfin added kind: enhancement Label for issues suggesting enhancements; and for pull requests implementing enhancements topic: performance bugs or enhancements related to performance (improvements or regressions) topic: library release notes: use title For PRs: the title of this PR is suitable for direct use in the release notes labels Oct 16, 2026
@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.39394% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 79.70%. Comparing base (856aeeb) to head (9184750).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
lib/combinat.gi 99.31% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6756      +/-   ##
==========================================
+ Coverage   79.67%   79.70%   +0.02%     
==========================================
  Files         684      684              
  Lines      295460   295609     +149     
  Branches     8656     8656              
==========================================
+ Hits       235419   235605     +186     
+ Misses      58215    58176      -39     
- Partials     1826     1828       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hulpke hulpke left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice.

Comment thread lib/dict.gd

## default for the option blistlimit of NewDictionary: the largest domain
## that gets a position dictionary
BindGlobal("DICT_BLISTLIMIT", 2^25);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe indicate how this number arose? Is it 2^17 bytes, i.e. 130kB?

@fingolfin fingolfin changed the title Add k-subset enumerator and use it and the existing tuples enumerator to speed up orbits on sets and tuples Add EnumeratorOfCombinations( mset, k ) and speed up orbits of permutation groups on sets and tuples Oct 16, 2026

@james-d-mitchell james-d-mitchell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think this is really good but should probably have a bit more robust checks on the additional arguments as indicated.

Comment thread lib/combinat.gi
return enum;
end);

InstallGlobalFunction(EnumeratorOfCombinations, function(mset, arg...)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we give an error or something if arg is of length >1?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also shouldn't there be some checks that if arg[1] is present then it is a non-negative integer? At the moment I can do stupid things like:

gap> EnumeratorOfCombinations([1 .. 3], infinity);
[  ]
gap> EnumeratorOfCombinations([1 .. 3], -infinity);
Error, Binomial: <k> must be an integer (not a positional object)
Stack trace:
*[1] Binomial( Length( els ), k )
   @ /Users/jdm3/gap/lib/combinat.gi:329
 [2] EnumeratorOfCombinations_k( mset, arg[1] )
   @ /Users/jdm3/gap/lib/combinat.gi:366
<function "EnumeratorOfCombinations">( <arguments> )
 called from read-eval loop at *stdin*:2
you can enter 'quit;' to quit to outer loop
brk>
gap> EnumeratorOfCombinations([1 .. 3], "a");
[  ]
gap> EnumeratorOfCombinations([1 .. 3], "a", "b");
[  ]

Comment thread lib/combinat.gi Outdated
BindGlobal("ElementNumber_Combinations_set", function(enum, num)
local set, n, r, comb, c, i, lo, hi, mid;
if num > enum!.size then
Error("Index ", num, " not bound.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Error("Index ", num, " not bound.");
Error("Index ", num, " not bound, expected at most ", enum!.size);

Might be a bit more helpful.

A k that was not a non-negative integer, such as infinity or a string,
gave an empty list or an error from Binomial, and extra arguments were
ignored. Both now raise an error. An index out of range now reports
the length, and DICT_BLISTLIMIT says how much memory it allows.

Assisted-by: Generative AI

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind: enhancement Label for issues suggesting enhancements; and for pull requests implementing enhancements release notes: use title For PRs: the title of this PR is suitable for direct use in the release notes topic: library topic: performance bugs or enhancements related to performance (improvements or regressions)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants