Graphviz: enforce nonoverlap among clusters - graphviz

I'm trying to generate a layout of several undirected graphs (trees) in the same .dot file for a research project; the goal is to be able to easily inspect the trees visually. In order to achieve this, I'm making a subgraph cluster_ for each tree, see example:
graph {
layout = neato
subgraph cluster_0 {
label = "n= 7, tree 1";
nd_93 [label= "1"];
nd_94 [label= "2"];
nd_95 [label= "3"];
nd_96 [label= "4"];
nd_97 [label= "5"];
nd_98 [label= "6"];
nd_99 [label= "7"];
nd_93 -- nd_94;
nd_93 -- nd_97;
nd_94 -- nd_95;
nd_94 -- nd_96;
nd_97 -- nd_98;
nd_97 -- nd_99;
}
subgraph cluster_1 {
label = "n= 8, tree 1";
nd_238 [label= "1"];
nd_239 [label= "2"];
nd_240 [label= "3"];
nd_241 [label= "4"];
nd_242 [label= "5"];
nd_243 [label= "6"];
nd_244 [label= "7"];
nd_245 [label= "8"];
nd_238 -- nd_239;
nd_238 -- nd_243;
nd_239 -- nd_240;
nd_239 -- nd_241;
nd_239 -- nd_242;
nd_243 -- nd_244;
nd_243 -- nd_245;
}
subgraph cluster_2 {
label = "n= 9, tree 1";
nd_380 [label= "1"];
nd_381 [label= "2"];
nd_382 [label= "3"];
nd_383 [label= "4"];
nd_384 [label= "5"];
nd_385 [label= "6"];
nd_386 [label= "7"];
nd_387 [label= "8"];
nd_388 [label= "9"];
nd_380 -- nd_381;
nd_380 -- nd_385;
nd_381 -- nd_382;
nd_382 -- nd_383;
nd_382 -- nd_384;
nd_385 -- nd_386;
nd_386 -- nd_387;
nd_386 -- nd_388;
}
subgraph cluster_232 {
label = "n= 13, tree 1";
nd_20639 [label= "1"];
nd_20640 [label= "2"];
nd_20641 [label= "3"];
nd_20642 [label= "4"];
nd_20643 [label= "5"];
nd_20644 [label= "6"];
nd_20645 [label= "7"];
nd_20646 [label= "8"];
nd_20647 [label= "9"];
nd_20648 [label= "10"];
nd_20649 [label= "11"];
nd_20650 [label= "12"];
nd_20651 [label= "13"];
nd_20639 -- nd_20640;
nd_20639 -- nd_20648;
nd_20640 -- nd_20641;
nd_20640 -- nd_20645;
nd_20641 -- nd_20642;
nd_20641 -- nd_20643;
nd_20641 -- nd_20644;
nd_20645 -- nd_20646;
nd_20645 -- nd_20647;
nd_20648 -- nd_20649;
nd_20649 -- nd_20650;
nd_20649 -- nd_20651;
}
}
The layout=dot makes the trees appear each next to each other in a horizontal line, which is not desirable when there are more trees than just four (and in the real file there are going to be around 300 trees) -- it becomes harder to find patterns.
.
The layout=neato produces nicer results concerning the layout of the trees themselves (the layout of the nodes in each cluster is more faithful to an undirected tree -- dot produces drawings of rooted trees) and the clusters (the actual trees) appear closer together and can be inspected visually more easily.
However, the layout produces serious overlaps between the boundaries of the clusters, and the labels (that I need to see in order to identify the trees). Other layouts seem to work better, but are not quite right: fdp does not produce overlaps in the cluster boundaries, but produces edge crossings. Layout sfdp removes the cluster boundaries and does not display labels altogether.
fdp:
sfdp:
QUESTION Does somebody know if there is some layout option, or perhaps some fine tuning of the options layout, mode, spline, ... so that
the clusters do not overlap (as in fdp),
the layout of the trees (the individual clusters) is not that of a rooted tree (as in neato, not as in dot), and
there are no edge crossings (as in neato and sfdp, not as in fdp)
The clusters can be arranged in any way on the plane, it is not important for the moment to have tree (cluster) n= 13, tree 1 close to n= 13, tree 2 (which does not appear in the example).

