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

1 Answer 1

5
$\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 to 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$
4
  • $\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
    Commented Sep 29, 2018 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
    Commented Oct 2, 2018 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
    Commented Oct 3, 2018 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
    Commented Oct 3, 2018 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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