Skip to content

Commit 3e32d9d

Browse files
Use graphviz v1
1 parent fddd7f9 commit 3e32d9d

File tree

2 files changed

+107
-51
lines changed

2 files changed

+107
-51
lines changed

PackageInfo.g

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ Dependencies := rec(
379379
NeededOtherPackages := [["datastructures", ">=0.2.5"],
380380
["digraphs", ">=1.6.2"],
381381
["genss", ">=1.6.5"],
382+
["graphviz", ">=0.0.0"],
382383
["images", ">=1.3.1"],
383384
["IO", ">=4.5.1"],
384385
["orb", ">=4.8.2"]],

gap/tools/display.gi

Lines changed: 106 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,8 @@ function(D)
731731
local S, edge_colors, N, msg, legend, node_labels, offset, en, label, next,
732732
edge_func, node_func, result, i;
733733

734+
# TODO replace with an appropriate call to DotWordGraph
735+
734736
S := SemigroupOfCayleyDigraph(D);
735737
edge_colors := ["\"#00ff00\"", "\"#ff00ff\"", "\"#007fff\"", "\"#ff7f00\"",
736738
"\"#7fbf7f\"", "\"#4604ac\"", "\"#de0328\"", "\"#19801d\"",
@@ -755,6 +757,8 @@ function(D)
755757
if IsOne(en[i]) then
756758
label := "ε";
757759
else
760+
# TODO maybe reuse some of this it seems to give better factorisations,
761+
# than the DotRightCayleyDigraph method below
758762
label := SEMIGROUPS.WordToExtRepObj(MinimalFactorization(S, i) + offset);
759763
label := SEMIGROUPS.ExtRepObjToString(label);
760764
fi;
@@ -805,88 +809,139 @@ function(D)
805809
end);
806810

807811
InstallMethod(DotLeftCayleyDigraph, "for a semigroup", [IsSemigroup],
808-
S -> DotString(LeftCayleyDigraph(S)));
812+
function(S)
813+
local label;
814+
label := x -> SEMIGROUPS.WordToString(MinimalFactorization(S, x));
815+
return DotWordGraph(LeftCayleyDigraph(S),
816+
List(S, label),
817+
List(GeneratorsOfSemigroup(S), label));
818+
end);
809819

810820
InstallMethod(DotRightCayleyDigraph, "for a semigroup", [IsSemigroup],
811-
S -> DotString(RightCayleyDigraph(S)));
821+
function(S)
822+
local label;
823+
824+
# TODO handle monoids
825+
# ToExtRepObj := SEMIGROUPS.WordToExtRepObj;
826+
# ToString := SEMIGROUPS.ExtRepObjToString;
827+
# label := x -> ToString(ToExtRepObj(MinimalFactorization(S, x)));
828+
# Current the above approach doesn't work becase labels aren't put into
829+
# quotes " by default and so an error is given for this at present.
830+
# TODO delete the next line when graphviz is fixed.
831+
label := x -> SEMIGROUPS.WordToString(MinimalFactorization(S, x));
832+
return DotWordGraph(RightCayleyDigraph(S),
833+
List(S, label),
834+
List(GeneratorsOfSemigroup(S), label));
835+
end);
812836

