0

I've got a graph with clusters. In this example the graph is generated "tilted" to the left. The graph is generated automatically with a strategy: to combine clusters I draw an edge between the last node of previous subgraph to the first node of subsequent subgraph. How to make the subgraphs to be aligned vertically in this scenario?

digraph {
    compound=true
    fontname="Verdana"
    fontsize=12
    nodesep = 1
    ranksep = 1.5
    node[shape=folder, style="filled,solid", color=green4, fontsize=12]
    subgraph "cluster 1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <l1>
        n0 [label=<n0> fillcolor=limegreen]
        n1 [label=<n1> fillcolor=limegreen]
        n2 [label=<n2> fillcolor=limegreen]
        n3 [label=<n3> fillcolor=limegreen]
        n4 [label=<n4> fillcolor=limegreen]
        n5 [label=<n5> fillcolor=limegreen]
        n0 -> n1
        n1 -> n2
        n2 -> n3
        n3 -> n4
        n4 -> n5
    }
    subgraph "cluster c1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c1>
        n6 [label=<n6> fillcolor=limegreen]
    }
    subgraph "cluster c2" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c2>
        n7 [label=<n7> fillcolor=limegreen]
        n8 [label=<n8> fillcolor=limegreen]
        n9 [label=<n9> fillcolor=limegreen]
        n10 [label=<n10> fillcolor=limegreen]
    }
    subgraph "cluster c3" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c3>
        n11 [label=<n11> fillcolor=limegreen]
        n12 [label=<n12> fillcolor=limegreen]
        n13 [label=<n13> fillcolor=limegreen]
        n14 [label=<n14> fillcolor=limegreen]
    }
    subgraph "cluster c4" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c4>
        n15 [label=<n15> fillcolor=limegreen]
        n16 [label=<n16> fillcolor=limegreen]
    }
    subgraph "cluster c5" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c5>
        n17 [label=<n17> fillcolor=limegreen]
        n18 [label=<n18> fillcolor=limegreen]
        n17 -> n18
    }
    subgraph "cluster c6" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c6>
        n48 [label=<n48> fillcolor=limegreen]
        n49 [label=<n49> fillcolor=limegreen]
        n50 [label=<n50> fillcolor=limegreen]
        n51 [label=<n51> fillcolor=limegreen]
        n48 -> n49
        n49 -> n50
        n50 -> n51
    }
    subgraph "cluster c7" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c7>
        n52 [label=<n52> fillcolor=limegreen]
        n53 [label=<n53> fillcolor=limegreen]
    }
    subgraph "cluster c8" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c8>
        n54 [label=<n54> fillcolor=limegreen]
        n55 [label=<n55> fillcolor=limegreen]
    }
    n6 -> n7 [ltail="cluster c1", lhead="cluster c2"]
    n10 -> n11 [ltail="cluster c2", lhead="cluster c3"]
    n18 -> n15 [ltail="cluster c5", lhead="cluster c4"]
    n14 -> n17 [ltail="cluster c3", lhead="cluster c5"]
    n55 -> n48 [ltail="cluster c8", lhead="cluster c6"]
    n16 -> n52 [ltail="cluster c4", lhead="cluster c7"]
    n53 -> n54 [ltail="cluster c7", lhead="cluster c8"]
}

This is the genarated graph with strategy: last to first

enter image description here

This is something I would like to get:

enter image description here

Example with strategy last to last:

enter image description here

dot for last-to-last

