# Layered Graph Drawing: Specify Layer for Nodes

I was trying to recreate following graphviz graph with Mathematica 11.2:

graph {
rankdir=LR;
a -- { b c d }; b -- { c e }; c -- { e f }; d -- { f g }; e -- h; f -- { h i j g };
g -- k; h -- { o l }; i -- { l m j }; j -- { m n k }; k -- { n r }; l -- { o m };
m -- { o p n }; n -- { q r }; o -- { s p }; p -- { s t q }; q -- { t r }; r -- t;
s -- z; t -- z;
{ rank=same; b, c, d }
{ rank=same; e, f, g }
{ rank=same; h, i, j, k }
{ rank=same; l, m, n }
{ rank=same; o, p, q, r }
{ rank=same; s, t }
} As you can see, you can specify in the dot language that certain nodes have the same 'rank', i.e. are located on the same layer.

How can I do this in Mathematica 11.2?

IMO this question has already been asked:

However, both questions are not satisfying:

1. The first one gives no example graph and hence never got answered.
2. The second has gotten an answer specific to the example (actual manual placement).

Furthermore, both questions are 4 years old, i.e. new Mathematica versions have appeared.

I have tried three approaches in Mathematica.

1. Using newer Graph command with GraphLayout -> {"LayeredEmbedding"}

Graph[{"a" <-> "b", "a" <-> "c", "a" <-> "d", "b" <-> "c",
"b" <-> "e", "c" <-> "e", "c" <-> "f", "d" <-> "f", "d" <-> "g",
"e" <-> "h", "f" <-> "h", "f" <-> "i", "f" <-> "j", "f" <-> "g",
"g" <-> "k", "h" <-> "o", "h" <-> "l", "i" <-> "l", "i" <-> "m",
"i" <-> "j", "j" <-> "m", "j" <-> "n", "j" <-> "k", "k" <-> "n",
"k" <-> "r", "l" <-> "o", "l" <-> "m", "m" <-> "o", "m" <-> "p",
"m" <-> "n", "n" <-> "q", "n" <-> "r", "o" <-> "s", "o" <-> "p",
"p" <-> "s", "p" <-> "t", "p" <-> "q", "q" <-> "t", "q" <-> "r",
"r" <-> "t", "s" <-> "z", "t" <-> "z"},
GraphLayout -> {"LayeredEmbedding", "Orientation" -> Left},
VertexShapeFunction -> "Circle", VertexSize -> {.2, .1},
VertexStyle -> White, VertexLabels -> Placed["Name", Center],
EdgeStyle -> Black,
EdgeShapeFunction -> {
"p" <-> "q" -> {"CurvedArc", "Curvature" -> -0.66},
"p" <-> "t" -> {"CurvedArc", "Curvature" -> -0.66},
"m" <-> "o" -> {"CurvedArc", "Curvature" -> 1}
}]


UPDATED:

• Orientation (comment by kglr)
• Specified edges which should be curved otherwise they are hidden (answer by kglr) 1. Using older GraphPlot command with promising feature VertexCoordinateRules:

