Search code examples
latexnetworkxdot

Draw DiGraph in networkx with attribute giving height in the graph


In networkx, I have a directed draph G = networkx.DiGraph() where each node has an attribute w (always a positive integer, including zero). The graph is acyclic (it represents a poset), and the value increases as one goes from node to node, that is, generically G.nodes[n1]["w"] < G.nodes[n2]["w"] if there is a path between n1 and n2, but it is not a direct function of the position in the graph, i.e. two nodes with edges to the same node can have different values of w, as long as they are bigger than that of their origin.

I need to draw this diagram as a tikz figure in latex. I want the figure to not only represent G, but also that nodes with different values of w are at different heights (and same values of w at the same height.

A toy example I implemented is:

import networkx
import dot2tex


nodes = [
    ["A", {"w": 0}],
    ["B", {"w": 1}],
    ["C", {"w": 2}],
    ["D", {"w": 3}],
        ]

edges = [
    ["A", "B"],
    ["A", "C"],
    ["B", "D"],
    ["C", "D"],
]

G = networkx.DiGraph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
    
A = networkx.nx_agraph.to_agraph(G)
dot_string = A.string()

latex_string = dot2tex.dot2tex(
    dot_string,
    preproc=True,
    docpreamble=r"\usepackage{amsmath,amssymb,amsthm,bm}",
    format="tikz"
)
latex_string = dot2tex.dot2tex(
    latex_string,
    figonly=False,
    docpreamble=r"\usepackage{amsmath,amssymb,amsthm,bm}",
    autosize=True,
    crop=True,
    valignmode="dot",
    format="tikz"
)

print(latex_string)

with open("test.tex", "w") as f:
    f.write(latex_string)

This gives the picture on the left

enter image description here enter image description here

However I need the node C to be half way between B and D to reflect the fact that its argument w=2 satisfies 1<2<3, as depicted more or less on the right. The actual diagrams I have can have order 50 nodes, so doing it by hand would take ages. Is there a simple way to do it automatically? The precise height is not really important, as long as it's clear that B and C have different w.


Solution

  • As commented by @sroush, this is achieved by setting the minlen attribute as the difference between each w values for the dot graph. The easiest is to do it directly at the level of the networkx.DiGraph.

    Simply adding the following bit lines in the code above will give the desired result

    ...
    G = networkx.DiGraph()
    G.add_nodes_from(nodes)
    G.add_edges_from(edges)
    
    for e in G.edges:
        G.edges[e]["minlen"] = G.nodes[e[1]]["w"] - G.nodes[e[0]]["w"]
    ...
    
    

    resulting figure after adding bit of code