From c02a5f8d65c52e272a0e56450c4ef26c1c2c468e Mon Sep 17 00:00:00 2001 From: joe-dw Date: Fri, 24 Jul 2026 17:55:16 +0100 Subject: [PATCH 01/11] Done I think? --- doc/congsemigraph.xml | 32 ++ doc/semigraph.xml | 77 ++++- doc/z-chap07.xml | 5 + gap/congruences/congsemigraph.gd | 16 +- gap/congruences/congsemigraph.gi | 374 ++++++++++++++++++++- gap/semigroups/semigraph.gd | 4 + gap/semigroups/semigraph.gi | 26 ++ tst/standard/congruences/congsemigraph.tst | 42 ++- tst/standard/semigroups/semigraph.tst | 77 +++-- 9 files changed, 618 insertions(+), 35 deletions(-) diff --git a/doc/congsemigraph.xml b/doc/congsemigraph.xml index 2a8bab25e..2d918c4ad 100644 --- a/doc/congsemigraph.xml +++ b/doc/congsemigraph.xml @@ -114,6 +114,38 @@ gap> AsCongruenceByWangPair(cong); <#/GAPDoc> +<#GAPDoc Label="TraceOfCongruenceByWangPair"> + + + The trace of congruence by Wang pair. + + This operation takes cong, a finite graph inverse semigroup + congruence represented by a Wang pair

, and returns an object + representing the trace of cong. + D := Digraph([[2, 3], [3], [4], []]); + +gap> S := GraphInverseSemigroup(D); + +gap> cong := CongruenceByWangPair(S, [4], [2]); + +gap> tr := TraceOfCongruenceByWangPair(cong); + +gap> a := S.2 * S.4 * S.4 ^ -1 * S.2 ^ -1; +e_2e_4e_4^-1e_2^-1 +gap> ImagesElm(tr, a); +[ 0, v_4, e_4e_4^-1, e_2e_4e_4^-1e_2^-1, e_3e_4e_4^-1e_3^-1, e_1e_3e_4e_4^-1e_3^-1e_1^-1 ] +gap> b := S.8; +v_4 +gap> CongruenceTestMembershipNC(tr, a, b); +true +gap> EquivalenceRelationPartition(tr); +[ [ 0, v_4, e_4e_4^-1, e_2e_4e_4^-1e_2^-1, e_3e_4e_4^-1e_3^-1, e_1e_3e_4e_4^-1e_3^-1e_1^-1 ], [ v_2, e_3e_3^-1 ], [ e_1e_1^-1, e_1e_3e_3^-1e_1^-1 ] ] +]]> + + + <#/GAPDoc> + <#GAPDoc Label="GeneratingCongruencesOfLattice"> diff --git a/doc/semigraph.xml b/doc/semigraph.xml index 0bc680519..cd91baf46 100644 --- a/doc/semigraph.xml +++ b/doc/semigraph.xml @@ -187,7 +187,7 @@ gap> GraphOfGraphInverseSemigroup(S); <#GAPDoc Label="IsGraphInverseSemigroupElementCollection"> - + Every collection of elements of a graph inverse semigroup belongs to the category IsGraphInverseSemigroupElementCollection. For example, @@ -258,8 +258,8 @@ gap> VerticesOfGraphInverseSemigroup(S); A positive integer. If v is a vertex of a graph inverse semigroup (i.e. it satisfies - ), then this attribute returns - the index of this vertex in its graph inverse semigroup.

+ ), then this attribute returns the + index of this vertex in S.