You could:
Produce (300) separate graphs in a special directory, say graphs/. Clusters probably no longer needed. Assume that the names of files just created have the format tree_nn_xxxxx.raw where nn is the number of nodes with two digits (01, 02, ...) and xxxxx is an index to distinguish between graphs of the same number of vertices; .raw is just a mock extension to be able to "capture" them from a regexp *.raw.
Run each through neato (or any of the engines). Note: make the output format -Tdot. For example,
for f in $(ls graphs/*.raw); do
# '$f' contains '.raw'
f_no_ext=${f:0: $(( ${#f} - 4 ))}
dot -Tdot $f > $f_no_ext.dot
done
Then, you can use gvpack to combine those files into one (very large) graph. For example,
function normalize_value {
n=$1
if [ $n -lt 10 ]; then
echo "0"$n
else
echo $n
fi
}
for n in `seq 1 14`; do
echo $n
nn=$(normalize_value $n)
gvpack -array_it20 graphs/tree_$nn*.dot > graphs/big_graph_$nn.dot
done
You will probably want to run some experiments with gvpack to get a "best" layout.
Finally, run each big_graph_* file through neato -n2 -Tsvg to visualize the result. For example,
for n in `seq 1 14`; do
echo $n
nn=$(normalize_value $n)
echo " Run neato"
neato -n2 -Tsvg graphs/big_graph_$nn.dot > all_trees_with_nonbip_$nn.svg
done

Related

Can I make graphviz concentrate edges based on color?

I have a directed graph where the color of the edges matters:
Current Diagram
digraph {
splines=ortho
node [shape=box];
1 -> 4 [color="#51dbf4"];
4 -> 7 [color="#51dbf4"];
7 -> 1 [color="#ac043e"];
7 -> 1 [color="#51dbf4"];
1 -> 7 [color="#ac043e"];
}
I want to combine the maroon colored arrows to make the graph more clear.
Desired Diagram
digraph {
splines=ortho
node [shape=box];
1 -> 4 [color="#51dbf4"];
4 -> 7 [color="#51dbf4"];
7 -> 1 [color="#ac043e" dir=both];
7 -> 1 [color="#51dbf4"];
}
However, this requires me to manually change each arrow that needs to be combined into a double-headed arrow. I wanted to merge these automatically. I tried using concentrate=true to make the arrows merge, but that kind of destroys the meaning of the graph:
Incorrect Diagram
digraph {
splines=ortho
concentrate=true
node [shape=box];
1 -> 4 [color="#51dbf4"];
4 -> 7 [color="#51dbf4"];
7 -> 1 [color="#ac043e"];
7 -> 1 [color="#51dbf4"];
1 -> 7 [color="#ac043e"];
}
Graphviz insists on merging the arrows regardless of their color, which is not correct in this case because it kind of just obliterates most of the edges.
Question
For my use case, I have a large, code-generated graph with tons of edges and I don't want to manually change so many edges to be double-ended. Is there a way to specify to graphviz that I only want to merge edges of the same color? Like maybe I could tag each edge with a group ID and then it would only merge edges that have the same ID? Any ideas are appreciated.
There may be an easier way, but this gvpr (http://www.graphviz.org/pdf/gvpr.1.pdf) program does what you want.
For each edge, it checks if a "reverse" edge exists, and if so checks if both have the same color.
If so, set dir=both & delete the "reverse" edge.
E{
edge_t N2;
N2=isEdge($.head, $.tail,"");
if (N2!=NULL && N2.color==$.color){
print("// bingo : ", $.name, " <--> ", N2.name);
$.dir="both";
delete($G, N2);
}
}
And a (Linux) command line of
gvpr -c -f single2double.gvpr myfile.gv | dot -Tpng >o.png
Giving:

Represent array with indices using dot record nodes (Graphviz)

I'm using Graphviz to represent arrays, using subgraphs and record nodes:
subgraph cluster_array
{
label="my array"
Array [shape="record", label="A | B | C | D"]
Array
}
I would like to add external indices for each array elements, mapping 0 -> A, 1 -> B and so on.
I want to achieve a result similar to:
I've searched online and tried using xlabel but couldn't find a way to correctly add a label for each record element. I've also tried making the indices part of the label, and moving the label with lp, but it seems to have no effect on record nodes.
Is it possible to add external element labels to record nodes using GraphViz?
Not a real answer to your question (which, I think, would be "no") but a workaround that may give you what you want. I use a "parallel" record node with no borders (or paper color borders, to be exact), located very close and connected by an invisible edge:
digraph so
{
subgraph cluster0
{
rank = same{ Array notes }
color = white;
Array [ shape = record, label = "{ A | B | C | D }"] ;
notes [ shape = record, color = white, label = "{ a_1 | b_2 | c_3 | d_4 }" ];
Array -> notes[ style = invis ];
}
nodesep = .0;
X -> Array -> Z;
}
which yields

Order cluster nodes in graphviz

I have the following (simplified) graph which is generated by the following .dot:
digraph Configurations {
subgraph cluster_1 {
s_0_0 [shape=circle,style=filled,fixedsize=true,width=0.5,label="0",fillcolor=yellowgreen]
s_0_1 [shape=circle,style=filled,fixedsize=true,width=0.5,label="1",fillcolor=yellowgreen]
}
subgraph cluster_2 {
s_1_0 [shape=circle,style=filled,fixedsize=true,width=0.5,label="0",fillcolor=yellowgreen]
s_1_1 [shape=circle,style=filled,fixedsize=true,width=0.5,label="1",fillcolor=white]
}
subgraph cluster_3 {
s_2_0 [shape=circle,style=filled,fixedsize=true,width=0.5,label="0",fillcolor=white]
s_2_1 [shape=circle,style=filled,fixedsize=true,width=0.5,label="1",fillcolor=yellowgreen]
}
subgraph cluster_4 {
s_3_0 [shape=circle,style=filled,fixedsize=true,width=0.5,label="0",fillcolor=white]
s_3_1 [shape=circle,style=filled,fixedsize=true,width=0.5,label="1",fillcolor=white]
}
s_0_1 -> s_1_1
s_0_0 -> s_2_0
s_2_1 -> s_3_1
s_1_0 -> s_3_0
}
I would like to be able to be able to enforce ordering inside the subgraphs so that the nodes of each subgraph are displayed in ascending order (each cluster should have nodes placed (0, 1), never (1, 0)). As I understand it, rankdir, which was my first attempt, is not supported in subgraphs, so what is a proper way to do this? I am looking for a solution which gives me a reasonably similar layout (which would then include more intersecting arrows) and is scalable, since the real graphs will be huge.
Turns out this could be solved by adding invisible edges inside and forcing same rank inside the graphs, like so:
subgraph cluster_1 {
{rank=same; s_0_0 s_0_1}
s_0_0 -> s_0_1 [style=invis]
s_0_0 [shape=circle,style=filled,fixedsize=true,width=0.5,label="0",fillcolor=yellowgreen]
s_0_1 [shape=circle,style=filled,fixedsize=true,width=0.5,label="1",fillcolor=yellowgreen]
}
If there are more nodes than 2 nodes, we need to change the solution.
subgraph cluster1 {
{
HL_1_n HL_1_1 HL_1_2 HL_1_3 HL_1_m
}
HL_1_1 [label="Hidden Layer 1 Node 1" color=3]
HL_1_2 [label="Hidden Layer 1 Node 2" color=3]
HL_1_3 [label="Hidden Layer 1 Node 3" color=3]
HL_1_m [label="Hidden Layer 1 Node ..." color=3]
HL_1_n [label="Hidden Layer 1 Node H_1" color=3]
label = "Hidden Layer"
}
It seems the order is determined, so we just need to change nodes' positions to fit the output. The solution does not use edge constraints and rank.

Rank attribute is confusing to me

Rank attribute on edge has five values "same", "min", "source", "max", "sink".
Except "same", I have no idea when to use other values.
min
\begin{dotpic}
rankdir=LR;
size="7,5";
node[shape=circle];
C->A;
{rank=min;A;B}
B->D
A->B;
\end{dotpic}
max
\begin{dotpic}
rankdir=LR;
size="7,5";
node[shape=circle];
C->A;
{rank=max;A;B}
B->D
A->B;
\end{dotpic}
source
\begin{dotpic}
rankdir=LR;
size="7,5";
node[shape=circle];
C->A;
{rank=source;A;B}
B->D
A->B;
\end{dotpic}
sink
\begin{dotpic}
rankdir=LR;
size="7,5";
node[shape=circle];
C->A;
{rank=sink;A;B}
B->D
A->B;
\end{dotpic}
With test on my vim environment, I can realize there is some difference btw these values.
But don't know exactly what they are for.
Leaving the rank empty or using rank=same are used far more often. These other four are usually only used in special circumstances.
When used alone, min and source have the same function: putting all those nodes on the minimum rank (the top row of a TB graph). The difference between them is that min will allow other subgraphs in the minimum rank. Source will not. Source only allows other subgraphs of min or source to be on the minimum rank.
Consider the following graph snippet:
{ rank=source; a -> b; }
{ rank=same; c -> d; }
You will end up with 2 rows. a -> b will be above c -> d.
If you change source to min, you will only get one row. a -> b will be to left of c -> d, all in the min rank.
{ rank=min; a -> b; }
{ rank=same; c -> d; }
Max and sink are the equivalents for the bottom of the graph.

Why does a top to bottom graphviz dot graph get layed out counter clockwise?

I updated the question with graphics and more details. Thanks to marapet, without the hack I couldnĀ“t have generated the desired results as images.
why does this code produce this graph?
digraph {
rankdir = TB;
1 -> 2 -> 3 -> 1;
}
How can I get graphviz/dot to produce a clockwise direction like this?
Update
This is the final graph I want to generate (afaik logically correct this way)
digraph {
rankdir = TB
start -> 1
1 -> 2 -> 3 -> 1
3 -> end
3 -> increment
end -> product
{rank = same; 2; 3; increment}
{rank = same; end; product}
}
Which produces this result
While I want this
Thanks
Why does this code produce this graph?
A directed graph puts its nodes on different ranks depending on their relations. Since 1 points to 2, it must be above 2, and since 2 points to 3 it gets to be above 3.
But since 3 also points to 1, the circle is completed - any of the 3 nodes could be on top. Graphviz simply puts the first mentioned node on top. Therefore, if you write instead:
2 -> 3 -> 1 -> 2;
node 2 will be on top, and when using
3 -> 1 -> 2 -> 3;
node 3 will be the top node.
Probably the layout engine neato would be more appropriate for this graph, producing a graph with a clockwise direction:
If you absolutely must use the dot layout engine, the following dot code
digraph {
rankdir = TB;
1 -> 2;
3 -> 2 [dir=back];
3 -> 1;
{rank=same; 2; 3;}
}
produces the desired output by changing the edge 2->3 into 3->2 and at the same time inverting the direction of the arrow.
Or, an other variant of the same technique, easier to explain: We reverse the order of all arrows (1->3->2->1), but display them backwards (dir=back), and force node 2 and 3 to be on the same rank:
rankdir = TB;
edge[dir=back];
1 -> 3 -> 2 -> 1;
{rank=same; 2;3;}
This hack yields the following result:

Resources