186

In the DOT language for GraphViz, I'm trying to represent a dependency diagram. I need to be able to have nodes inside a container and to be able to make nodes and/or containers dependent on other nodes and/or containers.

I'm using subgraph to represent my containers. Node linking works just fine, but I can't figure out how to connect subgraphs.

Given the program below, I need to be able to connect cluster_1 and cluster_2 with an arrow, but anything I've tried creates new nodes instead of connecting the clusters:

digraph G {

    graph [fontsize=10 fontname="Verdana"];
    node [shape=record fontsize=10 fontname="Verdana"];

    subgraph cluster_0 {
        node [style=filled];
        "Item 1" "Item 2";
        label = "Container A";
        color=blue;
    }

    subgraph cluster_1 {
        node [style=filled];
        "Item 3" "Item 4";
        label = "Container B";
        color=blue;
    }

    subgraph cluster_2 {
        node [style=filled];
        "Item 5" "Item 6";
        label = "Container C";
        color=blue;
    }

    // Renders fine
    "Item 1" -> "Item 2";
    "Item 2" -> "Item 3";

    // Both of these create new nodes
    cluster_1 -> cluster_2;
    "Container A" -> "Container C";
}

enter image description here

Potherca
  • 10,796
  • 5
  • 60
  • 79
Winston Smith
  • 20,241
  • 10
  • 55
  • 74
  • 3
    I'm having the same problem, yet they have a natural example where subgraphs are acting like nodes, http://www.graphviz.org/content/fdpclust. – nlucaroni Jun 17 '11 at 18:26
  • 1
    @nlucaroni i wonder if this problem is solved. this example gives me wrong graph: edges connect centers of subgraph. don't you know how to make it to work like in the example? – k102 Feb 14 '12 at 09:14
  • 1
    @k102, I do know. Check out that page again; it says you need to use `fdp`. The linked example, and the one above both work (the last line in the example here needs to use the subgraph names not the label and it might be nice to include line lengths for the graph); it's a little tight as is). – nlucaroni Feb 14 '12 at 18:24
  • 1
    @nlucaroni Using `fdp` v2.28.0 and copy/pasting the source from the example the lines connect to the center of the subgraph, not to the edges. If you open the .dot in OmniGraffle they are properly connected, while `neato` and `dot` both create superfluous nodes for the cluster. – Phrogz May 08 '12 at 04:09
  • Related Graphviz bug report: https://gitlab.com/graphviz/graphviz/-/issues/745 and others linked to from there. – Peter Nowee Sep 13 '20 at 11:27
  • The FDP cluster is now located at https://www.graphviz.org/Gallery/undirected/fdpclust.html – Ed. Apr 05 '21 at 16:08

3 Answers3

219

The DOT user manual gives the following example of a graph with clusters with edges between clusters:

IMPORTANT: The initial compound=true statement is required.

digraph G {
  compound=true;
  subgraph cluster0 {
    a -> b;
    a -> c;
    b -> d;
    c -> d;
  }
  subgraph cluster1 {
    e -> g;
    e -> f;
  }
  b -> f [lhead=cluster1];
  d -> e;
  c -> g [ltail=cluster0,lhead=cluster1];
  c -> e [ltail=cluster0];
  d -> h;
}

... and edges between nodes and clusters:

enter image description here

André C. Andersen
  • 7,351
  • 3
  • 43
  • 62
