4
$\begingroup$

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 }
}

enter image description here

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)

enter image description here

  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]]
    

enter image description here

  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]
    

enter image description here

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?
$\endgroup$
  • 1
    $\begingroup$ Re: point 3, you can use AbsoluteOptions[graph, VertexCoordinates] to retrieve the actual coordinates used by a Graph[] object. $\endgroup$ – J. M. is away Sep 28 '18 at 13:16
  • 1
    $\begingroup$ @J.M.issomewhatokay. for that you can use GraphEmbedding. $\endgroup$ – Vitaliy Kaurov Sep 28 '18 at 13:21
  • $\begingroup$ @Vitaliy, forgot about that. :) Tho, one would hope GraphEmbedding was in the "See Also" for GraphLayout... $\endgroup$ – J. M. is away Sep 28 '18 at 13:22
  • 2
    $\begingroup$ to change the orientation in approach use GraphLayout -> {"LayeredDigraphEmbedding", "Orientation" -> Left} $\endgroup$ – kglr Sep 28 '18 at 13:32
  • 2
    $\begingroup$ If you can export coordinates from your DOT implementation you can use them in Graph via VertexCoordinates ->. $\endgroup$ – Vitaliy Kaurov Sep 28 '18 at 13:32
2
$\begingroup$
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]

enter image description here

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 ]

enter image description here

$\endgroup$
  • $\begingroup$ 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. $\endgroup$ – Hotschke Sep 29 '18 at 11:37
  • $\begingroup$ 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? $\endgroup$ – Hotschke Oct 2 '18 at 13:48
  • 1
    $\begingroup$ @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 ] $\endgroup$ – kglr Oct 3 '18 at 0:33
  • $\begingroup$ Thanks again! You are helping me quite a bit, not the first time. I am really glad that the stackexchange network makes this possible. $\endgroup$ – Hotschke Oct 3 '18 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.