Random change of node order in GraphViz/dot - graphviz

I am trying to paint a little graph in GraphViz/dot.
The boxes "Team 1", "Turnier32" and "Team 2" should be on one horizontal level.
"Team 1" points right to "Turnier32", "Team 2" to the left.
I want the ellipses "Name1", "2015-11-14" and "Name2" below the corresponding nodes. There should only be straight horizontal and vertical edges.
My current approach is this:
digraph turnier{
ordering=out ;
subgraph {
rank=same;
T1 [shape="box", label="Team 1"];
TOURNAMENT [shape="box", label="Turnier32"];
T2 [shape="box", label="Team 2"];
}
subgraph {
rank=same;
N1 [shape="ellipse", label="Name1"];
DATE [shape="ellipse", label="2015-11-14"];
N2 [shape="ellipse", label="Name2"];
}
T1 -> N1 [label="hasName"];
TOURNAMENT -> DATE [label="occuredOnDate"];
T2 -> N2 [label="hasName"];
T1 -> TOURNAMENT [label="attended"];
T2 -> TOURNAMENT [label="attended"];
}
GraphViz reordered T1, TOURNAMENT and T2 to T1, T2, TOURNAMENT, which is not, what I want. If i strike out the last two edges, the order is correct.

Simply add to your last line constraint=false:
T2 -> TOURNAMENT [label="attended", constraint=false];

Related

strictly left to right arcs in graphviz

I would like to create a figure where the arcs only enter a node on the left hand side, and exit only from the right hand side.
I have created the following dot file, and its picture is shown below:
digraph net {
"C0" [shape=oval label="C0"]
"C1" [shape=oval label="C1"]
"B0" [shape=box label="B0"]
rankdir="LR"
"C0" -> "B0"
"B0" -> "C1"
"C1" -> "B0"
}
But I would like to see the arc C1->B0 as exiting C1 from its right hand side, and curving (or even a rectangular arc) back to the left side of B0. How do I do that?
Use port positions (https://graphviz.org/docs/attr-types/portPos/) to specify tail & head positions.
Note the syntax including/excluding " character. (The "s are not needed for these node names)
digraph net {
"C0" [shape=oval label="C0"]
"C1" [shape=oval label="C1"]
"B0" [shape=box label="B0"]
rankdir="LR"
"C0" -> "B0"
"B0" -> "C1"
"C1":e -> B0:w // note portpos with/without "s. Both work
}
Giving:

In a graphviz dot digraph, how can I break a wide layout (rankdir LR)

