From 382d52db72acdaca0693f7b397d2f83b2abcd6c8 Mon Sep 17 00:00:00 2001 From: Andromeda Date: Wed, 22 Apr 2026 14:33:45 +0200 Subject: [PATCH 1/7] god i can't believe i forgot to commit this --- gap/prop.gd | 2 ++ gap/prop.gi | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/gap/prop.gd b/gap/prop.gd index 808d67dc7..157942b53 100644 --- a/gap/prop.gd +++ b/gap/prop.gd @@ -55,6 +55,7 @@ DeclareProperty("IsDistributiveLatticeDigraph", IsDigraph); DeclareProperty("IsModularLatticeDigraph", IsDigraph); DeclareProperty("Is2EdgeTransitive", IsDigraph); DeclareProperty("IsCograph", IsDigraph); +DeclareProperty("IsBetweenCoverAndLattice", IsDigraph); DeclareSynonymAttr("IsLatticeDigraph", IsMeetSemilatticeDigraph and IsJoinSemilatticeDigraph); DeclareSynonymAttr("IsPreorderDigraph", @@ -82,6 +83,7 @@ InstallTrueMethod(IsAcyclicDigraph, IsEmptyDigraph); InstallTrueMethod(IsAcyclicDigraph, IsTournament and IsTransitiveDigraph); InstallTrueMethod(IsAntisymmetricDigraph, IsAcyclicDigraph); InstallTrueMethod(IsAntisymmetricDigraph, IsDigraph and IsTournament); +InstallTrueMethod(IsBetweenCoverAndLattice, IsLatticeDigraph); InstallTrueMethod(IsBipartiteDigraph, IsCompleteBipartiteDigraph); InstallTrueMethod(IsBipartiteDigraph, IsDigraph and IsUndirectedForest); InstallTrueMethod(IsCompleteMultipartiteDigraph, IsCompleteBipartiteDigraph); diff --git a/gap/prop.gi b/gap/prop.gi index 0c564ef17..7aebfc97e 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -137,6 +137,28 @@ InstallMethod(IsMeetSemilatticeDigraph, "for a digraph", [IsDigraph], D -> DIGRAPHS_IsMeetSemilatticeAndMeetTable(D)[1]); +InstallMethod(IsBetweenCoverAndLattice, "for a digraph", +[IsDigraph], +function(D) + local order, hasse; + + # 1. Topologically sort the nodes in D. + order := DigraphTopologicalSort(copy); + if order = fail then + return false; + fi; + + # 2. Iterate through pairs of nodes of D in topological order and construct a table of their meets. + hasse := DigraphTransitiveReduction(DigraphMutableCopyIfMutable(copy)); + in := InNeighbours(hasse); + # something along the lines of DigraphMeetTable() + out := OutNeighbours(hasse); + # something along the lines of DigraphJoinTable() + + # TODO: step 3 + return true; +end); + InstallImmediateMethod(IsStronglyConnectedDigraph, IsDigraph and HasDigraphStronglyConnectedComponents, 0, D -> Length(DigraphStronglyConnectedComponents(D).comps) = 1); From cd90e4db1401ddf29deb6f79a4b3fa3e094d21e5 Mon Sep 17 00:00:00 2001 From: Andromeda Date: Wed, 22 Apr 2026 15:10:48 +0200 Subject: [PATCH 2/7] Implement meet join table calls --- gap/prop.gi | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/gap/prop.gi b/gap/prop.gi index 7aebfc97e..468df7948 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -140,23 +140,36 @@ D -> DIGRAPHS_IsMeetSemilatticeAndMeetTable(D)[1]); InstallMethod(IsBetweenCoverAndLattice, "for a digraph", [IsDigraph], function(D) - local order, hasse; + local copy, order, hasse, neighbours, table; # 1. Topologically sort the nodes in D. + copy := DigraphRemoveLoops(DigraphMutableCopyIfMutable(D)); # protect from nasty mutable side effects i think order := DigraphTopologicalSort(copy); if order = fail then return false; fi; # 2. Iterate through pairs of nodes of D in topological order and construct a table of their meets. - hasse := DigraphTransitiveReduction(DigraphMutableCopyIfMutable(copy)); - in := InNeighbours(hasse); - # something along the lines of DigraphMeetTable() - out := OutNeighbours(hasse); - # something along the lines of DigraphJoinTable() + hasse := DigraphTransitiveReduction(copy); + neighbours := InNeighbours(hasse); + table := DIGRAPHS_MeetJoinTableBetweenCover( + DigraphNrVertices(copy), + Reversed(order), + neighbours, + false + ); + if table = fail then + return false; + fi; - # TODO: step 3 - return true; + neighbours := OutNeighbours(hasse); + table := DIGRAPHS_MeetJoinTableBetweenCover( + DigraphNrVertices(copy), + order, + neighbours, + true + ); + return table <> fail; end); InstallImmediateMethod(IsStronglyConnectedDigraph, From ca5bc24e71f48c9bdb0b3178978ee5154a9b5581 Mon Sep 17 00:00:00 2001 From: Andromeda Date: Wed, 22 Apr 2026 15:11:17 +0100 Subject: [PATCH 3/7] Implement DIGRAPHS_MeetJoinTableBetweenCover --- gap/prop.gi | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/gap/prop.gi b/gap/prop.gi index 468df7948..415b55fa6 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -91,6 +91,59 @@ function(D, P, U, join) return tab; end); +# Variant of DIGRAPHS_MeetJoinTable that does not require the input digraph +# to have all transitive edges: reachability between previously processed +# vertices is checked via the table being constructed, rather than via +# direct edge queries on the input digraph. +# Note: above descriptive comment is LLM-generated. +BindGlobal("DIGRAPHS_MeetJoinTableBetweenCover", +function(N, P, U, join) + local ord, tab, S, i, x, T, l, q, z, y; + + tab := List([1 .. N], x -> []); + + ord := []; + for i in [1 .. N] do + ord[P[i]] := i; + od; + + S := []; + + for x in P do + tab[x, x] := x; + for y in S do + T := []; + for z in U[x] do + Add(T, tab[y, z]); + od; + T := Set(T); + l := Length(T); + if l = 0 then + return fail; + fi; + q := T[l]; + for i in [1 .. l - 1] do + z := T[i]; + if ord[z] > ord[q] then + q := z; + fi; + od; + for z in T do + # the below conditions are the only part that differs from MeetJoinTable above + if join and tab[q, z] <> z then + return fail; + elif not join and tab[z, q] <> z then + return fail; + fi; + od; + tab[x, y] := q; + tab[y, x] := q; + od; + Add(S, x); + od; + return tab; +end); + InstallMethod(DIGRAPHS_IsJoinSemilatticeAndJoinTable, "for a digraph", [IsDigraph], function(D) From e7db279437bcbb1ab4e2fd53cbad1d5c68d01ba9 Mon Sep 17 00:00:00 2001 From: Andromeda Date: Wed, 22 Apr 2026 15:48:29 +0100 Subject: [PATCH 4/7] we love testinggg --- tst/standard/prop.tst | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tst/standard/prop.tst b/tst/standard/prop.tst index 623ff6713..c270c4d95 100644 --- a/tst/standard/prop.tst +++ b/tst/standard/prop.tst @@ -1427,6 +1427,48 @@ false gap> IsLatticeDigraph(gr); false +# IsBetweenCoverAndLattice +gap> IsBetweenCoverAndLattice(Digraph([])); +true +gap> IsBetweenCoverAndLattice(Digraph([[]])); +true +gap> IsBetweenCoverAndLattice(Digraph([[1]])); +true +gap> IsBetweenCoverAndLattice(ChainDigraph(5)); +true +gap> IsBetweenCoverAndLattice(DigraphReflexiveTransitiveClosure(ChainDigraph(5))); +true +gap> D := Digraph([[2, 3], [4], [4], []]); + +gap> IsLatticeDigraph(D); +false +gap> IsBetweenCoverAndLattice(D); +true +gap> D := Digraph([[2, 3, 4], [4], [4], []]); + +gap> IsBetweenCoverAndLattice(D); +true +gap> D := DigraphReflexiveTransitiveClosure(Digraph([[2, 3], [4], [4], []])); + +gap> IsLatticeDigraph(D); +true +gap> IsBetweenCoverAndLattice(D); +true +gap> D := Digraph([[3, 4], [3, 4], [5], [5], []]); + +gap> IsBetweenCoverAndLattice(D); +false +gap> IsBetweenCoverAndLattice(CycleDigraph(3)); +false +gap> IsBetweenCoverAndLattice(Digraph([[], []])); +false +gap> D := Digraph(IsMutable, [[2, 3], [4], [4], []]); + +gap> IsBetweenCoverAndLattice(D); +true +gap> D; + + # IsDistributiveLatticeDigraph gap> D := Digraph([[11, 10], [], [2], [2], [3], [4, 3], [6, 5], [7], [7], > [8], [9, 8]]); From f1af22bf0a36367f40283d1d47145d62bbbfc638 Mon Sep 17 00:00:00 2001 From: Andromeda Date: Wed, 22 Apr 2026 16:03:10 +0100 Subject: [PATCH 5/7] Make gaplint stop complaining --- gap/prop.gi | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/gap/prop.gi b/gap/prop.gi index 415b55fa6..6397d9945 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -129,7 +129,8 @@ function(N, P, U, join) fi; od; for z in T do - # the below conditions are the only part that differs from MeetJoinTable above + # the below conditions are the only part that + # differs from MeetJoinTable above if join and tab[q, z] <> z then return fail; elif not join and tab[z, q] <> z then @@ -196,21 +197,22 @@ function(D) local copy, order, hasse, neighbours, table; # 1. Topologically sort the nodes in D. - copy := DigraphRemoveLoops(DigraphMutableCopyIfMutable(D)); # protect from nasty mutable side effects i think + copy := DigraphRemoveLoops(DigraphMutableCopyIfMutable(D)); + # ^ protect from nasty mutable side effects i think order := DigraphTopologicalSort(copy); if order = fail then return false; fi; - # 2. Iterate through pairs of nodes of D in topological order and construct a table of their meets. + # 2. Iterate through pairs of nodes of D in topological order + # and construct a table of their meets. hasse := DigraphTransitiveReduction(copy); neighbours := InNeighbours(hasse); table := DIGRAPHS_MeetJoinTableBetweenCover( DigraphNrVertices(copy), Reversed(order), neighbours, - false - ); + false); if table = fail then return false; fi; @@ -220,8 +222,7 @@ function(D) DigraphNrVertices(copy), order, neighbours, - true - ); + true); return table <> fail; end); From 662290cd87a27e66f3aa5d2d0a1e5200c090add5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andromeda=20=E2=9C=A8?= Date: Wed, 22 Apr 2026 17:25:51 +0100 Subject: [PATCH 6/7] Add multidigraph input validation Co-authored-by: Reinis Cirpons <43414125+reiniscirpons@users.noreply.github.com> --- gap/prop.gi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gap/prop.gi b/gap/prop.gi index 6397d9945..ebd83f344 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -195,7 +195,9 @@ InstallMethod(IsBetweenCoverAndLattice, "for a digraph", [IsDigraph], function(D) local copy, order, hasse, neighbours, table; - + if IsMultiDigraph(D) then + ErrorNoReturn("the argument must not be a multidigraph,"); + fi; # 1. Topologically sort the nodes in D. copy := DigraphRemoveLoops(DigraphMutableCopyIfMutable(D)); # ^ protect from nasty mutable side effects i think From d2c54bcb1a9ac90dad4c3a9817aae73d3ddde585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andromeda=20=E2=9C=A8?= Date: Wed, 22 Apr 2026 17:26:12 +0100 Subject: [PATCH 7/7] Remove silly comment Co-authored-by: Reinis Cirpons <43414125+reiniscirpons@users.noreply.github.com> --- gap/prop.gi | 1 - 1 file changed, 1 deletion(-) diff --git a/gap/prop.gi b/gap/prop.gi index ebd83f344..fdcb9107e 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -200,7 +200,6 @@ function(D) fi; # 1. Topologically sort the nodes in D. copy := DigraphRemoveLoops(DigraphMutableCopyIfMutable(D)); - # ^ protect from nasty mutable side effects i think order := DigraphTopologicalSort(copy); if order = fail then return false;