Ordering of nodes based on declaring shapes

I don’t quite understand how this:


digraph test_application {			
		rankdir=LR;	
		size="6,5"	
		node[shape = square]; "Server1 (2 hr)" "Server2 (2 hr)" "Server3 (72 hr)" "Server4 (2 hr)"	
		node[shape = triangle]; "Storage (48 hr)" "Network (0 hr)"	
		node[shape = circle];	
		"Mitre (24 hr)" -> "NSM (48 hr)" [label = "Depends on",color=red, penwidth=3.0 ];	
		"Mitre (24 hr)" -> "Server2 (2 hr)" [label = "Runs on",color=blue, penwidth=1.0 ];	
		"Mitre (24 hr)" -> "Server3 (72 hr)" [label = "Runs on",color=red, penwidth=3.0 ];	
		"Regdata (24 hr)" -> "Storage (48 hr)" [label = "Depends on",color=blue, penwidth=1.0 ];	
		"Regdata (24 hr)" -> "Network (0 hr)" [label = "Depends on",color=blue, penwidth=1.0 ];	
		"Regdata (24 hr)" -> "NSM (48 hr)" [label = "Depends on",color=red, penwidth=3.0 ];	
        "Regdata (24 hr)" -> "Server4 (2 hr)" [label = "Runs on 24|2",color=blue, penwidth=1.0 ];
		"Regdata (24 hr)" -> "Server1 (2 hr)" [label = "Runs on",color=blue, penwidth=1.0 ];	
		"NSM (48 hr)" -> "Server4 (2 hr)" [label = "Runs on",color=blue, penwidth=1.0 ];	
		"NSM (48 hr)" -> "Regdata (24 hr)" [label = "Sends data to",color=blue, penwidth=1.0 ];	
}	

[dot]
digraph test_application {
rankdir=LR;
size=“6,5”
node[shape = square]; “Server1 (2 hr)” “Server2 (2 hr)” “Server3 (72 hr)” “Server4 (2 hr)”
node[shape = triangle]; “Storage (48 hr)” “Network (0 hr)”
node[shape = circle];
“Mitre (24 hr)” → “NSM (48 hr)” [label = “Depends on”,color=red, penwidth=3.0 ];
“Mitre (24 hr)” → “Server2 (2 hr)” [label = “Runs on”,color=blue, penwidth=1.0 ];
“Mitre (24 hr)” → “Server3 (72 hr)” [label = “Runs on”,color=red, penwidth=3.0 ];
“Regdata (24 hr)” → “Storage (48 hr)” [label = “Depends on”,color=blue, penwidth=1.0 ];
“Regdata (24 hr)” → “Network (0 hr)” [label = “Depends on”,color=blue, penwidth=1.0 ];
“Regdata (24 hr)” → “NSM (48 hr)” [label = “Depends on”,color=red, penwidth=3.0 ];
“Regdata (24 hr)” → “Server4 (2 hr)” [label = “Runs on 24|2”,color=blue, penwidth=1.0 ];
“Regdata (24 hr)” → “Server1 (2 hr)” [label = “Runs on”,color=blue, penwidth=1.0 ];
“NSM (48 hr)” → “Server4 (2 hr)” [label = “Runs on”,color=blue, penwidth=1.0 ];
“NSM (48 hr)” → “Regdata (24 hr)” [label = “Sends data to”,color=blue, penwidth=1.0 ];
}
[/dot]
is different from this:

digraph test_application {
	rankdir=LR;
	size="6,5"
	"Mitre (24 hr)" [shape = circle, label = "Mitre (24 hr)"];
	"Regdata (24 hr)" [shape = circle, label = "Regdata (24 hr)"];
	"NSM (48 hr)" [shape = circle, label = "NSM (48 hr)"];
	"Storage (48 hr)" [shape = triangle, label = "Storage (48 hr)"];
	"Network (0 hr)" [shape = triangle, label = "Network (0 hr)"];
	"Server1 (2 hr)" [shape = square, label = "Server1 (2 hr)"];
	"Server2 (2 hr)" [shape = square, label = "Server2 (2 hr)"];
	"Server3 (72 hr)" [shape = square, label = "Server3 (72 hr)"];
	"Server4 (2 hr)" [shape = square, label = "Server4 (2 hr)"];
	"Mitre (24 hr)" -> "NSM (48 hr)" [label = "Depends on",color=green, penwidth=1];
	"Mitre (24 hr)" -> "Server2 (2 hr)" [label = "Runs on",color=red, penwidth=3];
	"Mitre (24 hr)" -> "Server3 (72 hr)" [label = "Runs on",color=green, penwidth=1];
	"Regdata (24 hr)" -> "Storage (48 hr)" [label = "Depends on",color=green, penwidth=1];
	"Regdata (24 hr)" -> "Network (0 hr)" [label = "Depends on",color=red, penwidth=3];
	"Regdata (24 hr)" -> "NSM (48 hr)" [label = "Depends on",color=green, penwidth=1];
	"Regdata (24 hr)" -> "Server1 (2 hr)" [label = "Runs on",color=red, penwidth=3];
	"Regdata (24 hr)" -> "Server4 (2 hr)" [label = "Runs on",color=red, penwidth=3];
	"NSM (48 hr)" -> "Server4 (2 hr)" [label = "Runs on",color=red, penwidth=3];
	"NSM (48 hr)" -> "Regdata (24 hr)" [label = "Sends data to",color=red, penwidth=3];
}