With python I'm trying to generate a long graph where always one node points to the next. This ends up in having a long snail of nodes (rankdir LR). However I want to break it after a certain width or number or nodes. How can this be achived?
graph = gv.Digraph(format='svg')
graph.graph_attr.update({'rankdir': 'LR'})
graph.node('a', 'A')
graph.node('b', 'B')
graph.node('c', 'C')
graph.node('d', 'D')
graph.node('e', 'E')
graph.node('f', 'F')
...
graph.edges(['ab', 'bc', 'cd', 'de', 'ef', ...])
Output:
However I want (or similar):
I tried to use size, but that only zooms the whole graph.
As a workarround I tried to reduce ranksep, but that only makes it better for a few more items.
I also searched a lot but could not find an appropriate answer.
An unanswered question that goes into a similar direction is:
graphviz plot too wide.
For other related questions suggested answer was to use invisible elements but that does not work here either.
Update:
I've altered the code for edges according to the comment of #vaettchen:
graph.edge('a', 'b', None, {'weight':'5'})
graph.edge('b', 'c', None, {'weight':'5'})
graph.edge('d', 'e', None, {'weight':'5'})
graph.edge('e', 'f', None, {'weight':'5'})
graph.edge('c', 'd', None, {'weight':'1'})
graph.edge('a', 'd', None, {'style':'dashed', 'rank':'same'})
Unfortunately the result now looks like this (style 'dashed' instead of 'invis' for better visibility):
'rank': 'same' seems not change anything. Also when applied to nodes A and D.
This should be a comment rather than an answer as it doesn't address the python issue and I guess you are also looking for something more "automatic" - but maybe it gives some ideas; and as nobody else is picking it up, here a pure graphviz suggestion:
digraph so
{
// graph attributes
rankdir = LR; // horizontal graph
splines = ortho // edges with "corners"
// default/initial node style
node[ shape = box ];
// nodes where the "new lines" begin
// connected invisibly to keep them in order
{ rank = same; A -> E -> I[ style = invis ] }
// nodes that are to be in one line
// extra weight needed to keep the edges straight
edge[ weight = 5 ];
A -> B -> C -> D;
E -> F -> G -> H;
I -> J -> K -> etc;
// edges connecting the graph elements over the lines
edge[ weight = 1 ];
D -> E;
H -> I;
}
yields
There are several ways to make this "snake".
First, to create right-angle edge bends, apply to all edges attribute splines=ortho.
Variant 1
Use edge attributes such as constraint=false or weight=0 for C -> D edge to create "soft" edge and rank=same for A, D nodes to create "strong" alignment between these nodes.
DOT script:
digraph {
graph[rankdir=LR;splines=ortho]
node[shape=box]
A -> B -> C
D -> E -> F
C -> D [constraint=false]
{rank=same;A;D}
}
Variant 2
Use group attribute to create "strong" alignment between A, B, C nodes and between D, E. F nodes; and rank=same for A, D nodes to create "strong" alignment between these nodes.
DOT script:
digraph {
graph[rankdir=LR;splines=ortho]
node[shape=box]
A [group=g1]
B [group=g1]
C [group=g1]
D [group=g2]
E [group=g2]
F [group=g2]
A -> B -> C -> D -> E -> F
{rank=same;A;D}
}
Both variant give the same result, I suppose that you can also use the neato engine to set the exact coordinates of the nodes, but it looks overcomplicated.
Minimal code example (for variant 1) with comments:
import graphviz as gv
nodes = ['A','B','C','D','E','F']
# Count of nodes in row,
# can be changed for the desired graph width
columns = 3
graph = gv.Digraph(format='svg', filename = "output/mygraph.gv",
graph_attr=dict(rankdir='LR', splines='ortho'),
node_attr=dict(shape='box'))
# Set constraint=false only for desired edges, for example
# only for every 3rd edges, where `3` is set by `columns` variable
for i in range(1, len(nodes)):
if i % columns == 0 :
graph.edge(nodes[i-1], nodes[i], constraint='false')
else:
graph.edge(nodes[i-1], nodes[i])
# Add desired nodes to `rank=same` subgraph
with graph.subgraph() as s:
s.attr(rank='same')
for i in range(0, len(nodes)):
if i % columns == 0 :
s.node(nodes[i])
graph.view()
Result image:
Result mygraph.gv:
digraph {
graph [rankdir=LR splines=ortho]
node [shape=box]
A -> B
B -> C
C -> D [constraint=false]
D -> E
E -> F
{
rank=same
A
D
}
}
Possible improvements
If there is one node on the line, creates a non-consistent last arrow:
This can be corrected by creating an invisible node inv2 between the nodes F and G:
digraph {
graph [rankdir=LR splines=ortho nodesep=.2]
node [shape=box]
A -> B
B -> C
C -> inv1 [constraint=false arrowhead=none]
inv1 -> D [constraint=false ]
D -> E
E -> F
F -> inv2 [constraint=false arrowhead=none]
inv2 -> G [constraint=false]
{
rank=same
A
inv1 [shape=point width=.01]
D
inv2 [shape=point width=.01]
G
}
}
Result:

How do I order subgraph clusters when using dot?

