Search code examples
pythongraphvizpygraphviz

Difficulty separating ranks in graphviz python package


I'm trying to create a neural network graph using the graphviz Python package, and I'm running into issues trying to prevent rank overlap among other things. I'm specifically asking about preventing rank overlap in this post.

This is the code I have so far,

from graphviz import Graph

d = Graph(name="Network Graph",graph_attr={'rankdir':'TD'},engine='dot')

with d.subgraph() as s:
    s.attr(rank='same')
    s.node('density','density')
    s.node('te','Elec. Temp.')
    s.node('tr','Rad. Temp.')
    s.node('alpha','alpha')
    
layer0 = ['density','te','tr','alpha']
layer1 = []
layer2 = []
layer3 = []
layer4 = []
layer5 = []
allLayers =[ layer0, layer1, layer2, layer3, layer4, layer5 ]

with d.subgraph() as s1:
    s1.attr(rank='same')
    for i in range(1,7):
        layer1.append('a1%i'%i)
        s1.node('a1%i'%i,'',tailclip='true',headclip='true')
        
with d.subgraph() as s2:
    s2.attr(rank='same')
    for i in range(1,10):
        layer2.append('a1%i'%i)
        s2.node('a1%i'%i,'',tailclip='true',headclip='true')
        
with d.subgraph() as s3:
    s3.attr(rank='same')
    for i in range(1,3):
        layer3.append('a1%i'%i)
        s3.node('a1%i'%i,'',tailclip='true',headclip='true')

with d.subgraph() as s4:
    s4.attr(rank='same')
    for i in range(1,7):
        layer4.append('a1%i'%i)
        s4.node('a1%i'%i,'',tailclip='true',headclip='true')
        
with d.subgraph() as s5:
    s5.attr(rank='same')
    for i in range(1,14):
        layer5.append('a1%i'%i)
        s5.node('a1%i'%i,'',tailclip='true',headclip='true')

edges = []
d.attr(rankdir="LR",splines='line',ranksep='2.0',overlap='false')
for n in range(0,len(allLayers)-1):
    # d.attr(rankdir="LR",splines='line',ranksep='2.0',overlap='false')
    for i in allLayers[n]:
        for j in allLayers[n+1]:
            # edges.append((i,j))
            d.edge(i,j,directed="False")

print(d.source)

d.render('network.pdf',view=True)

This produces this is image

enter image description here

I have no idea as to why I'm able to separate ranks/layers 0 and 1 but then all subsequent layers stack on top of layer1. For some reason the line d.attr(rankdir="LR",splines='line',ranksep='2.0',overlap='false') appears to only apply once, but when I place the line inside the for loop, it behaves the same.

The documentation for the graphviz package leaves much to be desired, and there is very little help for Python Graphviz floating around the internet that I could find. Also, one apparently cannot place d.attr(rankdir='LR') immediately after the creation of the Graph() object. It had to be placed after the creation of all of the nodes, so order apparently matters.

FYI: I've also attempted to add the parameter overlap='false' in various places, but that didn't change behavior at all in the ways that I've used it.

How do I separate the ranks? Are there any other general tips with working with Graphviz particularly in terms of how to properly order commands and set attributes?


Solution

  • Glad to see that you solved your problem. But you might find the following changes helpful. Have a look at them.

    • Name each of your subgraphs
    • Use different names for your nodes (which you already realized)
    • Use a directed graph instead
    • You can in fact set all the graph attributes while instantiating
    from graphviz import Digraph
    
    d = Digraph(name="Network Graph",
              graph_attr={'rankdir':'LR', 'splines':'line','ranksep':'2.0','overlap':'false'},
              engine='dot')
    
    with d.subgraph(name='s') as s:
        s.node('density','density')
        s.node('te','Elec. Temp.')
        s.node('tr','Rad. Temp.')
        s.node('alpha','alpha')
        
    layer0 = ['density','te','tr','alpha']
    layer1 = []
    layer2 = []
    layer3 = []
    layer4 = []
    layer5 = []
    allLayers =[ layer0, layer1, layer2, layer3, layer4, layer5 ]
    
    with d.subgraph(name='s1') as s1:
        s1.attr(rank='same')
        for i in range(1,7):
            layer1.append('a1%i'%i)
            s1.node('a1%i'%i,'',tailclip='true',headclip='true')
            
    with d.subgraph(name='s2') as s2:
        s2.attr(rank='same')
        for i in range(1,10):
            layer2.append('a2%i'%i)
            s2.node('a2%i'%i,'',tailclip='true',headclip='true')
            
    with d.subgraph(name='s3') as s3:
        s3.attr(rank='same')
        for i in range(1,3):
            layer3.append('a3%i'%i)
            s3.node('a3%i'%i,'',tailclip='true',headclip='true')
    
    with d.subgraph(name='s4') as s4:
        s4.attr(rank='same')
        for i in range(1,7):
            layer4.append('a4%i'%i)
            s4.node('a4%i'%i,'',tailclip='true',headclip='true')
            
    with d.subgraph(name='s5') as s5:
        s5.attr(rank='same')
        for i in range(1,14):
            layer5.append('a5%i'%i)
            s5.node('a5%i'%i,'',tailclip='true',headclip='true')
    
    for n in range(0,len(allLayers)-1):
        for i in allLayers[n]:
            for j in allLayers[n+1]:
                d.edge(i,j,directed="False")
    
    # print(d.source)
    
    # d.render('network.pdf',view=True)
    

    You might also find the following links helpful

    https://logicatcore.github.io/scratchpad/machine%20learning/jupyter/graphviz/2020/12/26/Graphviz-Neural-Networks-visualisation.html

    https://github.com/martisak/dotnets