digraph {
    compound=true
    fontname="Verdana"
    fontsize=12
    nodesep = 1
    ranksep = 1.5
    node[shape=folder, style="filled,solid", color=green4, fontsize=12]
    subgraph "cluster c0" {
        style=filled color=green4 fillcolor=lightgreen
        label = <l1>
        n0 [label=<n0> fillcolor=limegreen]
        n1 [label=<n1> fillcolor=limegreen]
        n2 [label=<n2> fillcolor=limegreen]
        n3 [label=<n3> fillcolor=limegreen]
        n4 [label=<n4> fillcolor=limegreen]
        n5 [label=<n5> fillcolor=limegreen]
        n0 -> n1
        n1 -> n2
        n2 -> n3
        n3 -> n4
        n4 -> n5
    }
    subgraph "cluster c1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c1>
        n6 [label=<n6> fillcolor=limegreen]
    }
    subgraph "cluster c2" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c2>
        n7 [label=<n7> fillcolor=limegreen]
        n8 [label=<n8> fillcolor=limegreen]
        n9 [label=<n9> fillcolor=limegreen]
        n10 [label=<n10> fillcolor=limegreen]
    }
    subgraph "cluster c3" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c3>
        n11 [label=<n11> fillcolor=limegreen]
        n12 [label=<n12> fillcolor=limegreen]
        n13 [label=<n13> fillcolor=limegreen]
        n14 [label=<n14> fillcolor=limegreen]
    }
    subgraph "cluster c4" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c4>
        n15 [label=<n15> fillcolor=limegreen]
        n16 [label=<n16> fillcolor=limegreen]
    }
    subgraph "cluster c5" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c5>
        n17 [label=<n17> fillcolor=limegreen]
        n18 [label=<n18> fillcolor=limegreen]
        n17 -> n18
    }
    subgraph "cluster c6" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c6>
        n48 [label=<n48> fillcolor=limegreen]
        n49 [label=<n49> fillcolor=limegreen]
        n50 [label=<n50> fillcolor=limegreen]
        n51 [label=<n51> fillcolor=limegreen]
        n48 -> n49
        n49 -> n50
        n50 -> n51
    }
    subgraph "cluster c7" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c7>
        n52 [label=<n52> fillcolor=limegreen]
        n53 [label=<n53> fillcolor=limegreen]
    }
    subgraph "cluster c8" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c8>
        n54 [label=<n54> fillcolor=limegreen]
        n55 [label=<n55> fillcolor=limegreen]
    }
    n6 -> n10 [ltail="cluster c1", lhead="cluster c2"]
    n10 -> n14 [ltail="cluster c2", lhead="cluster c3"]
    n18 -> n16 [ltail="cluster c5", lhead="cluster c4"]
    n14 -> n18 [ltail="cluster c3", lhead="cluster c5"]
    n55 -> n51 [ltail="cluster c8", lhead="cluster c6"]
    n16 -> n53 [ltail="cluster c4", lhead="cluster c7"]
    n53 -> n55 [ltail="cluster c7", lhead="cluster c8"]
}
Artyum
  • 169
  • 2
  • 13
  • You have e.g. the link `n10 -> n11 [ltail="cluster c2", lhead="cluster c3"]` as it is not directly possible to link `"cluster c2" -> "cluster c3"` but why aren't you using: `n10 -> n14 [ltail="cluster c2", lhead="cluster c3"]` ? (similar for the other clusters). – albert Apr 20 '23 at 07:53
  • This is because of the automatic strategy that I've mentioned before (last-to-first). I've added picture with another strategy: last-to-last. It also doens't look how I would like. Therefore I'm looking is tehre a way to align the cluster vertically – Artyum Apr 20 '23 at 11:51
  • Can you also post the dot file for the strategy `last to last` ? Did you have a try with `rankdir="TB"`? – albert Apr 20 '23 at 12:19
  • Added dot file. I try rankdir but with no luck – Artyum Apr 20 '23 at 12:49
  • The clusters C5 and C6 look like to be a bit the odd ones out. 1) they have internal node connections (e.g n17 -> n18) 2) their node numbers don't follow directly on the ones from the previous cluster. Looks a bit that you have to have a hybrid strategy, normal `last -> last` and for the the ones with the connection `last -> first` (i.e. instead of `14 -> 18` use `14 -> 17` and instead of `55 -> 51` use `55 -> 48`) – albert Apr 20 '23 at 13:02
  • hybrid seems to be rather unplesant to automate – Artyum Apr 20 '23 at 13:47
  • Might be hard to automate, though as written before it looks like it has to do with the presence of the edges inside the cluster so probably you can detect that. Maybe there are possibilities with subgraphs in subgraphs (no idea on my side) or with invisible edges (no idea either) – albert Apr 20 '23 at 14:02
  • node placement in your horizontal clusters is right-to-left ("backwards"). Is this desired? It confuses the concepts of **first** and **last** – sroush Apr 20 '23 at 15:21
  • The LR node placement is indeed an issue. It is generated by the dot.exe that way. I can't find any option to control it. – Artyum Apr 21 '23 at 12:05