D := Digraph([[3, 4], [3, 4], [4], []]); @@ -273,3 +273,74 @@ gap> IndexOfVertexOfGraphInverseSemigroup(v_3); <#/GAPDoc> + +<#GAPDoc Label="EdgesWithRange"> + + + A list of graph inverse semigroup elements. + + If x is a vertex of a graph inverse semigroup (i.e. it satisfies + ), then this attribute returns a list + of all edges e such that Range(e)=x. If x is not a vertex + then it returns all edges with range Source(x). + + +<#/GAPDoc> + +<#GAPDoc Label="PathsWithRange"> + + + A list of graph inverse semigroup elements. + + If x is a vertex of a graph inverse semigroup (i.e. it satisfies + ), then this attribute returns a list + of all paths (products of edges) p such that Range(p)=x, including + vertices as paths of length 0. If x is not a vertex then it returns all + paths with range Source(x). + + +<#/GAPDoc> + +<#GAPDoc Label="EdgesWithSource"> + + + A list of graph inverse semigroup elements. + + If x is a vertex of a graph inverse semigroup (i.e. it satisfies + ), then this attribute returns a list + of all edges e such that Source(e)=x. If x is not a vertex + then it returns all edges with source Range(x). + + +<#/GAPDoc> + +<#GAPDoc Label="PathsWithSource"> + + + A list of graph inverse semigroup elements. + + If x is a vertex of a graph inverse semigroup (i.e. it satisfies + ), then this attribute returns a list + of all paths (products of edges) p such that Source(p)=x, including + vertices as paths of length 0. If x is not a vertex then it returns all + paths with source Range(x). + D := Digraph([[2, 3], [3, 4], [4], []]); + +gap> S := GraphInverseSemigroup(D); + +gap> EdgesWithRange(VerticesOfGraphInverseSemigroup(G)[4]); +Error, Variable: 'G' must have a value +not in any function at *stdin*:3 +gap> EdgesWithRange(VerticesOfGraphInverseSemigroup(S)[4]); +[ e_4, e_5 ] +gap> PathsWithRange(VerticesOfGraphInverseSemigroup(S)[4]); +[ v_4, e_4, e_1e_4, e_5, e_2e_5, e_3e_5, e_1e_3e_5 ] +gap> EdgesWithSource(VerticesOfGraphInverseSemigroup(S)[2]); +[ e_3, e_4 ] +gap> PathsWithSource(VerticesOfGraphInverseSemigroup(S)[2]); +[ v_2, e_3, e_3e_5, e_4 ] +]]> + + +<#/GAPDoc> diff --git a/doc/z-chap07.xml b/doc/z-chap07.xml index f39ccdfbf..da91137d8 100644 --- a/doc/z-chap07.xml +++ b/doc/z-chap07.xml @@ -248,6 +248,11 @@ <#Include Label = "IsGraphInverseSubsemigroup"> <#Include Label = "VerticesOfGraphInverseSemigroup"> <#Include Label = "IndexOfVertexOfGraphInverseSemigroup"> + <#Include Label = "EdgesWithRange"> + <#Include Label = "EdgesWithSource"> + <#Include Label = "PathsWithRange"> + <#Include Label = "PathsWithSource"> + diff --git a/gap/congruences/congsemigraph.gd b/gap/congruences/congsemigraph.gd index b665d5e10..a91fff829 100644 --- a/gap/congruences/congsemigraph.gd +++ b/gap/congruences/congsemigraph.gd @@ -23,6 +23,20 @@ DeclareOperation("CongruenceByWangPair", DeclareOperation("AsCongruenceByWangPair", [IsSemigroupCongruence]); DeclareOperation("MinimalHereditarySubsetsVertex", - [IsGraphInverseSemigroup, IsPosInt]); + [IsDigraph, IsPosInt]); DeclareAttribute("GeneratingCongruencesOfLattice", IsGraphInverseSemigroup); + +DeclareCategory("IsTraceOfCongruenceByWangPair", + IsSemigroupCongruence + and CanComputeEquivalenceRelationPartition + and IsMagmaCongruence + and IsAttributeStoringRep + and IsFinite); + +DeclareAttribute("TraceOfCongruenceByWangPair", IsCongruenceByWangPair); + +DeclareAttribute("EdgesWithRange", IsGraphInverseSemigroupElement); +DeclareAttribute("EdgesWithSource", IsGraphInverseSemigroupElement); +DeclareOperation("PathsWithRange", [IsGraphInverseSemigroupElement]); +DeclareOperation("PathsWithSource", [IsGraphInverseSemigroupElement]); diff --git a/gap/congruences/congsemigraph.gi b/gap/congruences/congsemigraph.gi index f1a9b4a79..259e1dd22 100644 --- a/gap/congruences/congsemigraph.gi +++ b/gap/congruences/congsemigraph.gi @@ -1,8 +1,9 @@ ############################################################################ ## ## congsemigraph.gi -## Copyright (C) 2022 Marina Anagnostopoulou-Merkouri +## Copyright (C) 2022-2026 Marina Anagnostopoulou-Merkouri ## James Mitchell +## Joseph Ward ## ## Licensing information can be found in the README file of this package. ## @@ -287,3 +288,374 @@ InstallMethod(TrivialCongruence, "for a graph inverse semigroup", [IsGraphInverseSemigroup], S -> AsCongruenceByWangPair(SemigroupCongruence(S, []))); + +InstallMethod(TraceOfCongruenceByWangPair, +"for a congruence by Wang pair", +[IsCongruenceByWangPair], +function(cong) + local S, fam, tr; + + S := IdempotentGeneratedSubsemigroup(Source(cong)); + + fam := GeneralMappingsFamily(ElementsFamily(FamilyObj(S)), + ElementsFamily(FamilyObj(S))); + tr := Objectify(NewType(fam, IsTraceOfCongruenceByWangPair), + rec(cong := cong)); + SetSource(tr, S); + SetRange(tr, S); + return tr; +end); + +InstallMethod(JoinSemigroupCongruences, +"for two traces of congruences by Wang pair", +[IsTraceOfCongruenceByWangPair, IsTraceOfCongruenceByWangPair], +{tr1, tr2} -> TraceOfCongruenceByWangPair( +JoinSemigroupCongruences(tr1!.cong, tr2!.cong))); + +InstallMethod(MeetSemigroupCongruences, +"for two traces of congruences by Wang pair", +[IsTraceOfCongruenceByWangPair, IsTraceOfCongruenceByWangPair], +{tr1, tr2} -> TraceOfCongruenceByWangPair( + MeetSemigroupCongruences(tr1!.cong, tr2!.cong))); + +InstallMethod(IsSubrelation, +"for two traces of congruences by Wang pair", +[IsTraceOfCongruenceByWangPair, IsTraceOfCongruenceByWangPair], +{tr1, tr2} -> IsSubrelation(tr1!.cong, tr2!.cong)); + +InstallMethod(IsSuperrelation, +"for two traces of congruences by Wang pair", +[IsTraceOfCongruenceByWangPair, IsTraceOfCongruenceByWangPair], +{tr1, tr2} -> IsSuperrelation(tr1!.cong, tr2!.cong)); + +InstallMethod(ViewObj, "for trace of a congruence by Wang pair", +[IsTraceOfCongruenceByWangPair], +tr -> Print(ViewString(tr))); + +InstallMethod(ViewString, "for a congruence by Wang pair", +[IsTraceOfCongruenceByWangPair], +function(tr) + return StringFormatted( + "", + ViewString(tr!.cong!.H), + ViewString(tr!.cong!.W)); +end); + +# TODO write an explanation of what the next function implements +# TODO add more examples +InstallMethod(CongruenceTestMembershipNC, +"for the trace of a congruence by Wang pair", +[IsTraceOfCongruenceByWangPair, + IsGraphInverseSemigroupElement, + IsGraphInverseSemigroupElement], +function(tr, elm1, elm2) + local p1, p2, range_elm1_in_H, range_elm2_in_H, tmp, S, e, i; + + if elm1 = elm2 then + return true; + fi; + + p1 := PositivePath(elm1); + p2 := PositivePath(elm2); + + range_elm1_in_H := IsMultiplicativeZero(Source(tr), elm1) + or IndexOfVertexOfGraphInverseSemigroup(Range(p1)) in tr!.cong!.H; + range_elm2_in_H := IsMultiplicativeZero(Source(tr), elm2) + or IndexOfVertexOfGraphInverseSemigroup(Range(p2)) in tr!.cong!.H; + + if range_elm1_in_H or range_elm2_in_H then + return range_elm1_in_H and range_elm2_in_H; + # If either is in the zero class, then the other must be + + elif Source(elm1) <> Source(elm2) or Range(elm1) <> Range(elm2) then + return false; # Since the elements are outside the zero class, + # they cannot be related if they have different source and range + fi; + if Length(p1![1]) > Length(p2![1]) then + tmp := p1; + p1 := p2; + p2 := tmp; # let p1 be the longer path + fi; + + for i in [1 .. Length(p1![1])] do + if p1![1][i] <> p2![1][i] then + return false; # check if the shorter path is a prefix of the longer one + fi; + od; + + S := Source(tr!.cong); + for i in [Length(p1![1]) .. Length(p2![1])] do + e := EdgesOfGraphInverseSemigroup(S)[p2![1][i]]; + if not IndexOfVertexOfGraphInverseSemigroup(Source(e)) in tr!.cong!.W then + return false; + # if any of the edges in the longer path are not W edges, + # then the elements are not related + fi; + od; + + return true; + # p1 is a prefix of p2 and every edge in p2 outside p1 is a W-edge, + # so the pair is related +end); + +# TODO add family check to install method below +InstallMethod(EdgesWithRange, +"for a graph inverse semigroup element", +[IsGraphInverseSemigroupElement], +function(x) + local G; + G := FamilyObj(x)!.semigroup; + if not x in G then + Error("TODO"); + elif not IsVertex(x) then + return EdgesWithRange(Source(x)); + else + return Filtered(EdgesOfGraphInverseSemigroup(G), e -> Range(e) = x); + fi; +end); + +InstallMethod(PathsWithRange, +"for a graph inverse semigroup element", +[IsGraphInverseSemigroupElement], +function(x) + local inPaths, e, inPathsPerEdge, G; + G := FamilyObj(x)!.semigroup; + if not x in G then + Error("TODO"); + else + inPaths := [[x]]; + for e in EdgesWithRange(Source(x)) do + inPathsPerEdge := List(PathsWithRange(Source(e)), p -> p * e * x); + Add(inPaths, inPathsPerEdge); + od; + fi; + return Concatenation(inPaths); +end); + +InstallMethod(EdgesWithSource, +"for a graph inverse semigroup element", +[IsGraphInverseSemigroupElement], +function(x) + local G; + G := FamilyObj(x)!.semigroup; + if not x in G then + Error("TODO"); + elif not IsVertex(x) then + return EdgesWithSource(Range(x)); + else + return Filtered(EdgesOfGraphInverseSemigroup(G), e -> Source(e) = x); + fi; +end); + +InstallMethod(PathsWithSource, +"for a graph inverse semigroup element", +[IsGraphInverseSemigroupElement], +function(x) + local outPaths, e, outPathsPerEdge, G; + G := FamilyObj(x)!.semigroup; + if not x in G then + Error("TODO"); + else + outPaths := [[x]]; + for e in EdgesWithSource(Range(x)) do + outPathsPerEdge := List(PathsWithSource(Range(e)), p -> x * e * p); + Add(outPaths, outPathsPerEdge); + od; + fi; + return Concatenation(outPaths); +end); + +InstallMethod(ImagesElm, +"for the trace of a congruence by Wang pair and an element", +[IsTraceOfCongruenceByWangPair, IsGraphInverseSemigroupElement], +function(tr, x) + local class, p; + if not IsIdempotent(x) then + Error("TODO"); + elif not FamilyRange(FamilyObj(tr!.cong))!.semigroup = + FamilyObj(x)!.semigroup then + Error("TODO"); + fi; + p := PositivePath(x); + if IsMultiplicativeZero(Source(tr!.cong), x) or ( + IndexOfVertexOfGraphInverseSemigroup(Range(p)) in tr!.cong!.H) then + return Concatenation([MultiplicativeZero(Source(tr!.cong))], + Concatenation(List(tr!.cong!.H, h -> List(PathsWithRange( + VerticesOfGraphInverseSemigroup(Source(tr!.cong))[h]), + p -> p * p ^ -1)))); + else + class := [x]; + while IndexOfVertexOfGraphInverseSemigroup(Range(p)) in tr!.cong!.W do + p := p * First(Filtered(EdgesWithSource(Range(p)), e -> not + IndexOfVertexOfGraphInverseSemigroup(Range(e)) + in tr!.cong!.H)); + Add(class, p * p ^ -1); + od; + if not IsVertex(x) then + p := PositivePath(x); + while (not (IsVertex(p)) and IndexOfVertexOfGraphInverseSemigroup( + Source(EdgesOfGraphInverseSemigroup(Source(tr!.cong))[Last(p![1])])) + in tr!.cong!.W) do + if Length(p![1]) > 1 then + p := EvaluateWord(GeneratorsOfSemigroup(Source(tr!.cong)), + p![1]{[1 .. Length(p![1]) - 1]}); + else + p := Source(p); + fi; + Add(class, p * p ^ -1); + od; + fi; + fi; + return class; +end); + +InstallMethod(EquivalenceRelationPartition, +"for the trace of a congruence by Wang Pair", +[IsTraceOfCongruenceByWangPair], +function(tr) + local classes, w, p; + classes := [Concatenation([MultiplicativeZero(Source(tr!.cong))], + Concatenation(List(tr!.cong!.H, h -> List(PathsWithRange( + VerticesOfGraphInverseSemigroup(Source(tr!.cong))[h]), + p -> p * p ^ -1))))]; + if Length(classes[1]) = 1 then + classes := []; + fi; + for w in tr!.cong!.W do + if Intersection(InNeighbours(GraphOfGraphInverseSemigroup(Source( + tr!.cong)))[w], tr!.cong!.W) = [] then + for p in PathsWithRange(VerticesOfGraphInverseSemigroup( + Source(tr!.cong))[w]) do + Add(classes, p * ImagesElm(tr, VerticesOfGraphInverseSemigroup( + Source(tr!.cong))[w]) * p ^ -1); + od; + else + Add(classes, ImagesElm(tr, VerticesOfGraphInverseSemigroup(Source( + tr!.cong))[w])); + fi; + od; + return classes; +end); + + # +InstallMethod(EquivalenceRelationPartition, +"for a congruence by Wang Pair", +[IsCongruenceByWangPair], +function(cong) + local classes, w, p, q, ps, pws; + classes := [Concatenation([MultiplicativeZero(Source(cong))], + Concatenation(List(cong!.H, h -> Concatenation(List(PathsWithRange( + VerticesOfGraphInverseSemigroup(Source(cong))[h]), p -> List( + PathsWithRange(VerticesOfGraphInverseSemigroup(Source(cong))[h]), + q -> p * q ^ -1))))))]; + if Length(classes[1]) = 1 then + classes := []; + fi; + for w in cong!.W do + if Intersection(InNeighbours(GraphOfGraphInverseSemigroup( + Source(cong)))[w], cong!.W) = [] then + for p in PathsWithRange(VerticesOfGraphInverseSemigroup( + Source(cong))[w]) do + for q in PathsWithRange(VerticesOfGraphInverseSemigroup( + Source(cong))[w]) do + Add(classes, p * ImagesElm(TraceOfCongruenceByWangPair(cong), + VerticesOfGraphInverseSemigroup(Source(cong))[w]) * q ^ -1); + od; + od; + else + ps := PathsWithRange(VerticesOfGraphInverseSemigroup(Source(cong))[w]); + pws := Filtered(ps, p -> (not IsVertex(p)) and ( + IndexOfVertexOfGraphInverseSemigroup(Source( + EdgesOfGraphInverseSemigroup(Source(cong))[Last(p![1])])) + in cong!.W)); + ps := Difference(ps, pws); + for p in ps do + for q in ps do + Add(classes, p * ImagesElm(TraceOfCongruenceByWangPair(cong), + VerticesOfGraphInverseSemigroup(Source(cong))[w]) * q ^ -1); + od; + for q in pws do + Add(classes, p * ImagesElm(TraceOfCongruenceByWangPair(cong), + VerticesOfGraphInverseSemigroup(Source(cong))[w]) * q ^ -1); + od; + od; + for p in pws do + for q in ps do + Add(classes, p * ImagesElm(TraceOfCongruenceByWangPair(cong), + VerticesOfGraphInverseSemigroup(Source(cong))[w]) * q ^ -1); + od; + for q in pws do + if Last(p![1]) <> Last(q![1]) then + Add(classes, p * ImagesElm(TraceOfCongruenceByWangPair(cong), + VerticesOfGraphInverseSemigroup(Source(cong))[w]) * q ^ -1); + fi; + od; + od; + fi; + od; + return classes; +end); + +InstallMethod(ImagesElm, +"for a congruence by Wang Pair and an element", +[IsCongruenceByWangPair, IsGraphInverseSemigroupElement], +function(cong, x) + local p, q; + if not x in Source(cong) then + Error("TODO"); + elif IsMultiplicativeZero(Source(cong), x) or ( + IndexOfVertexOfGraphInverseSemigroup(Range(PositivePath(x))) in cong!.H) + then + return Concatenation([MultiplicativeZero(Source(cong))], Concatenation( + Concatenation(List(cong!.H, h -> List(PathsWithRange( + VerticesOfGraphInverseSemigroup(Source(cong))[h]), + p -> List(PathsWithRange( + VerticesOfGraphInverseSemigroup(Source(cong))[h]), + q -> p * q ^ -1)))))); + elif IsIdempotent(x) then + return ImagesElm(TraceOfCongruenceByWangPair(cong), x); + else + p := PositivePath(x); + q := NegativePath(x); + if Last(p![1]) = - First(q![1]) and (not IsVertex(p)) + and (IndexOfVertexOfGraphInverseSemigroup(Source( + EdgesOfGraphInverseSemigroup(Source(cong))[-First(q![1])])) + in cong!.W) then + if Length(p![1]) = 1 then + p := Source(p); + q := EvaluateWord(GeneratorsOfSemigroup(Source(cong)), + q![1]{[2 .. Length(q![1])]}); + elif Length(q![1]) = 1 then + p := EvaluateWord(GeneratorsOfSemigroup(Source(cong)), + p![1]{[1 .. Length(p![1]) - 1]}); + q := Range(q); + else + p := EvaluateWord(GeneratorsOfSemigroup(Source(cong)), + p![1]{[1 .. Length(p![1]) - 1]}); + q := EvaluateWord(GeneratorsOfSemigroup(Source(cong)), + q![1]{[2 .. Length(q![1])]}); + fi; + return ImagesElm(cong, p * q); + else + return p * ImagesElm(TraceOfCongruenceByWangPair(cong), Range(p)) * q; + fi; + fi; +end); + +InstallMethod(CongruenceTestMembershipNC, +"for a congruence by Wang pair and two graph inverse semigroup elements", +[IsCongruenceByWangPair, IsGraphInverseSemigroupElement, + IsGraphInverseSemigroupElement], +138, +function(cong, x, y) + return CongruenceTestMembershipNC(TraceOfCongruenceByWangPair(cong), + x ^ -1 * x, y ^ -1 * y) + and (IsIdempotent(x * y ^ -1) + or IndexOfVertexOfGraphInverseSemigroup(Range + (PositivePath(x * y ^ -1))) in cong!.H); +end); + +InstallMethod(TraceOfSemigroupCongruence, +"for a congruence by Wang Pair", +[IsCongruenceByWangPair], +138, TraceOfCongruenceByWangPair); diff --git a/gap/semigroups/semigraph.gd b/gap/semigroups/semigraph.gd index 69de98a70..e939730c3 100644 --- a/gap/semigroups/semigraph.gd +++ b/gap/semigroups/semigraph.gd @@ -27,6 +27,10 @@ DeclareAttribute("Source", IsGraphInverseSemigroupElement); DeclareOperation("IsVertex", [IsGraphInverseSemigroupElement]); +DeclareAttribute("PositivePath", IsGraphInverseSemigroupElement); +DeclareAttribute("NegativePath", IsGraphInverseSemigroupElement); +# TODO IsEdge? + InstallTrueMethod(IsGeneratorsOfInverseSemigroup, IsGraphInverseSemigroupElementCollection); diff --git a/gap/semigroups/semigraph.gi b/gap/semigroups/semigraph.gi index 69e2b0f79..46778e5be 100644 --- a/gap/semigroups/semigraph.gi +++ b/gap/semigroups/semigraph.gi @@ -8,6 +8,10 @@ ############################################################################# ## +# TODO: Implement special methods for: +# * NrIdempotents +# * IdempotentGeneratedSubsemigroup + InstallMethod(AsMonoid, "for a graph inverse semigroup", [IsGraphInverseSemigroup], ReturnFail); @@ -302,3 +306,25 @@ InstallMethod(IsWholeFamily, "for a subsemigroup of a graph inverse semigroup", [IsGraphInverseSubsemigroup], S -> Size(ElementsFamily(FamilyObj(S))!.semigroup) = Size(S)); + +InstallMethod(PositivePath, +"for a graph inverse semigroup element", +[IsGraphInverseSemigroupElement], +function(elt) + local pos, S; + + pos := PositionProperty(elt![1], IsNegInt); + if pos = fail then + return elt; + elif pos = 1 then + return Source(elt); + fi; + # Get the semigroup containing "elt" + S := FamilyObj(elt)!.semigroup; + return EvaluateWord(GeneratorsOfSemigroup(S), elt![1]{[1 .. pos - 1]}); +end); + +InstallMethod(NegativePath, +"for a graph inverse semigroup element", +[IsGraphInverseSemigroupElement], +elt -> PositivePath(elt ^ -1) ^ -1); diff --git a/tst/standard/congruences/congsemigraph.tst b/tst/standard/congruences/congsemigraph.tst index a4a18b243..ef21ecce0 100644 --- a/tst/standard/congruences/congsemigraph.tst +++ b/tst/standard/congruences/congsemigraph.tst @@ -9,7 +9,7 @@ ############################################################################ # -#@local C, D, L, S, cong, cong1, cong2, e_1, e_3, i, j, join, meet, pos, val +#@local c, C, D, L, S, cong, cong1, cong2, e_1, e_3, i, j, join, meet, pos, val gap> START_TEST("Semigroups package: standard/congruences/congsemigraph.tst"); gap> LoadPackage("semigroups", false);; @@ -98,15 +98,14 @@ gap> AsSemigroupCongruenceByGeneratingPairs(C); <2-sided semigroup congruence over with 2 generating pairs> gap> EquivalenceRelationPartition(C); -[ [ e_1, e_1e_3e_3^-1 ], - [ e_4, v_4, e_4^-1, 0, e_2e_4, e_3e_4, e_4e_4^-1, e_4^-1e_2^-1, - e_4^-1e_3^-1, e_1e_3e_4, e_2e_4e_4^-1, e_3e_4e_4^-1, e_4e_4^-1e_2^-1, - e_4e_4^-1e_3^-1, e_4^-1e_3^-1e_1^-1, e_1e_3e_4e_4^-1, - e_2e_4e_4^-1e_2^-1, e_2e_4e_4^-1e_3^-1, e_3e_4e_4^-1e_2^-1, - e_3e_4e_4^-1e_3^-1, e_4e_4^-1e_3^-1e_1^-1, e_1e_3e_4e_4^-1e_2^-1, - e_1e_3e_4e_4^-1e_3^-1, e_2e_4e_4^-1e_3^-1e_1^-1, - e_3e_4e_4^-1e_3^-1e_1^-1, e_1e_3e_4e_4^-1e_3^-1e_1^-1 ], - [ v_2, e_3e_3^-1 ], [ e_1^-1, e_3e_3^-1e_1^-1 ], +[ [ 0, v_4, e_4^-1, e_4^-1e_2^-1, e_4^-1e_3^-1, e_4^-1e_3^-1e_1^-1, e_4, + e_4e_4^-1, e_4e_4^-1e_2^-1, e_4e_4^-1e_3^-1, e_4e_4^-1e_3^-1e_1^-1, + e_2e_4, e_2e_4e_4^-1, e_2e_4e_4^-1e_2^-1, e_2e_4e_4^-1e_3^-1, + e_2e_4e_4^-1e_3^-1e_1^-1, e_3e_4, e_3e_4e_4^-1, e_3e_4e_4^-1e_2^-1, + e_3e_4e_4^-1e_3^-1, e_3e_4e_4^-1e_3^-1e_1^-1, e_1e_3e_4, + e_1e_3e_4e_4^-1, e_1e_3e_4e_4^-1e_2^-1, e_1e_3e_4e_4^-1e_3^-1, + e_1e_3e_4e_4^-1e_3^-1e_1^-1 ], [ v_2, e_3e_3^-1 ], + [ e_1^-1, e_3e_3^-1e_1^-1 ], [ e_1, e_1e_3e_3^-1 ], [ e_1e_1^-1, e_1e_3e_3^-1e_1^-1 ] ] gap> D := ChainDigraph(4); @@ -287,6 +286,10 @@ gap> IsSuperrelation(cong1, cong2); false gap> IsSuperrelation(cong2, cong1); true +gap> IsSubrelation(TraceOfCongruenceByWangPair(cong1), TraceOfCongruenceByWangPair(cong2)) = IsSubrelation(cong1, cong2); +true +gap> IsSuperrelation(TraceOfCongruenceByWangPair(cong1), TraceOfCongruenceByWangPair(cong2)) = IsSuperrelation(cong1, cong2); +true gap> cong1 := CongruenceByWangPair(S, [2, 3, 4], []); gap> cong2 := CongruenceByWangPair(S, [4], [1]); @@ -416,6 +419,25 @@ gap> val := true;; > od; > val; true +gap> c := C[6]; + +gap> EquivalenceRelationPartition(c); +[ [ 0, v_4, e_3^-1, e_3^-1e_1^-1, e_4^-1, e_4^-1e_2^-1, e_4^-1e_2^-1e_1^-1, + e_3, e_3e_3^-1, e_3e_3^-1e_1^-1, e_3e_4^-1, e_3e_4^-1e_2^-1, + e_3e_4^-1e_2^-1e_1^-1, e_1e_3, e_1e_3e_3^-1, e_1e_3e_3^-1e_1^-1, + e_1e_3e_4^-1, e_1e_3e_4^-1e_2^-1, e_1e_3e_4^-1e_2^-1e_1^-1, e_4, + e_4e_3^-1, e_4e_3^-1e_1^-1, e_4e_4^-1, e_4e_4^-1e_2^-1, + e_4e_4^-1e_2^-1e_1^-1, e_2e_4, e_2e_4e_3^-1, e_2e_4e_3^-1e_1^-1, + e_2e_4e_4^-1, e_2e_4e_4^-1e_2^-1, e_2e_4e_4^-1e_2^-1e_1^-1, e_1e_2e_4, + e_1e_2e_4e_3^-1, e_1e_2e_4e_3^-1e_1^-1, e_1e_2e_4e_4^-1, + e_1e_2e_4e_4^-1e_2^-1, e_1e_2e_4e_4^-1e_2^-1e_1^-1 ], + [ v_1, e_1e_1^-1, e_1e_2e_2^-1e_1^-1 ], [ v_2, e_2e_2^-1 ], + [ e_1^-1, e_2e_2^-1e_1^-1 ], [ e_1, e_1e_2e_2^-1 ] ] +gap> EquivalenceRelationPartition(TraceOfCongruenceByWangPair(c)); +[ [ 0, v_4, e_3e_3^-1, e_1e_3e_3^-1e_1^-1, e_4e_4^-1, e_2e_4e_4^-1e_2^-1, + e_1e_2e_4e_4^-1e_2^-1e_1^-1 ], [ v_1, e_1e_1^-1, e_1e_2e_2^-1e_1^-1 ], + [ v_2, e_2e_2^-1 ] ] +gap> # gap> SEMIGROUPS.StopTest(); diff --git a/tst/standard/semigroups/semigraph.tst b/tst/standard/semigroups/semigraph.tst index 3f40e5004..c1ffaa321 100644 --- a/tst/standard/semigroups/semigraph.tst +++ b/tst/standard/semigroups/semigraph.tst @@ -9,7 +9,6 @@ ## #@local D, DigraphNrVertices, DigraphRange, DigraphSource, S, gr, s, x, y -#@local G, H gap> START_TEST("Semigroups package: standard/semigroups/semigraph.tst"); gap> LoadPackage("semigroups", false);; @@ -59,6 +58,63 @@ gap> List(x, Range); [ v_4, v_4, v_2, v_1, v_2, v_1, v_1, v_2, v_1, v_3, v_4, v_2, v_1, v_5, v_4, v_2, v_1, v_3, v_4, v_2, v_1, v_5, v_4, v_2, v_1, v_3, v_4, v_2, v_1, v_5, v_4, v_2, v_1, v_1, v_2, v_3, v_4, v_5 ] +gap> List(x, PositivePath); +[ v_5, v_3, v_5, v_5, v_3, v_3, v_2, e_1, e_1, e_1e_2, e_1e_2, e_1e_2, + e_1e_2, e_1e_3, e_1e_3, e_1e_3, e_1e_3, e_2, e_2, e_2, e_2, e_3, e_3, e_3, + e_3, e_4, e_4, e_4, e_4, e_5, e_5, e_5, e_5, v_1, v_2, v_3, v_4, v_5 ] +gap> List(x, NegativePath); +[ e_5^-1, e_4^-1, e_3^-1, e_3^-1e_1^-1, e_2^-1, e_2^-1e_1^-1, e_1^-1, v_2, + e_1^-1, v_3, e_4^-1, e_2^-1, e_2^-1e_1^-1, v_5, e_5^-1, e_3^-1, + e_3^-1e_1^-1, v_3, e_4^-1, e_2^-1, e_2^-1e_1^-1, v_5, e_5^-1, e_3^-1, + e_3^-1e_1^-1, v_3, e_4^-1, e_2^-1, e_2^-1e_1^-1, v_5, e_5^-1, e_3^-1, + e_3^-1e_1^-1, v_1, v_2, v_3, v_4, v_5 ] +gap> List(x, EdgesWithSource); +[ [ e_4, e_5 ], [ e_4, e_5 ], [ e_2, e_3 ], [ e_1 ], [ e_2, e_3 ], [ e_1 ], + [ e_1 ], [ e_2, e_3 ], [ e_1 ], [ ], [ e_4, e_5 ], [ e_2, e_3 ], [ e_1 ], + [ ], [ e_4, e_5 ], [ e_2, e_3 ], [ e_1 ], [ ], [ e_4, e_5 ], + [ e_2, e_3 ], [ e_1 ], [ ], [ e_4, e_5 ], [ e_2, e_3 ], [ e_1 ], [ ], + [ e_4, e_5 ], [ e_2, e_3 ], [ e_1 ], [ ], [ e_4, e_5 ], [ e_2, e_3 ], + [ e_1 ], [ e_1 ], [ e_2, e_3 ], [ ], [ e_4, e_5 ], [ ] ] +gap> List(x, EdgesWithRange); +[ [ e_3, e_5 ], [ e_2, e_4 ], [ e_3, e_5 ], [ e_3, e_5 ], [ e_2, e_4 ], + [ e_2, e_4 ], [ e_1 ], [ ], [ ], [ ], [ ], [ ], [ ], [ ], [ ], + [ ], [ ], [ e_1 ], [ e_1 ], [ e_1 ], [ e_1 ], [ e_1 ], [ e_1 ], [ e_1 ], + [ e_1 ], [ ], [ ], [ ], [ ], [ ], [ ], [ ], [ ], [ ], [ e_1 ], + [ e_2, e_4 ], [ ], [ e_3, e_5 ] ] +gap> List(x, PathsWithSource); +[ [ e_5^-1, 0, v_5 ], [ e_4^-1, v_3, 0 ], [ e_3^-1, 0, v_5 ], + [ e_3^-1e_1^-1, e_3^-1, 0, v_5 ], [ e_2^-1, v_3, 0 ], + [ e_2^-1e_1^-1, e_2^-1, v_3, 0 ], [ e_1^-1, v_2, e_2, e_3 ], + [ e_1, e_1e_2, e_1e_3 ], [ e_1e_1^-1, e_1, e_1e_2, e_1e_3 ], [ e_1e_2 ], + [ e_1e_2e_4^-1, e_1e_2, 0 ], [ e_1e_2e_2^-1, e_1e_2, 0 ], + [ e_1e_2e_2^-1e_1^-1, e_1e_2e_2^-1, e_1e_2, 0 ], [ e_1e_3 ], + [ e_1e_3e_5^-1, 0, e_1e_3 ], [ e_1e_3e_3^-1, 0, e_1e_3 ], + [ e_1e_3e_3^-1e_1^-1, e_1e_3e_3^-1, 0, e_1e_3 ], [ e_2 ], + [ e_2e_4^-1, e_2, 0 ], [ e_2e_2^-1, e_2, 0 ], + [ e_2e_2^-1e_1^-1, e_2e_2^-1, e_2, 0 ], [ e_3 ], [ e_3e_5^-1, 0, e_3 ], + [ e_3e_3^-1, 0, e_3 ], [ e_3e_3^-1e_1^-1, e_3e_3^-1, 0, e_3 ], [ e_4 ], + [ e_4e_4^-1, e_4, 0 ], [ e_4e_2^-1, e_4, 0 ], + [ e_4e_2^-1e_1^-1, e_4e_2^-1, e_4, 0 ], [ e_5 ], [ e_5e_5^-1, 0, e_5 ], + [ e_5e_3^-1, 0, e_5 ], [ e_5e_3^-1e_1^-1, e_5e_3^-1, 0, e_5 ], + [ v_1, e_1, e_1e_2, e_1e_3 ], [ v_2, e_2, e_3 ], [ v_3 ], [ v_4, e_4, e_5 ], + [ v_5 ] ] +gap> List(x, PathsWithRange); +[ [ e_5^-1, e_3e_5^-1, e_1e_3e_5^-1, e_5e_5^-1 ], + [ e_4^-1, e_2e_4^-1, e_1e_2e_4^-1, e_4e_4^-1 ], + [ e_3^-1, e_3e_3^-1, e_1e_3e_3^-1, e_5e_3^-1 ], + [ e_3^-1e_1^-1, e_3e_3^-1e_1^-1, e_1e_3e_3^-1e_1^-1, e_5e_3^-1e_1^-1 ], + [ e_2^-1, e_2e_2^-1, e_1e_2e_2^-1, e_4e_2^-1 ], + [ e_2^-1e_1^-1, e_2e_2^-1e_1^-1, e_1e_2e_2^-1e_1^-1, e_4e_2^-1e_1^-1 ], + [ e_1^-1, e_1e_1^-1 ], [ e_1 ], [ e_1e_1^-1 ], [ e_1e_2 ], [ e_1e_2e_4^-1 ], + [ e_1e_2e_2^-1 ], [ e_1e_2e_2^-1e_1^-1 ], [ e_1e_3 ], [ e_1e_3e_5^-1 ], + [ e_1e_3e_3^-1 ], [ e_1e_3e_3^-1e_1^-1 ], [ e_2, e_1e_2 ], + [ e_2e_4^-1, e_1e_2e_4^-1 ], [ e_2e_2^-1, e_1e_2e_2^-1 ], + [ e_2e_2^-1e_1^-1, e_1e_2e_2^-1e_1^-1 ], [ e_3, e_1e_3 ], + [ e_3e_5^-1, e_1e_3e_5^-1 ], [ e_3e_3^-1, e_1e_3e_3^-1 ], + [ e_3e_3^-1e_1^-1, e_1e_3e_3^-1e_1^-1 ], [ e_4 ], [ e_4e_4^-1 ], + [ e_4e_2^-1 ], [ e_4e_2^-1e_1^-1 ], [ e_5 ], [ e_5e_5^-1 ], [ e_5e_3^-1 ], + [ e_5e_3^-1e_1^-1 ], [ v_1 ], [ v_2, e_1 ], [ v_3, e_2, e_1e_2, e_4 ], + [ v_4 ], [ v_5, e_3, e_1e_3, e_5 ] ] gap> AssignGeneratorVariables(S); gap> String(gr); "DigraphFromDigraph6String(\"&DOS@O?\")" @@ -129,25 +185,6 @@ gap> S := GraphInverseSemigroup(D); gap> VerticesOfGraphInverseSemigroup(S); [ v_1, v_2, v_3, v_4, v_5, v_6, v_7, v_8, v_9, v_10, v_11, v_12 ] -# Issue 1177 - check FamilyObj for graph inverse semigroup element binary ops -gap> D := Digraph([[4, 5], [1, 3, 4, 5], [1, 4, 5], [], [4]]); - -gap> G := GraphInverseSemigroup(D); - -gap> H := GraphInverseSemigroup(D); - -gap> G = H; -false -gap> G.1 in H; -false -gap> G.1 < H.1; -Error, no method found! For debugging hints type ?Recovery from NoMethodFound -Error, no 1st choice method found for `<' on 2 arguments -gap> G.1 < G.1; -false -gap> G.1 = H.1; -false - # gap> SEMIGROUPS.StopTest(); gap> STOP_TEST("Semigroups package: standard/semigroups/semigraph.tst"); From 5f36c301dd9af711f78f956ff2fcdfccce19ba03 Mon Sep 17 00:00:00 2001 From: Joseph Ward <88321542+joe-dw@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:00:03 +0100 Subject: [PATCH 02/11] Update semigraph.tst --- tst/standard/semigroups/semigraph.tst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tst/standard/semigroups/semigraph.tst b/tst/standard/semigroups/semigraph.tst index c1ffaa321..d7a57ffc9 100644 --- a/tst/standard/semigroups/semigraph.tst +++ b/tst/standard/semigroups/semigraph.tst @@ -185,6 +185,25 @@ gap> S := GraphInverseSemigroup(D); gap> VerticesOfGraphInverseSemigroup(S); [ v_1, v_2, v_3, v_4, v_5, v_6, v_7, v_8, v_9, v_10, v_11, v_12 ] +# Issue 1177 - check FamilyObj for graph inverse semigroup element binary ops +gap> D := Digraph([[4, 5], [1, 3, 4, 5], [1, 4, 5], [], [4]]); + +gap> G := GraphInverseSemigroup(D); + +gap> H := GraphInverseSemigroup(D); + +gap> G = H; +false +gap> G.1 in H; +false +gap> G.1 < H.1; +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `<' on 2 arguments +gap> G.1 < G.1; +false +gap> G.1 = H.1; +false + # gap> SEMIGROUPS.StopTest(); gap> STOP_TEST("Semigroups package: standard/semigroups/semigraph.tst"); From 6faaee1199cdb48446424d6b06592b360acbff1b Mon Sep 17 00:00:00 2001 From: Joseph Ward <88321542+joe-dw@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:06:36 +0100 Subject: [PATCH 03/11] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 323f2c313..43cd906ea 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + + + # README - Semigroups package for GAP #### Copyright (C) 2011-2025 James D. Mitchell et al.
Licensing information is available in the LICENSE file. From 859140ab993b7fdfe73cd08c1693c427431f8ba8 Mon Sep 17 00:00:00 2001 From: Joseph Ward <88321542+joe-dw@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:14:37 +0100 Subject: [PATCH 04/11] Update semigraph.tst --- tst/standard/semigroups/semigraph.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/standard/semigroups/semigraph.tst b/tst/standard/semigroups/semigraph.tst index d7a57ffc9..0a18a04c7 100644 --- a/tst/standard/semigroups/semigraph.tst +++ b/tst/standard/semigroups/semigraph.tst @@ -8,7 +8,7 @@ ############################################################################# ## -#@local D, DigraphNrVertices, DigraphRange, DigraphSource, S, gr, s, x, y +#@local D, DigraphNrVertices, DigraphRange, DigraphSource, S, gr, s, x, y, G, H gap> START_TEST("Semigroups package: standard/semigroups/semigraph.tst"); gap> LoadPackage("semigroups", false);; From 001a382da25c420da94baee509ec2d6841ae7095 Mon Sep 17 00:00:00 2001 From: Joseph Ward <88321542+joe-dw@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:16:28 +0100 Subject: [PATCH 05/11] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 43cd906ea..323f2c313 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ - - - # README - Semigroups package for GAP #### Copyright (C) 2011-2025 James D. Mitchell et al.
Licensing information is available in the LICENSE file. From 59d7fd1a333a6bbacd7eeeb4c6c39fd4d17e80e3 Mon Sep 17 00:00:00 2001 From: joe-dw Date: Tue, 28 Jul 2026 10:37:42 +0100 Subject: [PATCH 06/11] Fixed error with incorrect semigroup name --- doc/semigraph.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/semigraph.xml b/doc/semigraph.xml index cd91baf46..24283a730 100644 --- a/doc/semigraph.xml +++ b/doc/semigraph.xml @@ -329,9 +329,6 @@ gap> D := Digraph([[2, 3], [3, 4], [4], []]); gap> S := GraphInverseSemigroup(D); -gap> EdgesWithRange(VerticesOfGraphInverseSemigroup(G)[4]); -Error, Variable: 'G' must have a value -not in any function at *stdin*:3 gap> EdgesWithRange(VerticesOfGraphInverseSemigroup(S)[4]); [ e_4, e_5 ] gap> PathsWithRange(VerticesOfGraphInverseSemigroup(S)[4]); From c58971ee900dfcdf653983a1415c97015f5bb79d Mon Sep 17 00:00:00 2001 From: joe-dw Date: Tue, 28 Jul 2026 12:35:49 +0100 Subject: [PATCH 07/11] lint --- gap/main/orbits-pairs.gi | 208 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 gap/main/orbits-pairs.gi diff --git a/gap/main/orbits-pairs.gi b/gap/main/orbits-pairs.gi new file mode 100644 index 000000000..b9464a82e --- /dev/null +++ b/gap/main/orbits-pairs.gi @@ -0,0 +1,208 @@ +PartitionRightCosets := function(S, SS, G) + local RC, uf, i, C, c; + RC := RightCosets(S, G); + uf := PartitionDS(IsPartitionDS, Size(S)); + for C in RC do + i := Position(SS, Representative(C)); + for c in C do + Unite(uf, i, Position(SS, c)); + od; + od; + return uf; +end; + +PartitionLeftCosets := function(S, SS, G) + local LC, uf, i, C, c; + LC := LeftCosets(S, G); + uf := PartitionDS(IsPartitionDS, Size(S)); + for C in LC do + i := Position(SS, Representative(C)); + for c in C do + Unite(uf, i, Position(SS, c)); + od; + od; + return uf; +end; + +PartitionInverses := function(G) + local elts, parts, g, h; + elts := Elements(G); + parts := []; + for g in elts do + h := g ^ -1; + if g = h then + Add(parts, [g]); + else + Add(parts, [g, h]); + fi; + od; + return parts; +end; + +# RootsOfPartitionDS := function(uf) +# local pt, gp, data, n, result; +# +# pt := 0; +# gp := UF.getParent; +# data := uf!.data; +# n := SizeUnderlyingSetDS(uf); +# result := []; +# while pt <= n do +# pt := pt + 1; +# while pt <= n and gp(data[pt]) <> pt do +# pt := pt + 1; +# od; +# if pt <= n then +# Add(result, pt); +# fi; +# od; +# return result; +# end; +# +UfJoin := function(uf1parts, uf2) + local join, rep, part, x; + join := ShallowCopy(uf2); + for part in uf1parts do + rep := Representative(uf2, part[1]); + for x in part do + Unite(join, rep, x); + od; + od; + return join; +end; + +UfFromPartition := function(domain, partition) + local uf, part, k, p; + uf := PartitionDS(IsPartitionDS, Size(domain)); + for part in partition do + k := Position(domain, part[1]); + for p in part do + Unite(uf, k, Position(domain, p)); + od; + od; + return uf; +end; + +OrbitsOfPairs := function(G, S, act) + local T, tLength, A, GS, J, D, R, L, invPartition, invPartUF, invParts, i, + lParts, j, pairs, g, x, y; + T := List(Orbits(G, S, act), l -> l[1]); + tLength := Length(T); + A := List(T, t -> Stabiliser(G, t, act)); + GS := AsSet(G); + J := []; + D := []; + R := []; + L := []; + invPartition := PartitionInverses(G); + invPartUF := UfFromPartition(GS, invPartition); + invParts := PartsOfPartitionDS(invPartUF); + for i in [1 .. tLength] do + Add(R, PartitionRightCosets(G, GS, A[i])); + Add(L, PartitionLeftCosets(G, GS, A[i])); + Print(Concatenation(["\rPartitioned Cosets ", String(i), " of ", + String(tLength)])); + od; + Print("\033[2K\rPartitioned All Cosets\n"); + lParts := List(L, PartsOfPartitionDS); + + for i in [1 .. tLength] do + Add(J, []); + Add(D, []); + for j in [1 .. i - 1] do + Add(J[i], UfJoin(lParts[j], R[i])); + Add(D[i], List(RootsOfPartitionDS(J[i][j]), k -> GS[k])); + od; + Add(J[i], UfJoin(lParts[i], (UfJoin(invParts, R[i])))); + Add(D[i], List(RootsOfPartitionDS(J[i][i]), k -> GS[k])); + Print(Concatenation(["\rFound Join for Element ", String(i), " of ", + String(tLength)])); + od; + Print("\033[2K\rFound All Joins\n"); + pairs := []; + + for i in [tLength, tLength - 1 .. 1] do + for j in [i - 1, i - 2 .. 1] do + for g in D[i][j] do + x := act(T[i], g); + y := T[j]; + Add(pairs, [x, y]); + od; + od; + for g in D[i][i] do + y := T[i]; + x := act(y, g); + if x <> y then + Add(pairs, [x, y]); + fi; + od; + Print(Concatenation(["\rFound Pairs for Element ", String(tLength - i + 1), + " of ", String(tLength)])); + od; + Print("\033[2K\rFound All Pairs\n"); + return pairs; +end; + +OrbitsOfPairsWithCount := function(G, S, act) + local T, tLength, A, GS, J, D, R, L, invPartition, invPartUF, invParts, i, + lParts, j, pairs, g, x, y; + T := List(Orbits(G, S, act), l -> l[1]); + tLength := Length(T); + A := List(T, t -> Stabiliser(G, t, act)); + GS := AsSet(G); + J := []; + D := []; + R := []; + L := []; + invPartition := PartitionInverses(G); + invPartUF := UfFromPartition(GS, invPartition); + invParts := PartsOfPartitionDS(invPartUF); + for i in [1 .. tLength] do + Add(R, PartitionRightCosets(G, GS, A[i])); + Add(L, PartitionLeftCosets(G, GS, A[i])); + Print(Concatenation(["\rPartitioned Cosets ", String(i), " of ", + String(tLength)])); + od; + Print("\033[2K\rPartitioned All Cosets\n"); + lParts := List(L, PartsOfPartitionDS); + + for i in [1 .. tLength] do + Add(J, []); + Add(D, []); + for j in [1 .. i - 1] do + Add(J[i], UfJoin(lParts[j], R[i])); + Add(D[i], List(RootsOfPartitionDS(J[i][j]), k -> GS[k])); + od; + Add(J[i], UfJoin(lParts[i], (UfJoin(invParts, R[i])))); + Add(D[i], List(RootsOfPartitionDS(J[i][i]), k -> GS[k])); + Print(Concatenation(["\rFound Join for Element ", String(i), " of ", + String(tLength)])); + od; + Print("\033[2K\rFound All Joins\n"); + pairs := []; + + for i in [tLength, tLength - 1 .. 1] do + for j in [i - 1, i - 2 .. 1] do + for g in D[i][j] do + x := act(T[i], g); + y := T[j]; + Add(pairs, [[x, y], Size(Intersection(A[i] ^ g, A[j]))]); + od; + od; + for g in D[i][i] do + y := T[i]; + x := act(y, g); + if x <> y then + Add(pairs, [[x, y], Size]); + fi; + od; + Print(Concatenation(["\rFound Pairs for Element ", String(tLength - i + 1), + " of ", String(tLength)])); + od; + Print("\033[2K\rFound All Pairs\n"); + return pairs; +end; + +# OrbitsOfPairsIterator := undefined; +# +# OrbitsOfPairsWithCountIterator := undefined; From a69fe3f8c9b6cc0577c23abdef4257caf40d6163 Mon Sep 17 00:00:00 2001 From: joe-dw Date: Tue, 28 Jul 2026 13:50:27 +0100 Subject: [PATCH 08/11] lint and todos --- gap/congruences/congsemigraph.gi | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/gap/congruences/congsemigraph.gi b/gap/congruences/congsemigraph.gi index 259e1dd22..db65d0ba2 100644 --- a/gap/congruences/congsemigraph.gi +++ b/gap/congruences/congsemigraph.gi @@ -330,7 +330,9 @@ InstallMethod(IsSuperrelation, InstallMethod(ViewObj, "for trace of a congruence by Wang pair", [IsTraceOfCongruenceByWangPair], -tr -> Print(ViewString(tr))); +function(tr) + Print(ViewString(tr)); +end); InstallMethod(ViewString, "for a congruence by Wang pair", [IsTraceOfCongruenceByWangPair], @@ -398,7 +400,6 @@ function(tr, elm1, elm2) # so the pair is related end); -# TODO add family check to install method below InstallMethod(EdgesWithRange, "for a graph inverse semigroup element", [IsGraphInverseSemigroupElement], @@ -438,9 +439,7 @@ InstallMethod(EdgesWithSource, function(x) local G; G := FamilyObj(x)!.semigroup; - if not x in G then - Error("TODO"); - elif not IsVertex(x) then + if not IsVertex(x) then return EdgesWithSource(Range(x)); else return Filtered(EdgesOfGraphInverseSemigroup(G), e -> Source(e) = x); @@ -451,17 +450,12 @@ InstallMethod(PathsWithSource, "for a graph inverse semigroup element", [IsGraphInverseSemigroupElement], function(x) - local outPaths, e, outPathsPerEdge, G; - G := FamilyObj(x)!.semigroup; - if not x in G then - Error("TODO"); - else - outPaths := [[x]]; - for e in EdgesWithSource(Range(x)) do - outPathsPerEdge := List(PathsWithSource(Range(e)), p -> x * e * p); - Add(outPaths, outPathsPerEdge); - od; - fi; + local outPaths, e, outPathsPerEdge; + outPaths := [[x]]; + for e in EdgesWithSource(Range(x)) do + outPathsPerEdge := List(PathsWithSource(Range(e)), p -> x * e * p); + Add(outPaths, outPathsPerEdge); + od; return Concatenation(outPaths); end); @@ -537,7 +531,6 @@ function(tr) return classes; end); - # InstallMethod(EquivalenceRelationPartition, "for a congruence by Wang Pair", [IsCongruenceByWangPair], @@ -658,4 +651,5 @@ end); InstallMethod(TraceOfSemigroupCongruence, "for a congruence by Wang Pair", [IsCongruenceByWangPair], -138, TraceOfCongruenceByWangPair); +138, # prioritises over regular function for inverse semigroups +TraceOfCongruenceByWangPair); From bb7f0c6b09c2a35e5ec9312a7b5bf3dcaaea5071 Mon Sep 17 00:00:00 2001 From: joe-dw Date: Tue, 28 Jul 2026 13:54:52 +0100 Subject: [PATCH 09/11] lint --- gap/congruences/congsemigraph.gi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gap/congruences/congsemigraph.gi b/gap/congruences/congsemigraph.gi index db65d0ba2..a1a83c0e9 100644 --- a/gap/congruences/congsemigraph.gi +++ b/gap/congruences/congsemigraph.gi @@ -595,7 +595,8 @@ InstallMethod(ImagesElm, function(cong, x) local p, q; if not x in Source(cong) then - Error("TODO"); + Error( + "Element must be in the semigroup that the congruence is defined on!"); elif IsMultiplicativeZero(Source(cong), x) or ( IndexOfVertexOfGraphInverseSemigroup(Range(PositivePath(x))) in cong!.H) then From a41a419ca40b8ae0748409b6ab7b1c79b2b02edb Mon Sep 17 00:00:00 2001 From: Joseph Ward <88321542+joe-dw@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:01:48 +0100 Subject: [PATCH 10/11] Delete gap/main/orbits-pairs.gi --- gap/main/orbits-pairs.gi | 208 --------------------------------------- 1 file changed, 208 deletions(-) delete mode 100644 gap/main/orbits-pairs.gi diff --git a/gap/main/orbits-pairs.gi b/gap/main/orbits-pairs.gi deleted file mode 100644 index b9464a82e..000000000 --- a/gap/main/orbits-pairs.gi +++ /dev/null @@ -1,208 +0,0 @@ -PartitionRightCosets := function(S, SS, G) - local RC, uf, i, C, c; - RC := RightCosets(S, G); - uf := PartitionDS(IsPartitionDS, Size(S)); - for C in RC do - i := Position(SS, Representative(C)); - for c in C do - Unite(uf, i, Position(SS, c)); - od; - od; - return uf; -end; - -PartitionLeftCosets := function(S, SS, G) - local LC, uf, i, C, c; - LC := LeftCosets(S, G); - uf := PartitionDS(IsPartitionDS, Size(S)); - for C in LC do - i := Position(SS, Representative(C)); - for c in C do - Unite(uf, i, Position(SS, c)); - od; - od; - return uf; -end; - -PartitionInverses := function(G) - local elts, parts, g, h; - elts := Elements(G); - parts := []; - for g in elts do - h := g ^ -1; - if g = h then - Add(parts, [g]); - else - Add(parts, [g, h]); - fi; - od; - return parts; -end; - -# RootsOfPartitionDS := function(uf) -# local pt, gp, data, n, result; -# -# pt := 0; -# gp := UF.getParent; -# data := uf!.data; -# n := SizeUnderlyingSetDS(uf); -# result := []; -# while pt <= n do -# pt := pt + 1; -# while pt <= n and gp(data[pt]) <> pt do -# pt := pt + 1; -# od; -# if pt <= n then -# Add(result, pt); -# fi; -# od; -# return result; -# end; -# -UfJoin := function(uf1parts, uf2) - local join, rep, part, x; - join := ShallowCopy(uf2); - for part in uf1parts do - rep := Representative(uf2, part[1]); - for x in part do - Unite(join, rep, x); - od; - od; - return join; -end; - -UfFromPartition := function(domain, partition) - local uf, part, k, p; - uf := PartitionDS(IsPartitionDS, Size(domain)); - for part in partition do - k := Position(domain, part[1]); - for p in part do - Unite(uf, k, Position(domain, p)); - od; - od; - return uf; -end; - -OrbitsOfPairs := function(G, S, act) - local T, tLength, A, GS, J, D, R, L, invPartition, invPartUF, invParts, i, - lParts, j, pairs, g, x, y; - T := List(Orbits(G, S, act), l -> l[1]); - tLength := Length(T); - A := List(T, t -> Stabiliser(G, t, act)); - GS := AsSet(G); - J := []; - D := []; - R := []; - L := []; - invPartition := PartitionInverses(G); - invPartUF := UfFromPartition(GS, invPartition); - invParts := PartsOfPartitionDS(invPartUF); - for i in [1 .. tLength] do - Add(R, PartitionRightCosets(G, GS, A[i])); - Add(L, PartitionLeftCosets(G, GS, A[i])); - Print(Concatenation(["\rPartitioned Cosets ", String(i), " of ", - String(tLength)])); - od; - Print("\033[2K\rPartitioned All Cosets\n"); - lParts := List(L, PartsOfPartitionDS); - - for i in [1 .. tLength] do - Add(J, []); - Add(D, []); - for j in [1 .. i - 1] do - Add(J[i], UfJoin(lParts[j], R[i])); - Add(D[i], List(RootsOfPartitionDS(J[i][j]), k -> GS[k])); - od; - Add(J[i], UfJoin(lParts[i], (UfJoin(invParts, R[i])))); - Add(D[i], List(RootsOfPartitionDS(J[i][i]), k -> GS[k])); - Print(Concatenation(["\rFound Join for Element ", String(i), " of ", - String(tLength)])); - od; - Print("\033[2K\rFound All Joins\n"); - pairs := []; - - for i in [tLength, tLength - 1 .. 1] do - for j in [i - 1, i - 2 .. 1] do - for g in D[i][j] do - x := act(T[i], g); - y := T[j]; - Add(pairs, [x, y]); - od; - od; - for g in D[i][i] do - y := T[i]; - x := act(y, g); - if x <> y then - Add(pairs, [x, y]); - fi; - od; - Print(Concatenation(["\rFound Pairs for Element ", String(tLength - i + 1), - " of ", String(tLength)])); - od; - Print("\033[2K\rFound All Pairs\n"); - return pairs; -end; - -OrbitsOfPairsWithCount := function(G, S, act) - local T, tLength, A, GS, J, D, R, L, invPartition, invPartUF, invParts, i, - lParts, j, pairs, g, x, y; - T := List(Orbits(G, S, act), l -> l[1]); - tLength := Length(T); - A := List(T, t -> Stabiliser(G, t, act)); - GS := AsSet(G); - J := []; - D := []; - R := []; - L := []; - invPartition := PartitionInverses(G); - invPartUF := UfFromPartition(GS, invPartition); - invParts := PartsOfPartitionDS(invPartUF); - for i in [1 .. tLength] do - Add(R, PartitionRightCosets(G, GS, A[i])); - Add(L, PartitionLeftCosets(G, GS, A[i])); - Print(Concatenation(["\rPartitioned Cosets ", String(i), " of ", - String(tLength)])); - od; - Print("\033[2K\rPartitioned All Cosets\n"); - lParts := List(L, PartsOfPartitionDS); - - for i in [1 .. tLength] do - Add(J, []); - Add(D, []); - for j in [1 .. i - 1] do - Add(J[i], UfJoin(lParts[j], R[i])); - Add(D[i], List(RootsOfPartitionDS(J[i][j]), k -> GS[k])); - od; - Add(J[i], UfJoin(lParts[i], (UfJoin(invParts, R[i])))); - Add(D[i], List(RootsOfPartitionDS(J[i][i]), k -> GS[k])); - Print(Concatenation(["\rFound Join for Element ", String(i), " of ", - String(tLength)])); - od; - Print("\033[2K\rFound All Joins\n"); - pairs := []; - - for i in [tLength, tLength - 1 .. 1] do - for j in [i - 1, i - 2 .. 1] do - for g in D[i][j] do - x := act(T[i], g); - y := T[j]; - Add(pairs, [[x, y], Size(Intersection(A[i] ^ g, A[j]))]); - od; - od; - for g in D[i][i] do - y := T[i]; - x := act(y, g); - if x <> y then - Add(pairs, [[x, y], Size]); - fi; - od; - Print(Concatenation(["\rFound Pairs for Element ", String(tLength - i + 1), - " of ", String(tLength)])); - od; - Print("\033[2K\rFound All Pairs\n"); - return pairs; -end; - -# OrbitsOfPairsIterator := undefined; -# -# OrbitsOfPairsWithCountIterator := undefined; From af6b2ea07b380b0592756df2e627dd4fd9bae709 Mon Sep 17 00:00:00 2001 From: joe-dw Date: Wed, 29 Jul 2026 12:40:56 +0100 Subject: [PATCH 11/11] Removed undefined declaration of MinimalHereditarySubsetsVertex --- gap/congruences/congsemigraph.gd | 3 --- 1 file changed, 3 deletions(-) diff --git a/gap/congruences/congsemigraph.gd b/gap/congruences/congsemigraph.gd index a91fff829..87239ba7b 100644 --- a/gap/congruences/congsemigraph.gd +++ b/gap/congruences/congsemigraph.gd @@ -22,9 +22,6 @@ DeclareOperation("CongruenceByWangPair", DeclareOperation("AsCongruenceByWangPair", [IsSemigroupCongruence]); -DeclareOperation("MinimalHereditarySubsetsVertex", - [IsDigraph, IsPosInt]); - DeclareAttribute("GeneratingCongruencesOfLattice", IsGraphInverseSemigroup); DeclareCategory("IsTraceOfCongruenceByWangPair",