GraphViz - How to connect subgraphs?
Asked Answered
L

4

231

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

Large answered 6/1, 2010 at 9:45 Comment(6)
I'm having the same problem, yet they have a natural example where subgraphs are acting like nodes, graphviz.org/content/fdpclust.Monroy
@Monroy 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?Chili
@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).Monroy
@Monroy 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.Darlinedarling
Related Graphviz bug report: gitlab.com/graphviz/graphviz/-/issues/745 and others linked to from there.Pristine
The FDP cluster is now located at graphviz.org/Gallery/undirected/fdpclust.htmlReceptacle
T
275

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

Tricrotic answered 6/1, 2010 at 10:3 Comment(10)
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.Large
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.Large
This answer did help me out, now I just have to work out whether I can use neato to lay the clusters out :)Interflow
@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]Extended
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=1 on the edges. c -> g [ltail=cluster0,lhead=cluster1,minlen=1];Amanuensis
Here's the link to the manual with the example: graphviz.org/Documentation/dotguide.pdf (page 30).Qadi
modification include the graph-level attribute compound=true; and the node-level attributes ltail=cluster_... and lhead=cluster_....Rump
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: graphviz.996277.n3.nabble.com/…Negress
+1. By the way as one of the top users of the FORTRAN tag, I thought you'd be delighted to see this: mattermodeling.stackexchange.com/q/6252/5 on a brand new SE site!Contradiction
By the way, stupid error of mine, but cluster1, cluster0 is not just names, the names are necessary to create these boxes...Arrearage
F
112

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

It is vital to:

  • Use compound=true in the graph declaration
  • Name the subgraphs cluster_*
  • According to a comment below, not start the cluster name with a capital letter. I'm not precisely sure what that means (eg. does it forbid cluster_A?), but wanted to highlight it here in case it is a gotcha.

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 (not shown above) 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.

Fellow answered 29/5, 2012 at 16:11 Comment(6)
Note: 'graph [fontsize=10 fontname="Verdana" compound=true];' is essential - if you miss that linking to ltail/lhead does not work.Strike
@JonathanHartley, As per your last paragraph, is there any way to center that node right in the middle of the cluster?Mcspadden
also the name of the cluster should not start by a capital letterBlastogenesis
@Strike It's just the compound=true; which is requiredElsewhere
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.Narghile
@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. :-(Fellow
I
15

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

Illaffected answered 11/10, 2012 at 23:14 Comment(2)
I too have experientially found that the neato engine does not support clusters.. I'm not sure if this is a bug or not..Burrstone
Looks like this is no longer the case.Pointsman
P
2

Make sure that compound=true in the digraph options (reference):

digraph {
  compound=true;

  subgraph cluster_a {
    label="Cluster A";
    node1; node3; node5; node7;
  }
  subgraph cluster_b {
    label="Cluster B";
    node2; node4; node6; node8;
  }

  node1 -> node2 [label="1"];
  node3 -> node4 [label="2" ltail="cluster_a"];
  
  node5 -> node6 [label="3" lhead="cluster_b"];
  node7 -> node8 [label="4" ltail="cluster_a" lhead="cluster_b"];
}

enter image description here

Paphlagonia answered 18/7, 2022 at 11:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.