How to control ordering of root nodes? - graphviz

I can control ordering of other nodes using ordering="in", but root nodes have no incoming edges to define the order by that way.
Eg:
digraph {
A -> B
C -> D
D -> X
B -> X
X [ordering="in"]
}
It generates this:
but I want this:

This question may be duplicated with the question here.
The following approach using additional invisible edges works perfectly for this graph:
digraph {
A -> B
A -> D[style=invis];
C -> B[style=invis];
C -> D
D -> X
B -> X
X [ordering="in"]
}

Related

Graphviz: align nodes horizontally

I thought the following would be simple, but for some reason, it really isn't...
Let's start with this:
digraph G {
rankdir=LR
A -> B -> C -> E -> F
}
This is the output:
Now, let's add a backwards relationship:
digraph G {
rankdir=LR
A -> B -> C -> E -> F
E -> A
}
I get this:
In the end, what I want is for the nodes to be aligned horizontally, and the backwards arrow to go around. I'm thinking, easy, with rank=same:
digraph G {
rankdir=LR
{rank=same;A -> B -> C -> E -> F}
E -> A
}
Oops:
Now, it's not even aligned horizontally or even sequentially (it's E, F, A, B, C). So how do I get a layout as in the very first output, with an arrow from E to A going around?
How about:
digraph G {
//rankdir=LR
{rank=same;A -> B -> C -> E -> F}
E -> A
}
the rank=same forces the LR

In graphviz, can you bring two vertices closer together?

When describing a graph with graphviz, I sometimes find I want two vertices to appear closer together than the layout engine I chose places them. Is there a way to hint that I want them closer?
I'm mostly interested in the case of two connected vertices, so an answer specific to that case is fine.
Concrete example:
digraph G {
node [shape="circle"];
Start [shape="none" label=""];
C [shape="doublecircle"];
Start -> A;
A -> B [label="0,1"];
B -> C [label="0,1"];
C -> D [label="0,1"];
D -> D [label="0,1"];
}
I want the vertices Start and A to be closer.
You can't do that, but you can make nearly everything else twice as big, here is a start.
(But you can't increase the size of an edge to self)
digraph G {
rankdir=LR
edge[minlen=2 fontsize=28 arrowsize=2 penwidth=2]
node[fontsize=28 height=1 penwidth=2]
graph[fontsize=28 penwidth=2]
node [shape="circle"];
Start [shape="none" label=""];
C [shape="doublecircle"];
Start -> A[minlen=1]; // not twice the size to get the requested effect
A -> B [label="0,1"];
B -> C [label="0,1"];
C -> D [label="0,1"];
D -> D [label="0,1"];
}
[this answer applies specifically to dot]
there is no edge-level attribute that explicitly sets or changes edge length
the graph-level nodesep attribute sets minimum distance between two nodes of same rank
so:
digraph G {
nodesep=.17
{
rank=same
node [shape="circle"];
Start [shape="none" label=""];
C [shape="doublecircle"];
Start -> A;
A -> B [label="0,1"];
B -> C [label="0,1"];
C -> D [label="0,1"];
D -> D [label="0,1"];
}
}
produces:
To increase the distance between the other nodes, you can add spaces to the labels.
I'm not wild about it either, but this change:
B -> C [label=" 0,1 "]; // pad to make label (and edge) longer
produced this:

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:

Graphviz: How to add label to arrows if nodes already have a label

I am making a graph using graphviz dot.
like this
digraph protocol {
label="Protocol workflow";
node[shape=box, style=rounded];
a[label="Byte received"];
b[label="Is start delimiter?", shape=diamond];
c[label="Recv length [n, 1B]"]; c1[label="Set count=n"];
d[label="Recv 1 payload byte and decrement count"];
e[label="count > 0", shape=diamond];
f[label="Recv CRC [2B]"];
g[label="Is CRC valid?", shape=diamond];
a -> b -> c -> c1 -> d -> e -> f -> g;
e -> d;
}
Problem is that to some arrows I need to add yes no labels but I don't know how I could do that.
You may add labels to edges like this:
e -> f [label="Yes"];

How can I create symmetric edges in Graphviz?

I would like the edges between two nodes to be symmetric about a horizontal line. For example, this code:
digraph G {
rankdir=LR
s [style=invisible]
A [peripheries=2]
D [peripheries=2]
B [peripheries=2]
s -> A
A -> C [label="1"]
C -> D [label="0"]
D -> C [label="1"]
A -> B [label="0"]
}
Produces this graph:
I would prefer the edges between C and D to look more like this:
Is there a way to get Graphviz to do this?
You can place a third edge and make the middle one invisible:
C -> D [label="0"];
D -> C [style=invis];
D -> C [label="1"];

Resources