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..0a18a04c7 100644
--- a/tst/standard/semigroups/semigraph.tst
+++ b/tst/standard/semigroups/semigraph.tst
@@ -8,8 +8,7 @@
#############################################################################
##
-#@local D, DigraphNrVertices, DigraphRange, DigraphSource, S, gr, s, x, y
-#@local G, H
+#@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);;
@@ -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?\")"