GraphPlot[{"a" -> "b", "a" -> "c", "a" -> "d", "b" -> "c", "b" -> "e", "c" -> "e",
"c" -> "f", "d" -> "f", "d" -> "g", "e" -> "h", "f" -> "h", "f" -> "i", "f" -> "j",
"f" -> "g", "g" -> "k", "h" -> "o", "h" -> "l", "i" -> "l", "i" -> "m", "i" -> "j",
"j" -> "m", "j" -> "n", "j" -> "k", "k" -> "n", "k" -> "r", "l" -> "o", "l" -> "m",
"m" -> "o", "m" -> "p", "m" -> "n", "n" -> "q", "n" -> "r", "o" -> "s", "o" -> "p",
"p" -> "s", "p" -> "t", "p" -> "q", "q" -> "t", "q" -> "r", "r" -> "t", "s" -> "z",
"t" -> "z"},
VertexLabeling -> True, DirectedEdges -> False,
VertexCoordinateRules -> Flatten[{
(# -> {0, Automatic}) & /@ {"a"},
(# -> {1, Automatic}) & /@ {"b", "c", "d"},
(# -> {2, Automatic}) & /@ {"e", "f", "g"},
(# -> {3, Automatic}) & /@ {"h", "i", "j", "k"},
(# -> {4, Automatic}) & /@ {"l", "m", "n"},
(# -> {5, Automatic}) & /@ {"o", "p", "q", "r"},
(# -> {6, Automatic}) & /@ {"s", "t"},
(# -> {7, Automatic}) & /@ {"z"}
}, 1]] 1. Using older LayeredGraphPlot (only supports VertexCoordinateRules with SpringElectricalEmbedding)

LayeredGraphPlot[{"a" -> "b", "a" -> "c", "a" -> "d", "b" -> "c", "b" -> "e",
"c" -> "e", "c" -> "f", "d" -> "f", "d" -> "g", "e" -> "h", "f" -> "h", "f" -> "i",
"f" -> "j", "f" -> "g", "g" -> "k", "h" -> "o", "h" -> "l", "i" -> "l", "i" -> "m",
"i" -> "j", "j" -> "m", "j" -> "n", "j" -> "k", "k" -> "n", "k" -> "r", "l" -> "o",
"l" -> "m", "m" -> "o", "m" -> "p", "m" -> "n", "n" -> "q", "n" -> "r", "o" -> "s",
"o" -> "p", "p" -> "s", "p" -> "t", "p" -> "q", "q" -> "t", "q" -> "r", "r" -> "t",
"s" -> "z", "t" -> "z"},
Left, DirectedEdges -> False, VertexLabeling -> True] Each with limitations, but most importantly only VertexCoordinateRules seem to be a possibility to have something similar as in the dot language.

Issues/Questions

1. How can I switch the orientation in my first approach? Also a few edges are not visible, e.g. edge p--q (a tiny bit thicker edges p--s and s--q).
2. For the most promising second approach, the edge h--o is almost not visible: h--o is bended in the graphviz version (as done in approach 3). How I can I fix this?
3. I could imagine to use the first or third approach to generate a first layout and then manually fix the x-value of the nodes. How could I do this?
• Re: point 3, you can use AbsoluteOptions[graph, VertexCoordinates] to retrieve the actual coordinates used by a Graph[] object. – J. M. is away Sep 28 '18 at 13:16
• @J.M.issomewhatokay. for that you can use GraphEmbedding. – Vitaliy Kaurov Sep 28 '18 at 13:21
• @Vitaliy, forgot about that. :) Tho, one would hope GraphEmbedding was in the "See Also" for GraphLayout... – J. M. is away Sep 28 '18 at 13:22
• to change the orientation in approach use GraphLayout -> {"LayeredDigraphEmbedding", "Orientation" -> Left} – kglr Sep 28 '18 at 13:32
• If you can export coordinates from your DOT implementation you can use them in Graph via VertexCoordinates ->. – Vitaliy Kaurov Sep 28 '18 at 13:32

## 1 Answer

edges = {"a"<->"b","a"<->"c","a"<->"d","b"<->"c","b"<->"e","c"<->"e","c"<->"f",
"d"<->"f", "d"<->"g","e"<->"h","f"<->"h","f"<->"i","f"<->"j","f"<->"g","g"<->"k",
"h"<->"o","h"<->"l", "i"<->"l","i"<->"m","i"<->"j","j"<->"m","j"<->"n","j"<->"k",
"k"<->"n","k"<->"r","l"<->"o", "l"<->"m","m"<->"o","m"<->"p","m"<->"n","n"<->"q",
"n"<->"r","o"<->"s","o"<->"p","p"<->"s", "p"<->"t","p"<->"q","q"<->"t","q"<->"r",
"r"<->"t","s"<->"z","t"<->"z"};


Using the sorted list of vertices as the first argument in Graph and using {"LayeredigraphEmbedding", "Orientation"->Left} as the setting for the option GraphLayout and using "CurvedArc" as EdgeShapeFunction solves part 1 of the question:

vertices = Union[Flatten[List @@@ edges]];

Graph[vertices, edges,
GraphLayout -> {"LayeredEmbedding", "Orientation" -> Left},
VertexShapeFunction -> "Circle", VertexSize -> .3,
VertexStyle -> White, VertexLabels -> Placed["Name", Center],
EdgeStyle -> Black,
EdgeShapeFunction -> "CurvedArc",
ImageSize -> Large] A more convenient approach is too use "MultipartiteEmbedding" and specify the number of vertices in each layer as the value of the sub-option "VertexPartition":

layers = {1, 3, 3, 4, 3, 4, 2, 1};
Graph[vertices, edges,
GraphLayout -> {"MultipartiteEmbedding",  "VertexPartition" -> layers},
VertexShapeFunction -> "Circle",
VertexSize -> .3, VertexStyle -> White,
VertexLabels -> Placed["Name", Center], EdgeStyle -> Black,
ImageSize -> Large ] • Thanks for your great answer! IMO the MultipartiteEmbedding with layer specs is really the way to go here. I am not sure whether sooner or later I would have realized that I should provide the list of vertices alphabetically sorted to get them in the correct layer. I am really happy that you pointed out this 'detail'. BTW I updated the first approach by applying the undocumented CurvedArc with a manually adjusted curvature only to the required edges. I noticed that I had overlooked several overlapping edges. Therefore I guess one should not recommend this approach. – Hotschke Sep 29 '18 at 11:37
• Now I have a graph where I want to use MultipartiteEmbedding with layers as you have described but I want a top to bottom orientation. I tried to use "Orientation" -> Top. However, I get the error Graph::moptx: Method option Orientation in MultipartiteLayout is not one of {VertexPartition}.. This looks like Mathematica does not support this. Do you know again where I could look for a solution? – Hotschke Oct 2 '18 at 13:48
• @Hotschke, you can use SetProperty[g, VertexCoordinates -> RotationTransform[-Pi/2][GraphEmbedding[g]]] where g = Graph[vertices, edges, GraphLayout -> {"MultipartiteEmbedding", "VertexPartition" -> layers}, VertexShapeFunction -> "Circle", VertexSize -> .3, VertexStyle -> White, VertexLabels -> Placed["Name", Center], EdgeStyle -> Black, ImageSize -> Large ] – kglr Oct 3 '18 at 0:33
• Thanks again! You are helping me quite a bit, not the first time. I am really glad that the stackexchange network makes this possible. – Hotschke Oct 3 '18 at 11:18