2 Answers2

0

To get the nodes in the "correct" sequence (left-to-right), add a subgraph inside each cluster. If the cluster turns out to be horizontal - no internal edges - add the rank=same attribute to the subgraph and invisible edges connecting the cluster's nodes.
Programmatically, this can be done at the end of building the cluster (see attached file).
This allows you to correctly identify first and last nodes.
To connect the clusters with edges, I agree that a "hybrid" method is needed.

  • first-to-first if the tail cluster is horizontal (see above)
  • last-to-first if the tail cluster is vertical
digraph {
    compound=true
    fontname="Verdana"
    fontsize=12
    nodesep = 1
    ranksep = 1.5
    node[shape=folder, style="filled,solid", color=green4, fontsize=12]
    subgraph "cluster 1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <l1>
        n0 [label=<n0> fillcolor=limegreen]
        n1 [label=<n1> fillcolor=limegreen]
        n2 [label=<n2> fillcolor=limegreen]
        n3 [label=<n3> fillcolor=limegreen]
        n4 [label=<n4> fillcolor=limegreen]
        n5 [label=<n5> fillcolor=limegreen]
        n0 -> n1
        n1 -> n2
        n2 -> n3
        n3 -> n4
        n4 -> n5
    }
    subgraph "cluster c1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c1>
      {
        n6 [label=<n6> fillcolor=limegreen]
      rank=same}  // added
    }
    subgraph "cluster c2" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c2>
      {  // added
        n7 [label=<n7> fillcolor=limegreen]
        n8 [label=<n8> fillcolor=limegreen]
        n9 [label=<n9> fillcolor=limegreen]
        n10 [label=<n10> fillcolor=limegreen]
    // added next 3 lines
    edge[style=invis]
    n7->n8->n9->n10
      rank=same}
    }
    subgraph "cluster c3" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c3>
      {
        n11 [label=<n11> fillcolor=limegreen]
        n12 [label=<n12> fillcolor=limegreen]
        n13 [label=<n13> fillcolor=limegreen]
        n14 [label=<n14> fillcolor=limegreen]
    edge[style=invis]
    n11->n12->n13->n14
      rank=same}
    }
    subgraph "cluster c4" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c4>
      {
        n15 [label=<n15> fillcolor=limegreen]
        n16 [label=<n16> fillcolor=limegreen]
    edge[style=invis]
    n15->n16
      rank=same}
    }
    subgraph "cluster c5" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c5>
      {
        n17 [label=<n17> fillcolor=limegreen]
        n18 [label=<n18> fillcolor=limegreen]
        n17 -> n18
      }  // note: did not add rank=same
    }
    subgraph "cluster c6" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c6>
      {
        n48 [label=<n48> fillcolor=limegreen]
        n49 [label=<n49> fillcolor=limegreen]
        n50 [label=<n50> fillcolor=limegreen]
        n51 [label=<n51> fillcolor=limegreen]
        n48 -> n49
        n49 -> n50
        n50 -> n51
      }  // note: did not add rank=same
    }
    subgraph "cluster c7" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c7>
      {
        n52 [label=<n52> fillcolor=limegreen]
        n53 [label=<n53> fillcolor=limegreen]
    edge[style=invis]
    n52->n53
      rank=same}
    }
    subgraph "cluster c8" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c8>
      {
        n54 [label=<n54> fillcolor=limegreen]
        n55 [label=<n55> fillcolor=limegreen]
    edge[style=invis]
    n54->n55
      rank=same}
    }
    n6 -> n7 [ltail="cluster c1", lhead="cluster c2"]
    n7 -> n11 [ltail="cluster c2", lhead="cluster c3"]
    // last-to-first
    n18 -> n15 [ltail="cluster c5", lhead="cluster c4"]