813-
InstallMethod(DotWordGraph, "for a word graph and list of edge labels",
814-
[IsDigraph, IsList, IsList],
837+
InstallMethod(DotWordGraph,
838+
"for a word graph, list of node labels, and list of edge labels",
839+
[IsDigraph, IsHomogeneousList, IsHomogeneousList],
815840
function(D, node_labels, edge_labels)
816-
local M, N, edge_colors, msg, legend, label, next, node_func, edge_func, result, v, i;
841+
local M, N, msg, edge_colors, dot, mm, pos, targets, e, legend, subgraph,
842+
key, key2, label, next, str, m, n, i;
843+
844+
# TODO have some options, like include the legend, maybe fancy labels
817845

818-
if IsNullDigraph(D) then
846+
if not DigraphHasAVertex(D) then
819847
# Word graphs must have at least one node . . .
820-
ErrorNoReturn("TODO3");
848+
ErrorNoReturn("The 1st argument (a digraph) must have at least one vertex");
849+
elif not IsOutRegularDigraph(D) then
850+
ErrorNoReturn("The 1st argument (a digraph) must be out-regular");
821851
fi;
822852

823853
M := DigraphNrVertices(D);
824854
N := Length(OutNeighboursOfVertex(D, 1));
825-
826-
if not IsOutRegularDigraph(D) then
827-
ErrorNoReturn("TODO1");
855+
if Length(node_labels) <> M then
856+
msg := "Expected the 2nd argument (a list) to have length ";
857+
Append(msg, "{}, but found {}");
858+
ErrorNoReturn(StringFormatted(msg), M, Length(node_labels));
859+
elif not IsString(node_labels[1]) then
860+
ErrorNoReturn("TODO");
828861
elif Length(edge_labels) <> N then
829-
ErrorNoReturn("TODO2");
862+
msg := "Expected the 3rd argument (a list) to have length ";
863+
Append(msg, "{}, but found {}");
864+
ErrorNoReturn(msg, M, Length(edge_labels));
865+
elif not IsString(edge_labels[1]) then
866+
ErrorNoReturn("TODO");
830867
fi;
831868

832-
edge_colors := ["\"#00ff00\"", "\"#ff00ff\"", "\"#007fff\"", "\"#ff7f00\"",
833-
"\"#7fbf7f\"", "\"#4604ac\"", "\"#de0328\"", "\"#19801d\"",
834-
"\"#d881f5\"", "\"#00ffff\"", "\"#ffff00\"", "\"#00ff7f\"",
835-
"\"#ad5867\"", "\"#85f610\"", "\"#84e9f5\"", "\"#f5c778\"",
836-
"\"#207090\"", "\"#764ef3\"", "\"#7b4c00\"", "\"#0000ff\"",
837-
"\"#b80c9a\"", "\"#601045\"", "\"#29b7c0\"", "\"#839f12"];
869+
# TODO longer list here
870+
edge_colors := ["#00ff00", "#ff00ff", "#007fff", "#ff7f00",
871+
"#7fbf7f", "#4604ac", "#de0328", "#19801d",
872+
"#d881f5", "#00ffff", "#ffff00", "#00ff7f",
873+
"#ad5867", "#85f610", "#84e9f5", "#f5c778",
874+
"#207090", "#764ef3", "#7b4c00", "#0000ff",
875+
"#b80c9a", "#601045", "#29b7c0", "#839f12"];
838876

839877
if N > Length(edge_colors) then
840-
msg := Concatenation("the out-degree of every vertex in the 1st argument (a digraph) ",
841-
"must have at most {}, found {}");
878+
msg := Concatenation("the out-degree of every vertex in the 1st argument ",
879+
"(a digraph) must be at most {}, found {}");
842880
ErrorNoReturn(StringFormatted(msg, Length(edge_colors), N));
843881
fi;
844882

845-
# node_labels := ["&#949;"];
883+
dot := GV_Digraph("WordGraph");
884+
GV_SetAttr(dot, "node [shape=\"box\"]");
885+
for m in [1 .. M] do
886+
mm := GV_AddNode(dot, m);
887+
GV_SetAttr(mm, "label", node_labels[m]);
888+
pos := Position(edge_labels, node_labels[m]);
889+
if pos <> fail then
890+
GV_SetAttr(mm, "color", edge_colors[pos]);
891+
GV_SetAttr(mm, "style", "filled");
892+
fi;
893+
od;
846894

847-
# for v in [2 .. M] do
848-
# Add(node_labels, edge_labels{DigraphPath(D, 1, v)[2]});
849-
# od;
895+
for m in [1 .. M] do
896+
targets := OutNeighboursOfVertex(D, m);
897+
for n in [1 .. N] do
898+
e := GV_AddEdge(dot, m, targets[n]);
899+
GV_SetAttr(e, "color", edge_colors[n]);
900+
od;
901+
od;
850902

851-
legend := "node [shape=plaintext]\nsubgraph cluster_01 {\nlabel=\"Legend\"\n";
852-
Append(legend, "key2 [label=<<table border=\"0\" cellpadding=\"2\"");
853-
Append(legend, " cellspacing=\"0\" cellborder=\"0\">\n");
903+
legend := GV_AddContext(dot, "legend");
904+
GV_SetAttr(legend, "node [shape=plaintext]");
905+
subgraph := GV_AddSubgraph(legend, "legend");
906+
key := GV_AddNode(subgraph, "key");
907+
key2 := GV_AddNode(subgraph, "key2");
854908

909+
label := Concatenation("<<table border=\"0\" cellpadding=\"2\"",
910+
" cellspacing=\"0\"",
911+
" cellborder=\"0\">\n");
855912
for i in [1 .. N] do
856-
Append(legend,
913+
Append(label,
857914
StringFormatted(" <tr><td port=\"i{}\">&nbsp;</td></tr>\n",
858915
i));
859916
od;
860-
Append(legend, "</table>>]\n");
861-
Append(legend, "key [label=<<table border=\"0\" cellpadding=\"2\"");
862-
Append(legend, " cellspacing=\"0\" cellborder=\"0\">\n");
917+
Append(label, "</table>>\n");
918+
GV_SetAttr(key2, "label", label);
863919

920+
# TODO remove code dupl
921+
label := Concatenation("<<table border=\"0\" cellpadding=\"2\"",
922+
" cellspacing=\"0\"",
923+
" cellborder=\"0\">\n");
864924
for i in [1 .. N] do
865-
label := [edge_labels[i]];
866-
next := "<tr><td align=\"right\" ";
867-
Append(next, StringFormatted("port=\"i{}\">{}&nbsp;</td></tr>\n", i, label));
868-
Append(legend, next);
925+
next := " <tr><td align=\"right\" ";
926+
Append(next, StringFormatted("port=\"i{}\">{}&nbsp;</td></tr>\n",
927+
i,
928+
edge_labels[i]));
929+
Append(label, next);
869930
od;
931+
Append(label, "</table>>\n");
870932

871-
Append(legend, "</table>>]\n\n");
933+
GV_SetAttr(key, "label", label);
872934

873935
for i in [1 .. N] do
874-
next := StringFormatted("key:i{1}:e -> key2:i{1}:w [color={2},",
875-
i,
876-
edge_colors[i]);
877-
Append(next, "constraint=false]\n");
878-
Append(legend, next);
936+
e := GV_AddEdge(subgraph,
937+
StringFormatted("key:i{}:e", i),
938+
StringFormatted("key2:i{}:w", i));
939+
GV_SetAttr(e, "color", edge_colors[i]);
940+
GV_SetAttr(e, "constraint", false);
879941
od;
880-
Append(legend, "}\n");
881-
882-
node_func := i -> StringFormatted(" [label=\"{}\"]", node_labels[i]);
883-
edge_func := {i, j} -> StringFormatted("[color={}]", edge_colors[j]);
884942

885-
result := DIGRAPHS_DotDigraph(D, [node_func], [edge_func]);
886-
result := SplitString(result, "\n");
887-
result[3] := "subgraph 00 {";
888-
Add(result, "node [shape=box]", 3);
889-
Add(result, legend);
890-
Add(result, "}");
891-
return JoinStringsWithSeparator(result, "\n");
943+
str := GV_String(dot);
944+
# TODO remove the next two lines, these work around some issues in graphviz
945+
str := ReplacedString(str, "--", "->");
946+
return Concatenation("//dot\n", str);
892947
end);

0 commit comments

Comments
 (0)