Search code examples
graphvizsubgraph

top-down subgraphs, left-right inside subgraphs


I'd like to have my graph looks like this:

But I can only get this:

The problem is, rankdir does not work in subgraph. So, how to emulate it?

The code:

digraph G {
    node [shape = circle]
    0 [style = invis]

    0 -> "0A"

    subgraph clusterA {
        label=A
        "0A"
        "1A"
        "2A" -> "0A" [label=•]
    }

    subgraph clusterB {
        label=B
        "0B"
        "1B"
        "2B" -> "0B" [label=•]
    }

        subgraph clusterC {
        label=C
        "0C"
        "1C"
        "2C" -> "0C" [label=•]
    }

    subgraph clusterD {
        label=D
        "0D"
        "1D"
        "2D" -> "0D" [label=•]
    }

    subgraph clusterE {
        label=E
        "0E"
        "1E"
        "2E" -> "0E" [label=•]
    }

    subgraph clusterF {
        label=F
            {node [shape = doublecircle] "0F" "1F"}
        "2F" -> "0F" [label=•]
    }

    "0A" -> "1B" [label=a]
    "1A" -> "2B" [label=a]
    "0B" -> "1C" [label=b]
    "1B" -> "2C" [label=b]
    "0C" -> "1D" [label=c]
    "1C" -> "2D" [label=c]
    "0D" -> "1E" [label=d]
    "1D" -> "2E" [label=d]
    "0E" -> "1F" [label=e]
    "1E" -> "2F" [label=e]
}

Solution

  • 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