Implement IsJoinIrreducible and IsMeetIrreducible#910
Implement IsJoinIrreducible and IsMeetIrreducible#910ThatOtherAndrew wants to merge 8 commits intodigraphs:mainfrom
Conversation
|
Requesting a review from @james-d-mitchell please - how does this look? Is the PR description sufficiently detailed and mathematically accurate? |
james-d-mitchell
left a comment
There was a problem hiding this comment.
Looks good to me! If you also resolve #714, then this PR (or the code if merged first) should be updated to check for that rather than IsPartialOrderDigraph. Otherwise, this looks good to me!
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #910 +/- ##
=======================================
Coverage 97.35% 97.36%
=======================================
Files 50 50
Lines 21045 21069 +24
Branches 639 639
=======================================
+ Hits 20489 20513 +24
Misses 491 491
Partials 65 65 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
If I understand correctly, InDegrees is faster than InDegreeOfVertexNC here due to caching behaviour where the in degrees of the digraph are computed once then looked up from cache, as opposed to the other function which has to recompute for each input vertex. Co-authored-by: James Mitchell <james-d-mitchell@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds vertex-level operations to determine join-/meet-irreducibility on order-structured digraphs, and extends the standard operations test suite to cover these new APIs (motivated by #400).
Changes:
- Declares new operations
IsJoinIrreducibleandIsMeetIrreduciblein the operations interface. - Implements both operations in
gap/oper.giusing the reflexive transitive reduction (Hasse diagram) as a shortcut. - Adds test coverage in
tst/standard/oper.tstfor several small lattices and input validation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
gap/oper.gd |
Declares the two new public operations. |
gap/oper.gi |
Implements irreducibility checks via Hasse-diagram degree counts. |
tst/standard/oper.tst |
Adds tests for N5, chains, B2, M3, plus error handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| local hasse; | ||
| if not IsPartialOrderDigraph(D) then | ||
| ErrorNoReturn("the 1st argument <D> must satisfy IsPartialOrderDigraph,"); | ||
| elif not v in DigraphVertices(D) then | ||
| ErrorNoReturn("the 2nd argument <v> must be a vertex of the ", | ||
| "1st argument <D>,"); | ||
| fi; | ||
| hasse := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D)); | ||
| # join-irreducible iff at most one lower cover in the Hasse diagram | ||
| return InDegrees(hasse)[v] <= 1; |
There was a problem hiding this comment.
IsJoinIrreducible is implemented as “at most one lower cover in the Hasse diagram”. This implication only holds in (finite) join-semilattices/lattices; for a general partial order it can be wrong (e.g. two incomparable minimal upper bounds means joins may not exist, yet a vertex can have multiple lower covers while still not being the join of any two smaller vertices). Consider either tightening the precondition to IsJoinSemilatticeDigraph/IsLatticeDigraph (and error accordingly), or implementing the join-based definition via PartialOrderDigraphJoinOfVertices so the result matches the operation’s stated meaning for arbitrary partial orders.
| local hasse; | |
| if not IsPartialOrderDigraph(D) then | |
| ErrorNoReturn("the 1st argument <D> must satisfy IsPartialOrderDigraph,"); | |
| elif not v in DigraphVertices(D) then | |
| ErrorNoReturn("the 2nd argument <v> must be a vertex of the ", | |
| "1st argument <D>,"); | |
| fi; | |
| hasse := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D)); | |
| # join-irreducible iff at most one lower cover in the Hasse diagram | |
| return InDegrees(hasse)[v] <= 1; | |
| local verts, i, j, x, y, join; | |
| if not IsPartialOrderDigraph(D) then | |
| ErrorNoReturn("the 1st argument <D> must satisfy IsPartialOrderDigraph,"); | |
| elif not v in DigraphVertices(D) then | |
| ErrorNoReturn("the 2nd argument <v> must be a vertex of the ", | |
| "1st argument <D>,"); | |
| fi; | |
| # An element v is join-irreducible if it is not the join of | |
| # two distinct strictly smaller elements. We check all unordered | |
| # pairs of vertices (x, y) with x <> y and neither equal to v. | |
| verts := DigraphVertices(D); | |
| for i in [1 .. Length(verts) - 1] do | |
| x := verts[i]; | |
| if x = v then | |
| continue; | |
| fi; | |
| for j in [i + 1 .. Length(verts)] do | |
| y := verts[j]; | |
| if y = v then | |
| continue; | |
| fi; | |
| join := PartialOrderDigraphJoinOfVertices(D, x, y); | |
| if join = v then | |
| # v can be expressed as the join of two distinct smaller elements | |
| return false; | |
| fi; | |
| od; | |
| od; | |
| # No such pair found; v is join-irreducible. | |
| return true; |
| if not IsPartialOrderDigraph(D) then | ||
| ErrorNoReturn("the 1st argument <D> must satisfy IsPartialOrderDigraph,"); | ||
| elif not v in DigraphVertices(D) then | ||
| ErrorNoReturn("the 2nd argument <v> must be a vertex of the ", | ||
| "1st argument <D>,"); | ||
| fi; | ||
| hasse := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D)); | ||
| # meet-irreducible iff at most one upper cover in the Hasse diagram | ||
| return OutDegreeOfVertexNC(hasse, v) <= 1; |
There was a problem hiding this comment.
IsMeetIrreducible relies on “at most one upper cover in the Hasse diagram”, which (like the join case) is only equivalent to “not a meet of two distinct larger elements” when meets exist appropriately (e.g. meet-semilattices/lattices). For arbitrary partial orders, vertices can have multiple upper covers while no such meet representation exists. Either restrict the accepted digraphs to IsMeetSemilatticeDigraph/IsLatticeDigraph, or compute using the actual meet operation to match the documented semantics.
| fi; | ||
| hasse := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D)); | ||
| # join-irreducible iff at most one lower cover in the Hasse diagram | ||
| return InDegrees(hasse)[v] <= 1; |
There was a problem hiding this comment.
IsJoinIrreducible uses InDegrees(hasse)[v], which computes indegrees for all vertices even though only one value is needed. Using InDegreeOfVertexNC(hasse, v) (mirroring the OutDegreeOfVertexNC usage below) avoids the extra O(|V|+|E|) work per call on large digraphs.
| return InDegrees(hasse)[v] <= 1; | |
| return InDegreeOfVertexNC(hasse, v) <= 1; |
| if not IsPartialOrderDigraph(D) then | ||
| ErrorNoReturn("the 1st argument <D> must satisfy IsPartialOrderDigraph,"); | ||
| elif not v in DigraphVertices(D) then | ||
| ErrorNoReturn("the 2nd argument <v> must be a vertex of the ", | ||
| "1st argument <D>,"); | ||
| fi; | ||
| hasse := DigraphReflexiveTransitiveReduction(DigraphMutableCopyIfMutable(D)); | ||
| # join-irreducible iff at most one lower cover in the Hasse diagram |
There was a problem hiding this comment.
The only explicit precondition checked is IsPartialOrderDigraph(D), but IsPartialOrderDigraph does not exclude multidigraphs. If D has multiple edges, DigraphReflexiveTransitiveReduction will throw a different error (“must be a digraph with no multiple edges”) from inside this method. Consider either rejecting multidigraphs up front with a clear error, or normalizing the copied digraph by removing multiple edges before computing the reduction.
Adds two new operations to section 10 (Operations for vertices) of
oper.gi/oper.gd:IsJoinIrreducible(D, v)— returnstrueif vertexvis not the join of any two distinct smaller nodes inDIsMeetIrreducible(D, v)— returnstrueif vertexvis not the meet of any two distinct larger nodes inDBoth were originally implemented by building the full O(N²) join/meet table, but then optimised to use
DigraphReflexiveTransitiveReduction, a function producing a Hasse diagram which allows for a computational shortcut (see SageMath docs below):https://doc.sagemath.org/html/en/reference/combinat/sage/combinat/posets/hasse_diagram.html#sage.combinat.posets.hasse_diagram.HasseDiagram.find_nontrivial_congruence
Tests are merged into
tst/standard/oper.tst, covering N5, a chain, B2, M3, and error handling (two test case sets written by myself and the rest with LLM assistance because I do not think my mathematical knowledge of the underlying concepts is strong enough to come up with reasonably comprehensive/diverse test cases).Closes #400