//   n17 -> n15 [ltail="cluster c5", lhead="cluster c4"] 
    n11 -> n17 [ltail="cluster c3", lhead="cluster c5"]
    n54 -> n48 [ltail="cluster c8", lhead="cluster c6"]
    n15 -> n52 [ltail="cluster c4", lhead="cluster c7"]
    n52 -> n54  [ltail="cluster c7", lhead="cluster c8"]
}

Giving:
enter image description here

sroush
  • 5,375
  • 2
  • 5
  • 11
0

Thanks for advice. My final solution is mixed-strategy as follows:

  • In a cluster add a row: { rank=same ... [style=invis] } and list in it every node (in appearance order) that is not dependent of other node but only if there are at least 2 of them

  • Between clusters add an edge "first-to-first" if there are exactly 0 dependencies in a first cluster

  • Between clusters add an edge "last-to-first" if there are at least one dependency in a first cluster

      digraph {
          compound=true
          fontsize=12
          nodesep = 0.5
          ranksep = 1
          node[shape=folder, style="filled,solid"]
    
          subgraph "cluster c0" {
              label = <c0>
              n0 [label=<n0>]
              n1 [label=<n1>]
              n2 [label=<n2>]
              n3 [label=<n3>]
              n4 [label=<n4>]
              n5 [label=<n5>]
              n0 -> n1
              n1 -> n2
              n2 -> n3
              n3 -> n4
              n4 -> n5
          }
    
          subgraph "cluster c1" {
              label = <c1>
              n6 [label=<n6>]
          }
    
          subgraph "cluster c2" {
              label = <c2>
              n7 [label=<n7>]
              n8 [label=<n8>]
              n9 [label=<n9>]
              n10 [label=<n10>]
              { rank=same n7->n8->n9->n10 [style=invis] }
          }
    
          subgraph "cluster c3" {
              label = <c3>
              n11 [label=<n11>]
              n12 [label=<n12>]
              n13 [label=<n13>]
              n14 [label=<n14>]
              { rank=same n11->n12->n13->n14 [style=invis] }
          }
    
          subgraph "cluster c4" {
              label = <c4>
              n15 [label=<n15>]
              n16 [label=<n16>]
              { rank=same n15->n16 [style=invis] }
          }
    
          subgraph "cluster c5" {
              label = <c5>
              n17 [label=<n17>]
              n18 [label=<n18>]
              n181 [label=<n181>]
              n18 -> n181
              { rank=same n17->n18 [style=invis] }
          }
    
          subgraph "cluster c6" {
              label = <c6>
              n48 [label=<n48>]
              n49 [label=<n49>]
              n50 [label=<n50>]
              n51 [label=<n51>]
              n48 -> n49
              n49 -> n50
              n50 -> n51
          }
    
          subgraph "cluster c7" {
              label = <c7>
              n52 [label=<n52>]
              n53 [label=<n53>]
              { rank=same n52->n53 [style=invis] }
          }
    
          subgraph "cluster c8" {
              label = <c8>
              n54 [label=<n54>]
              n55 [label=<n55>]
              { rank=same n54->n55 [style=invis] }
          }
    
          n6 -> n7 [ltail="cluster c1", lhead="cluster c2"]
          n7 -> n11 [ltail="cluster c2", lhead="cluster c3"]
          n181 -> n15 [ltail="cluster c5", lhead="cluster c4"]
          n11 -> n17 [ltail="cluster c3", lhead="cluster c5"]
          n15 -> n52 [ltail="cluster c4", lhead="cluster c7"]
          n52 -> n54 [ltail="cluster c7", lhead="cluster c8"]
          n54 -> n48 [ltail="cluster c8", lhead="cluster c6"]
      }
    

The result:

enter image description here

Artyum
  • 169
  • 2
  • 13