High Performance Mark
  • 74,067
  • 7
  • 97
  • 147
  • 21
    Thanks - that works, but it really feels like an ugly hack. I'm **hoping** I don't have a scenario where I have a container with no nodes. – Winston Smith Jan 06 '10 at 11:24
  • 5
    In case anyone is interested in, this can cause positioning problems if you have labelled links (edges). While the head or the tail of the edge may be hidden beneath a cluster, the label is still positioned at the midpoint, meaning some edge labels appear to be floating over a cluster instead of being positioned by the edge itself. – Winston Smith Jan 08 '10 at 11:12
  • This answer did help me out, now I just have to work out whether I can use neato to lay the clusters out :) – Chris Nash Mar 03 '11 at 02:07
  • 70
    @WinstonSmith: Old question, but I had a similar problem and solved it with an invisible dummy node per cluster, that can be linked to even if the cluster is empty otherwise. `DUMMY_0 [shape=point style=invis]` – DevSolar Jun 25 '12 at 07:36
  • 2
    I found my inter-cluster edges to be collapsed to just arrow heads, when using clusters that are only vertically connected. I fixed that with [minlen](http://www.graphviz.org/doc/info/attrs.html#d:minlen)=1 on the edges. c -> g [ltail=cluster0,lhead=cluster1,minlen=1]; – Freenerd Jan 27 '14 at 14:40
  • 3
    Here's the link to the manual with the example: http://graphviz.org/Documentation/dotguide.pdf (page 30). – Kirill Bulygin Apr 30 '17 at 15:59
  • modification include the graph-level attribute `compound=true;` and the node-level attributes `ltail=cluster_...` and `lhead=cluster_...`. – Franklin Piat Mar 23 '18 at 09:41
  • On viz-js.com, this works only for `dot` engine. For `fdp` engine it does not work, however, the direct connection between node and cluster (without need for `compound` and `lhead`/`ltail` attributes) works instead. Source: http://graphviz.996277.n3.nabble.com/ltail-lhead-attribute-in-fdp-interpreter-regd-tp1042p1043.html – Tomáš Záluský Oct 06 '20 at 07:27
95

For ease of reference the solution described in HighPerformanceMark's answer, applied directly to the original question, looks like this:

digraph G {

    graph [fontsize=10 fontname="Verdana" compound=true];
    node [shape=record fontsize=10 fontname="Verdana"];

    subgraph cluster_0 {
        node [style=filled];
        "Item 1" "Item 2";
        label = "Container A";
        color=blue;
    }

    subgraph cluster_1 {
        node [style=filled];
        "Item 3" "Item 4";
        label = "Container B";
        color=blue;
    }

    subgraph cluster_2 {
        node [style=filled];
        "Item 5" "Item 6";
        label = "Container C";
        color=blue;
    }

    // Edges between nodes render fine
    "Item 1" -> "Item 2";
    "Item 2" -> "Item 3";

    // Edges that directly connect one cluster to another
    "Item 1" -> "Item 3" [ltail=cluster_0 lhead=cluster_1];
    "Item 1" -> "Item 5" [ltail=cluster_0 lhead=cluster_2];
}

The compound=true in the graph declaration is vital. That produces output:

graph with connected clusters

Note that I changed the edges to reference nodes within the cluster, added the ltail and lhead attributes to each edge, specifying the cluster name, and added the graph-level attribute 'compound=true'.

Regarding the worry that one might want to connect a cluster with no nodes inside it, my solution has been to always add a node to every cluster, rendered with style=plaintext. Use this node to label the cluster (instead of the cluster's built-in "label" attribute, which should be set to the empty string (in Python, label='""'). This means I'm no longer adding edges that connect clusters directly, but it works in my particular situation.

qfazille
  • 1,337
  • 12
  • 25
Jonathan Hartley
  • 13,922
  • 8
  • 71
  • 77
  • 24
    Note: 'graph [fontsize=10 fontname="Verdana" compound=true];' is essential - if you miss that linking to ltail/lhead does not work. – s.Daniel Dec 17 '12 at 16:34
  • 1
    @JonathanHartley, As per your last paragraph, is there any way to center that node right in the middle of the cluster? – Pacerier Jul 16 '14 at 18:29
  • also the name of the cluster should not start by a capital letter – JCLL Sep 09 '16 at 08:29
  • 7
    @s.Daniel It's just the compound=true; which is required – Dr. Max Völkel Nov 26 '16 at 20:20
  • Instead of reset lhead and ltail when link "Item 1" -> "Item 3" , how do I link cluster_0 and cluster_1 with meanful code ? I meam, make `cluster_0 -> cluster_1` present as you output. Because there may be many item in cluster_0 link to other many items in cluster_1 ( many to many or one to many ). It would be good to just link two. – Mithril Dec 06 '17 at 07:04
  • @Mithral I agree that would be nice. I am no expert, but as far as I know, it cannot be done directly in DOT. I think whatever generates your DOT would need to see that there are many such links, and boil them down to just one link in the DOT file, choosing an arbitrary two nodes within the clusters. If the thing that generates the DOT file is you, then this is your job. :-( – Jonathan Hartley Dec 06 '17 at 17:48
12

Make sure you are using fdp layout for the file. I don't think neato supports clusters.

mihajlv
  • 2,145
  • 3
  • 33
  • 55
  • 3
    I too have experientially found that the `neato` engine does not support clusters.. I'm not sure if this is a bug or not.. – Ross Rogers Jul 30 '13 at 22:43