`mpl_draw()` does not work for multigraphs
Information
- rustworkx version: 0.12.1
- Python version: 3.9
- Rust version: I do not know, but not relevant for this issue.
- Operating system: macOs
What is the current behavior?
When plotting a multigraph with parallel edges using rustworkx.visualization.mpl_draw(), two issues occur:
- The arrows for multiple edges are printed on top of each other.
- Only one label is drawn for every pair of nodes. (The labels are currently saved in a dict, which uses node pair as the key.)
What is the expected behavior?
I see three possible solutions:
- The documentation could warn that
mpl_draw()does not work for multigraphs. mpl_draw()could raise a warning when a multigraph is passed. (networkx seems to do that for at least the edge labels)- Ideally,
mpl_draw()would be modified to show parallel edges. As far as I can see, this would include three steps, similar to what has been outlined in https://stackoverflow.com/a/70245742:- Modifying
draw_edges()so it bends the arrows of parallel edges to prevent overlap. - Modifying
draw_edge_labels()so it plots the edge labels at the positions of the arrows and not simply on the line connecting the nodes. - Modifying
draw_graph()sokwds["edge_labels"]contains one entry per edge and not one per node-pair. (The key of the dict should be the edge id and not the node pair.)
- Modifying
Steps to reproduce the problem
- Create a graph with two parallel edges:
graph = rustworkx.PyDiGraph()
graph.add_node('A')
graph.add_node('B')
graph.add_edge(0, 1, 1)
graph.add_edge(0, 1, 2)
mpl_draw(graph, with_labels=True, labels=str, edge_labels=str, alpha=0.5)
- Save the image as a vector graphic and inspect it.
The image will contain two arrows on top of each other and the label for edge 2. The label for edge 1 is not drawn.
As a temporary workaround, you might want to try rustworkx.visualization.graphviz_draw which does support multigraphs.
But indeed, we ported the visualization code from NetworkX. So we inherited the problems when drawing multigraphs.
As I worked on a rustworkx-related PR in last year's UnitaryHack, I'd like to work on this issue.
This is the graph I have obtained after a few edits in the matplotlib.py file:
The code used to recreate this is:
import rustworkx
from rustworkx.visualization import mpl_draw
import matplotlib.pyplot as plt
graph = rustworkx.PyDiGraph()
graph.add_node('A')
graph.add_node('B')
graph.add_node('C')
graph.add_edge(1, 2, 4)
graph.add_edge(1, 0, 2)
graph.add_edge(0, 1, 3)
fig = mpl_draw(graph, with_labels=True, labels=str, edge_labels=str, alpha=0.5)
plt.savefig('test_fig.png')
Before moving forward, I wish to clarify a few points:
- The sample code shared to recreate the problem has two edges from 0 to 1, and plotting this only creates one node from
0to1. Is this the expected behavior? - Should I assert the
connectionstyleto eitherarcorarc3for this plotting style? Also, is it okay to keepradconstant? I've usedrad=0.25. - It appears that one of the labels of the loop was disappearing because of being plotted over by the other label. Offsetting the labels has solved the problem and there is no need to explicitly change
kwds["edge_labels"].
Given that UnitaryHack concludes in less than a week, can someone please review my PR for any further changes required?