Shortest Path
At Folding@home, often we use Markov state models (MSMs) to represent how molecules (usually proteins) move around in a simulation. These models help simplify large amounts of simulation data and ultimate yield a more concise network representation. Because of this, we can borrow tools normally used in fields such as graph theory to study how biological molecules behave.
One such tool is Dijkastra’s algorithm, which finds an approximate to the shortest path between any two nodes in a (weakly connected) graph. At each iteration, the algorithm finds the neighboring node that will minimize the total distance needed to traverse the graph. This method can be extremely powerful, and has become as ubiquitous to our society as getting driving directions from Google Maps.
In social networks, Dijkastra’s algorithm is what you would use to compute an Erdös number or how many degrees of separation you share with Kevin Bacon. In protein folding (my line of work), Dijkastra’s algorithm is useful for identifying the possible steps (e.g. confomational changes) that lead to a folded protein. This information can help scientists better understand the underlying physics of biology, which could lead to new therapeutic drugs.
In this tutorial, I’ll go over how to use NumPy
and NetworkX
to construct a random graph and calculate the shortest path between two nodes. I’ll also be using matplotlib
and Seaborn
to visualize the results. Optional: If you’d like to make interactive graphs, as the ones below, the mpld3
package is fairly handy to convert static plots into interactive JavaScript.
Installing Packages
Assuming you already have Python (and pip
) installed on your computer, in your terminal type:
This will install the conda
package manager, which will make installation of the other required Python packages relatively painless:
Seaborn
can be installed using pip
:
Generating Data
To start, we will need a dataset which can be respresented as a directed graph. There are a ton of ways to create this sort of dataset, but in this tutorial I’ll be using NetworkX
to generate an Erdös–Rényi graph with directed edges. This type of graph is constructed using a binomial distribution to determine whether an edge is formed between any two nodes. We can do this pretty easily using the erdos_renyi_graph
function in NetworkX
, which samples the number of edges for each node from a binomial distribution, . Let’s try and for our binomial:
Visualizing the Graph
Before we go on to the shortest path calculation, let’s plot the graph to get a sense about what it looks like. In the code below, I’ve used the scatter
function in matplotlib.pyplot
to plot nodes, with node sizes representing PageRank scores, and node color representing in-degree, or the number of incoming edges. We can get a decent layout to position the nodes using nx.spring_layouts
, which creates a list of xy-coordinates for each node by quickly simulating the graph as a set of masses connected by springs. Edges are simply lines connecting the nodes using the plot
function, with the thicker gray ticks denoting an incoming edge to a node.
Computing the Shortest Path
Now that we have an idea of how to plot a network graph, let’s finally get to calculating the shortest path between a pair of nodes. For this exercise, let’s find the path between the nodes with the lowest and highest PageRank scores. In terms of social networks, this might represent the distance in social hierachy between a second year grad student (low PageRank) and someone like Neil deGrasse Tyson (high PageRank). In protein folding, it could model the steps taken from an unfolded state (low PageRank) to a folded one (high PageRank).
In our example graph from above, it turns out that nodes 0 and 7 are the lowest and highest PageRank scorers, respectively. We can find this by applying np.argmin
and np.argmax
to sizes
, the array PageRank scores we calculated earlier. With this, we can finally calculate the shortest path using nx.shortest_path
like so:
Since we are interested in going from node 0 to node 7, we set the must set the arguments source
and target
accordingly. The only other input to this function is our graph, G
. In this example, our shortest path only requires 3 steps, passing through nodes 22 and 2 along the way. Below you can find modified versions of the previous code and plot, which have been changed to highlight the nodes that form the shortest path between 0 and 7. You can even hover over the nodes to check the labels for yourself!
On a parting note, what should be taken away from this toy model is the not-so-intuitive fact that even in a somewhat sparse network (i.e. low probability of sharing a connection)– although this example is a bit exaggerated in sparsity – things are fairly closely connected. We took the two furthest objects in a directed graph of 25 nodes and were still able to cross it in only 3 steps! Such an effect is made even more apparent when analyzing other graph metrics, such as average path length, on real-life social networks as done in Milgram’s Small-world experiment. Milgram found that on average, any two people in the United States are only 6 degrees of separation away. So just think about that next time you’re alone, watching TV– you could totally become BFFs with someone like Shaq!