Draw subgraphs independently

I’m trying to draw a graph with many subgraphs, but due the global ranking system, the height of each subgraph is influenced by every other subgraph’s layout. Is it possible to decouple them so this doesn’t happen?

This is what I currently have:

digraph G {
    subgraph cluster_outer {
        subgraph cluster_1 {
            a [shape=rect, height=1.5]
            b [shape=rect]
            a -> b
        }
        subgraph cluster_2 {
           c [shape=rect]
           d [shape=rect]
           c -> d [color=red]
           color=red
        }
    }
}

Notice how the length of the edge c ->d is longer than it needs to be, and the subgraph containing c and d is taller than it needs to be.

Do you ever have edges connecting the clusters, or are the clusters fully independent? If independent, should they be arranged horizontally, vertically, in a grid, or magically (my favorite)?

There is a graph attribute for DOT named clusterrank which sounds like it would activate a feature allowing for independent layouts in the clusters. I tried adding it to your DOT file, but unfortunately it had no effect on the graph.

Here is what the documentation says:

clusterrank - Mode used for handling clusters. If clusterrank is “local”, a subgraph whose name begins with “cluster” is given special treatment. The subgraph is laid out separately, and then integrated as a unit into its parent graph, with a bounding rectangle drawn about it. If the cluster has a label parameter, this label is displayed within the rectangle. Note also that there can be clusters within clusters. At present, the modes “global” and “none” appear to be identical, both turning off the special cluster processing.

Maybe I read too much into that description, or maybe clusterrank has a bug. Perhaps someone with more knowledge of clusterrank can provide some more insights.

If I’m not mistaken, clusterrank=local allows the level (rank) assignment algorithm for a cluster to ignore external edges. The problem is that ranks are still a global property; if some node anywhere in the layout causes a particular level to be higher, that’s a global property that will apply to all nodes and clusters that include that rank.

I don’t believe there’s any workaround.

Sorry about sounding like an infinite audio loop but addressing this would be an interesting project. One could say a lot about allowing fine-grained multilevel nodes in layered graphs (Gordon Woodhull had this working in dynagraph.org if I’m not mistaken) and it would be very desirable to allow nesting generalized subgraphs as big nodes in other layouts, but doing either of these things would affect a lot of the layout code; it’s more than a summer project. Hoping future generations can learn from our mistakes.

Here is a “devil-in-the-details” solution.
Chop your graph into multiple graphs, each cluster into its own graph. Run each (new) graph through dot -Tdot to determine layouts of each separately. Run those results through gvpack (https://www.graphviz.org/pdf/gvpack.1.pdf) to combine them into a single graph. And finally hand that to neato -n2 to produce an output file. Like so:

S="";
for f in sub[12].gv;  # sub1.gv and sub2.gv are the two created graphs
do 
  T=dot; 
  F=`basename $f .gv`;
  dot -T$T $f >$F.$T; 
  S="$S  $F.$T"; 
done; 
gvpack -array_i3  $S |
  neato -n2 -Tpng >O.png

Giving:
O

I’m not sure what you mean by “magically”.

In my actual use case, clusters are not directly connected (I believe that’s not possible), but nodes are connected between clusters. It is always the top nodes of one cluster being connected to the bottom nodes of another cluster such that the clusters can be seen to form a DAG.

So the arrangement of the clusters is a DAG, if you were to look at it as a bunch of interconnected subgraphs. Whether two clusters are arranged horizontally or vertically depends on their relationship in the DAG.

To make matters a bit more complicated, I’m also working with some elaborate nesting.

Here’s an example (my project generates diagrams like this). Notice how the height of the “Led” subgraph is dictated by the height of the “Led\n2” subgraph (that is the problem I’m trying to solve).

graphviz (1)

Thanks. I had feeling the only solution would involve calling Graphviz multiple times. Unfortunately, I need to generate self-contained DOT files.

[sorry for the confusing “magical” comment. It was not an editorial, I keep thinking (incorrectly) that I am a comedian]
As you noted, you are being caught on the universal nature of dot’s ranking algorithm. I can’t think of a one-step work-around. A general solution would almost undoubtedly be a quite challenging enhancement to dot. I kind of, maybe think I can envision an enhancement that would solve the more special-case you describe (clusters only connected via top/bottom nodes). But even it would be fiddly at best.
The one thing I would note is that for the “led” example you link to, expanding node width and making the “Led\n2” text fit on one line would solve the problem - locally.

p.s. I like your graph’s look/style

It is possible to connect clusters. You can connect nodes to clusters, and clusters to clusters. There is a good writeup here on StackOverflow of how to do it.

The top example here has node, cluster links: Sample Pipeline / Gordon Smith / Observable (observablehq.com)

It is a shame this can’t be done, but thank you anyway for your explanations.

A solution would be to use ranksep=0.1 and change the len of specific edges, with minlen.

digraph G {
ranksep=0.1
    subgraph cluster_outer {
        subgraph cluster_1 {
            a [shape=rect, height=1.5]
            b [shape=rect]
            a -> b[minlen=2]
        }
        
        subgraph cluster_2 {
            
           c [shape=rect]
           d [shape=rect]
           c -> d [color=red]
           color=red
        }
    }
}

graphviz

I don’t believe that’s a very robust solution. If I take away one subgraph, it becomes obvious that ranksep really is 0.1.

image

digraph G {
    ranksep=0.1
    subgraph cluster_outer {
        subgraph cluster_2 {
          c [shape=rect]
          d [shape=rect]
          c -> d [color=red]
          color=red
        }
    }
}

I’m really looking for something that will allow me to automatically generate graphs, sometimes with dozens of nested and interconnected subgraphs.

Thank you anyway.