Add EnumeratorOfPartitionsSet enumerator#6384
Add EnumeratorOfPartitionsSet enumerator#6384reiniscirpons wants to merge 3 commits intogap-system:masterfrom
EnumeratorOfPartitionsSet enumerator#6384Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6384 +/- ##
========================================
Coverage 78.69% 78.70%
========================================
Files 684 684
Lines 292892 293008 +116
Branches 8660 8686 +26
========================================
+ Hits 230497 230607 +110
- Misses 60581 60587 +6
Partials 1814 1814 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| ## gap> i := 0;; for c in PartitionsSet(m) do i := i+1; od; | ||
| ## gap> i; |
There was a problem hiding this comment.
Any reason not do just do
| ## gap> i := 0;; for c in PartitionsSet(m) do i := i+1; od; | |
| ## gap> i; | |
| ## gap> Length(PartitionsSet(m)); |
| ## gap> Position(cm, [[1, 3], [2, 5], [4], [6, 7, 8, 9]]); | ||
| ## 11001 | ||
| ## gap> cm[11001]; | ||
| ## [ [ 1, 3 ], [ 2, 5 ], [ 4 ], [ 6, 7, 8, 9 ] ] |
There was a problem hiding this comment.
How about this instead:
| ## gap> Position(cm, [[1, 3], [2, 5], [4], [6, 7, 8, 9]]); | |
| ## 11001 | |
| ## gap> cm[11001]; | |
| ## [ [ 1, 3 ], [ 2, 5 ], [ 4 ], [ 6, 7, 8, 9 ] ] | |
| ## gap> Position(cm, last); | |
| ## 1000 |
|
Very nice achievement, @reiniscirpons, thanks! |
fingolfin
left a comment
There was a problem hiding this comment.
The documentation seems fine, the feature is useful. I didn't try to understand the code in depth, that'd require a lot of work. But I trust @reiniscirpons who put in a lot of work, the code is derived from a formalization and well-documented, and there are tests.
Maybe two very minor tweaks can be considered for the documentation, but they are not required.
| ## <#GAPDoc Label="IteratorOfPartitionsSet"> | ||
| ## <ManSection> | ||
| ## <Func Name="IteratorOfPartitionsSet" Arg='set [, k [ flag ] ]'/> | ||
| ## <Heading>Iterator and enumerator of unordered set partitions</Heading> |
There was a problem hiding this comment.
I am unsure with this comment.
The iterator accepts <positive integer> and <set> as first argument.
PartitionsSet accepts only <set>.
GAP proposes other function for <positive integer> and its called Partitions.
I would profer to have primary function, focusing entirely each of these.
Same goes here, I would prefer to accept positive integer here too.
This PR adds an enumerator for the
PartitionsSetobject, which returns all set partitions of a finite set, with the possibility of restricting the number of parts too. This PR resolves #6362.Interestingly, the enumerator is faster than the iterator for a fixed
k(whenkis not fixed, they seem to run comparably fast):I also used the Rocq theorem prover at the initial stages of writing this function to help get the encoding/decoding logic correct (see Further details section for more details).
Text for release notes
see title
Further details
The enumerator is implemented using
EnumeratorByFunctions. To do this we needed to implement two functions: an encoder, which takes a partition and returns its number with respect to some fixed order (NumberElement), and a decoder which does the opposite (ElementNumber). It was not initially clear to me how to do this, I knew I would make use of the identityStirling2(n, k) = Stirling2(n-1, k-1) + k * Stirling2(n-1, k), but how exactly to do this would take a lot of iteration on my part if I were to do it directly in GAP. Another option would be to sketch it out on paper, but I decided to instead model it in Rocq. I am including below an annotated proof script I came up with as a result of this, for those interested. It should work with Rocq 9 and mathcomp >= 2.5:It was useful since I could detect when the encode/decode functions are wrong: if a function was wrong, I would get stuck when proving a lemma. But the way I got stuck in the proof informed me on what I needed to change in the function to make it more correct. It took about 2-3 iterations until I got the functions right and got all of the proofs correct.
This gave me a good idea for what the GAP functions should look like. I could not translate the functions 1-1 to GAP, since the Rocq script uses a functional style. If I had more Rocq-fu I could probably write it closer to an imperative style. So the main complexity was unrolling the recursion when implementing these functions in GAP and translating the inductive partition definition to the list definition. But this was not too bad, and the big advantage was that I did not have to second guess the mathematical foundations of my encode/decode function.