I have a dot file in which I create subgraph clusters which I want to appear in a specific order, let's say I have this:
digraph G {
splines=true;
sep="+25,25";
overlap=scalexy;
nodesep=0.6;
subgraph cluster_2 {
label="ADD_MORE_PROBLEMS";
subgraph cluster_3 {
label="pattern";
N1 [label="problem"];
}
subgraph cluster_4 {
label="replacement";
N2 [label="problem"];
N3 [label="problem"];
}
}
}
Which creates:
How do I ensure that "pattern" appears to the left of "replacement" (I may have an arbitrary number of subgraphs).
Clusters are one of the odd cases where simply the ordering in the code makes most (if not quite all) of the difference. If we simply reorder your code like this:
digraph G {
splines=true;
sep="+25,25";
overlap=scalexy;
nodesep=0.6;
subgraph cluster_2 {
label="ADD_MORE_PROBLEMS";
subgraph cluster_4 {
label="replacement";
N2 [label="problem"];
N3 [label="problem"];
}
subgraph cluster_3 {
label="pattern";
N1 [label="problem"];
}
}
}
that makes all the difference.
Now, that can fail, in which case setting up invisible edges is one of the more common solutions.
I can't give and answer, but can provide some clarification. The usual approach to force layout is to introduce hidden edges. In this case, it does not work.
Without the nested clusters, you can use rank=same to force connected edges onto the same level. Then, an invisible edge N1 -> N2 [style = invis] would force the nodes into the correct ordering.
However, constraining nodes with rank breaks the cluster membership and prevents the scheme from working.
The modified graph shows the result. There may not be a general solution.
digraph G {
splines=true;
sep="+25,25";
overlap=scalexy;
nodesep=0.6;
subgraph cluster_2 {
label="ADD_MORE_PROBLEMS";
subgraph cluster_3 {
label="pattern";
N1 [label="problem 1"];
}
subgraph cluster_4 {
label="replacement";
N2 [label="problem 2"];
N3 [label="problem 3"];
}
// Introduce hidden edge (shown dashed)
N1 -> N2 [style = dashed];
// Force nodes to remain at same rank
{ rank = same; N1; N2; }
}
}

Graphviz Vertical Ordering

I have a set of GraphViz nodes such that:
digraph {
A->B;
A->C;
A->D;
}
But B, C, and D happen sequentially in time!
It would be great if there was some way to indicate the vertical level each node should appear upon (where the number of levels may be unknown beforehand).
Does anyone have thoughts on how to accomplish this?
One option to have a node display on a different rank (vertical level) than an other node is to add invisible edges.
Assigning those nodes the same group indicates graphviz to lay them out in a straight line if possible.
For example:
digraph g{
A;
node[group=a];
B;C;D;
A -> B;
A -> C;
A -> D;
edge[style=invis];
B->C->D;
}
An other option is to have one vertical line of (invisible) nodes, then force the same rank by defining the nodes of the same rank within the same subgraph with rank=same:
digraph g{
{rank=same; l1[style=invis, shape=point]; A;}
{rank=same; l2[style=invis, shape=point]; B;}
{rank=same; l3[style=invis, shape=point]; C;}
{rank=same; l4[style=invis, shape=point]; D;E;F;}
A -> B;
A -> C;
A -> D;
edge[style=invis];
l1->l2->l3->l4;
}

simple "T shaped" graph in graphviz

Need draw a graph with dot/graphviz like this image:
The texts can be above arrows, like graphviz does it. But how to achieve the T-layout? Need make a cluster for the top row?
This is one possibility using rank=same for a subgraph:
digraph g {
node[shape=point, width=0.2];
{
rank=same;
p1 -> n [label="text1"];
n -> p2 [label="text2"];
}
n -> p3 [label="text3", dir=back];
n[label="node", shape=rect, style=rounded];
}
You could also use a left-right layout instead of top-down.
An other possibility is to disable the effect of some edges using constraint=false:
digraph g {
node[shape=point, width=0.2];
p1 -> n [label="text1", constraint=false];
n -> p2 [label="text2", constraint=false];
n -> p3 [label="text3", dir=back];
n[label="node", shape=rect, style=rounded];
}
The result is the same.
dot usually layouts trees in layers. To force an edge to not be a layer separation you can add the constraint=false option. So something like:
digraph {
A [shape=point]
B [shape=point]
C [shape=point]
N [label="node"]
A -> N [label="text1", constraint=false]
N -> B [label="text2", constraint=false]
N -> C [label="text3", dir=back]
}
should work.
Note that the edge from the lower node to "node" has to be backwards, since dot layouts trees from top to bottom. Therefore the logical edge direction has to be from top to bottom, even though the display direction might be the other way round (which is the case here).

Resources