top-down subgraphs, left-right inside subgraphs

Reproducing particular graph layouts usually can be achieved with:

  • Invisible nodes and edges
  • rank constraints

Here's how I reproduced your graph - or at least a part of it:

digraph g {
    rankdir="LR";
    node[shape = circle, fontsize=14];
    fontsize=18;
    labeljust="l";

    edge[style=invis, fontsize=12];

    { rank=same;
        0 [style = invis];
        01 [style = invis];
        02 [style=invis];
        0 -> 01 -> 02;
    }

    subgraph clusterA {
        "0A" -> "1A" -> "2A";
        "2A" -> "0A" [label=".", constraint=false, style=solid];
        label="A";
    }

    subgraph clusterB {
        "0B" -> "1B" -> "2B";
        "2B" -> "0B" [label=".", constraint=false, style=solid];
        label="B";
    }

    subgraph clusterC {
        "0C" -> "1C" -> "2C";
        "2C" -> "0C" [label=".", constraint=false, style=solid];
        label="C";
    }

    0 -> "0A"[style=solid];
    01 -> "0B"[style=invis];
    02 -> "0C"[style=invis];

    // edges between clusters
    edge[constraint=false, style=solid];
    "0A" -> "1B" [label=a]
    "1A" -> "2B" [label=a]
    "0B" -> "1C" [label=b]
    "1B" -> "2C" [label=b]
}

This solution is not very intuitive. A couple of points to achieve this:

  • I chose rankdir="LR" which resulted in nicer edges than TB, though it does not really correspond with the direction of the graph
  • Invisible nodes and edges are use for the top rank nodes (0, 01, 02) in order to have the clusters align left.
  • The (invisible) top nodes are forced to the same rank and are linked by invisible edges - this will ensure that the clusters linked to each node appear in the correct order.

The result is:

graphviz output


It looks like rank=same might be a cleaner solution. Take a look at Placing clusters on the same rank in Graphviz.

You can also use 'constraint=false' and invisible edges to carefully control node rank. This is basically the same answer as the above.

digraph G {
    newrank=true; // rank without respect to cluster
    rankdir="LR"; 
    node [shape = circle]
    
    subgraph clusterA {
        a0 -> a1 -> a2 [style = invis] // set node order in cluster
        a2 -> a0 [constraint=false] //don't use this edge for ranking
    }

    subgraph clusterB {
        b0 -> b1 -> b2 [style = invis]
        b2 -> b0  [constraint=false]
    }
    
    a0 -> b1 [constraint=false]
    a1 -> b2 [constraint=false]
    
}

enter image description here