Skip to content

Commit

Permalink
Use graphviz v1
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-mitchell committed Mar 14, 2024
1 parent fddd7f9 commit 3e32d9d
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 51 deletions.
1 change: 1 addition & 0 deletions PackageInfo.g
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ Dependencies := rec(
NeededOtherPackages := [["datastructures", ">=0.2.5"],
["digraphs", ">=1.6.2"],
["genss", ">=1.6.5"],
["graphviz", ">=0.0.0"],
["images", ">=1.3.1"],
["IO", ">=4.5.1"],
["orb", ">=4.8.2"]],
Expand Down
157 changes: 106 additions & 51 deletions gap/tools/display.gi
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,8 @@ function(D)
local S, edge_colors, N, msg, legend, node_labels, offset, en, label, next,
edge_func, node_func, result, i;

# TODO replace with an appropriate call to DotWordGraph

S := SemigroupOfCayleyDigraph(D);
edge_colors := ["\"#00ff00\"", "\"#ff00ff\"", "\"#007fff\"", "\"#ff7f00\"",
"\"#7fbf7f\"", "\"#4604ac\"", "\"#de0328\"", "\"#19801d\"",
Expand All @@ -755,6 +757,8 @@ function(D)
if IsOne(en[i]) then
label := "ε";
else
# TODO maybe reuse some of this it seems to give better factorisations,
# than the DotRightCayleyDigraph method below
label := SEMIGROUPS.WordToExtRepObj(MinimalFactorization(S, i) + offset);
label := SEMIGROUPS.ExtRepObjToString(label);
fi;
Expand Down Expand Up @@ -805,88 +809,139 @@ function(D)
end);

InstallMethod(DotLeftCayleyDigraph, "for a semigroup", [IsSemigroup],
S -> DotString(LeftCayleyDigraph(S)));
function(S)
local label;
label := x -> SEMIGROUPS.WordToString(MinimalFactorization(S, x));
return DotWordGraph(LeftCayleyDigraph(S),
List(S, label),
List(GeneratorsOfSemigroup(S), label));
end);

InstallMethod(DotRightCayleyDigraph, "for a semigroup", [IsSemigroup],
S -> DotString(RightCayleyDigraph(S)));
function(S)
local label;

# TODO handle monoids
# ToExtRepObj := SEMIGROUPS.WordToExtRepObj;
# ToString := SEMIGROUPS.ExtRepObjToString;
# label := x -> ToString(ToExtRepObj(MinimalFactorization(S, x)));
# Current the above approach doesn't work becase labels aren't put into

Check failure on line 828 in gap/tools/display.gi

View workflow job for this annotation

GitHub Actions / codespell

becase ==> because
# quotes " by default and so an error is given for this at present.
# TODO delete the next line when graphviz is fixed.
label := x -> SEMIGROUPS.WordToString(MinimalFactorization(S, x));
return DotWordGraph(RightCayleyDigraph(S),
List(S, label),
List(GeneratorsOfSemigroup(S), label));
end);