[dot]
digraph test_application {
rankdir=LR;
size=“6,5”
“Mitre (24 hr)” [shape = circle, label = “Mitre (24 hr)”];
“Regdata (24 hr)” [shape = circle, label = “Regdata (24 hr)”];
“NSM (48 hr)” [shape = circle, label = “NSM (48 hr)”];
“Storage (48 hr)” [shape = triangle, label = “Storage (48 hr)”];
“Network (0 hr)” [shape = triangle, label = “Network (0 hr)”];
“Server1 (2 hr)” [shape = square, label = “Server1 (2 hr)”];
“Server2 (2 hr)” [shape = square, label = “Server2 (2 hr)”];
“Server3 (72 hr)” [shape = square, label = “Server3 (72 hr)”];
“Server4 (2 hr)” [shape = square, label = “Server4 (2 hr)”];
“Mitre (24 hr)” → “NSM (48 hr)” [label = “Depends on”,color=green, penwidth=1];
“Mitre (24 hr)” → “Server2 (2 hr)” [label = “Runs on”,color=red, penwidth=3];
“Mitre (24 hr)” → “Server3 (72 hr)” [label = “Runs on”,color=green, penwidth=1];
“Regdata (24 hr)” → “Storage (48 hr)” [label = “Depends on”,color=green, penwidth=1];
“Regdata (24 hr)” → “Network (0 hr)” [label = “Depends on”,color=red, penwidth=3];
“Regdata (24 hr)” → “NSM (48 hr)” [label = “Depends on”,color=green, penwidth=1];
“Regdata (24 hr)” → “Server1 (2 hr)” [label = “Runs on”,color=red, penwidth=3];
“Regdata (24 hr)” → “Server4 (2 hr)” [label = “Runs on”,color=red, penwidth=3];
“NSM (48 hr)” → “Server4 (2 hr)” [label = “Runs on”,color=red, penwidth=3];
“NSM (48 hr)” → “Regdata (24 hr)” [label = “Sends data to”,color=red, penwidth=3];
}
[/dot]

The only difference between those 2 graphs is a layout of nodes and I have noticed that by swapping where I’m specifying the shape of nodes tends to change the layout.
Why is there no consistent way of displaying the same information and if there is,how do I achieve it?

P.S. I’m very new to it and trying to automate the process, so it’s not ideal to write the code manually, but if there isn’t a choice I’d be very happy to hear any kind of suggestions on how to fix it.

The good news - The placement is not based on where you declare shapes (I’m pretty sure).
The messy news -

  • The primary issue is that you have a cycle: NSM ↔ Regdata. The dot algorithm doesn’t want to get in an endless loop, so it cuts the cycle (temporarily removes an edge in the cycle) while determining ranking.
    In one case NSM gets ranked higher, in the other Regdata gets ranked higher
  • the way to determine the “winner” would be to set constraint=false on the edge you wanted to be cut/ignored.

To experiment on your two examples, try constraint-false on one edge or the other. You will note that ranking is now consistent, though the actual placement is not.
On to the second difference (not shape placement)

  • The second difference is (probably) that the nodes are defined in different order. The 1st file defines many of the nodes as they appear in edges (this is quite legal). The second defines all the nodes explicitly and in different order from the first file (again quite legal).
    Even though the resulting images are all different, from a dot point-of-view, they are equivalent.

How to consistency? Depending on the varying nature of the input, it can be easy-to-impossible.

  • I like defining all nodes before edges, easier for me to read
  • {rank=same nodes_go_here} helps with ranking.
  • placement of nodes within a rank is more challenging. invisible edges or the group attribute can help

I think what we’re seeing here is that the layout of a graph involves a search that depends on the input order of nodes and edges. With nodes, this is the first time the node appears in the file, whether or not there is an incident edge. I think if you make the appearance of nodes and edges in the file completely invariant, the layout ordering will not change either.