InstallMethod(DotWordGraph, "for a word graph and list of edge labels",
[IsDigraph, IsList, IsList],
InstallMethod(DotWordGraph,
"for a word graph, list of node labels, and list of edge labels",
[IsDigraph, IsHomogeneousList, IsHomogeneousList],
function(D, node_labels, edge_labels)
local M, N, edge_colors, msg, legend, label, next, node_func, edge_func, result, v, i;
local M, N, msg, edge_colors, dot, mm, pos, targets, e, legend, subgraph,
key, key2, label, next, str, m, n, i;

# TODO have some options, like include the legend, maybe fancy labels

if IsNullDigraph(D) then
if not DigraphHasAVertex(D) then
# Word graphs must have at least one node . . .
ErrorNoReturn("TODO3");
ErrorNoReturn("The 1st argument (a digraph) must have at least one vertex");
elif not IsOutRegularDigraph(D) then
ErrorNoReturn("The 1st argument (a digraph) must be out-regular");
fi;

M := DigraphNrVertices(D);
N := Length(OutNeighboursOfVertex(D, 1));

if not IsOutRegularDigraph(D) then
ErrorNoReturn("TODO1");
if Length(node_labels) <> M then
msg := "Expected the 2nd argument (a list) to have length ";
Append(msg, "{}, but found {}");
ErrorNoReturn(StringFormatted(msg), M, Length(node_labels));
elif not IsString(node_labels[1]) then
ErrorNoReturn("TODO");
elif Length(edge_labels) <> N then
ErrorNoReturn("TODO2");
msg := "Expected the 3rd argument (a list) to have length ";
Append(msg, "{}, but found {}");
ErrorNoReturn(msg, M, Length(edge_labels));
elif not IsString(edge_labels[1]) then
ErrorNoReturn("TODO");
fi;

edge_colors := ["\"#00ff00\"", "\"#ff00ff\"", "\"#007fff\"", "\"#ff7f00\"",
"\"#7fbf7f\"", "\"#4604ac\"", "\"#de0328\"", "\"#19801d\"",
"\"#d881f5\"", "\"#00ffff\"", "\"#ffff00\"", "\"#00ff7f\"",
"\"#ad5867\"", "\"#85f610\"", "\"#84e9f5\"", "\"#f5c778\"",
"\"#207090\"", "\"#764ef3\"", "\"#7b4c00\"", "\"#0000ff\"",
"\"#b80c9a\"", "\"#601045\"", "\"#29b7c0\"", "\"#839f12"];
# TODO longer list here
edge_colors := ["#00ff00", "#ff00ff", "#007fff", "#ff7f00",
"#7fbf7f", "#4604ac", "#de0328", "#19801d",
"#d881f5", "#00ffff", "#ffff00", "#00ff7f",
"#ad5867", "#85f610", "#84e9f5", "#f5c778",
"#207090", "#764ef3", "#7b4c00", "#0000ff",
"#b80c9a", "#601045", "#29b7c0", "#839f12"];

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

# node_labels := ["&#949;"];
dot := GV_Digraph("WordGraph");
GV_SetAttr(dot, "node [shape=\"box\"]");
for m in [1 .. M] do
mm := GV_AddNode(dot, m);
GV_SetAttr(mm, "label", node_labels[m]);
pos := Position(edge_labels, node_labels[m]);
if pos <> fail then
GV_SetAttr(mm, "color", edge_colors[pos]);
GV_SetAttr(mm, "style", "filled");
fi;
od;

# for v in [2 .. M] do
# Add(node_labels, edge_labels{DigraphPath(D, 1, v)[2]});
# od;
for m in [1 .. M] do
targets := OutNeighboursOfVertex(D, m);
for n in [1 .. N] do
e := GV_AddEdge(dot, m, targets[n]);
GV_SetAttr(e, "color", edge_colors[n]);
od;
od;

legend := "node [shape=plaintext]\nsubgraph cluster_01 {\nlabel=\"Legend\"\n";
Append(legend, "key2 [label=<<table border=\"0\" cellpadding=\"2\"");
Append(legend, " cellspacing=\"0\" cellborder=\"0\">\n");
legend := GV_AddContext(dot, "legend");
GV_SetAttr(legend, "node [shape=plaintext]");
subgraph := GV_AddSubgraph(legend, "legend");
key := GV_AddNode(subgraph, "key");
key2 := GV_AddNode(subgraph, "key2");

label := Concatenation("<<table border=\"0\" cellpadding=\"2\"",
" cellspacing=\"0\"",
" cellborder=\"0\">\n");
for i in [1 .. N] do
Append(legend,
Append(label,
StringFormatted(" <tr><td port=\"i{}\">&nbsp;</td></tr>\n",
i));
od;
Append(legend, "</table>>]\n");
Append(legend, "key [label=<<table border=\"0\" cellpadding=\"2\"");
Append(legend, " cellspacing=\"0\" cellborder=\"0\">\n");
Append(label, "</table>>\n");
GV_SetAttr(key2, "label", label);

# TODO remove code dupl
label := Concatenation("<<table border=\"0\" cellpadding=\"2\"",
" cellspacing=\"0\"",
" cellborder=\"0\">\n");
for i in [1 .. N] do
label := [edge_labels[i]];
next := "<tr><td align=\"right\" ";
Append(next, StringFormatted("port=\"i{}\">{}&nbsp;</td></tr>\n", i, label));
Append(legend, next);
next := " <tr><td align=\"right\" ";
Append(next, StringFormatted("port=\"i{}\">{}&nbsp;</td></tr>\n",
i,
edge_labels[i]));
Append(label, next);
od;
Append(label, "</table>>\n");

Append(legend, "</table>>]\n\n");
GV_SetAttr(key, "label", label);

for i in [1 .. N] do
next := StringFormatted("key:i{1}:e -> key2:i{1}:w [color={2},",
i,
edge_colors[i]);
Append(next, "constraint=false]\n");
Append(legend, next);
e := GV_AddEdge(subgraph,
StringFormatted("key:i{}:e", i),
StringFormatted("key2:i{}:w", i));
GV_SetAttr(e, "color", edge_colors[i]);
GV_SetAttr(e, "constraint", false);
od;
Append(legend, "}\n");

node_func := i -> StringFormatted(" [label=\"{}\"]", node_labels[i]);
edge_func := {i, j} -> StringFormatted("[color={}]", edge_colors[j]);

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

0 comments on commit 3e32d9d

Please sign in to comment.