📚 The CoCalc Library - books, templates and other resources
License: OTHER
Small World Graphs
Code examples from Think Complexity, 2nd edition.
Copyright 2016 Allen Downey, MIT License
Regular ring lattice
To make a ring lattice, I'll start with a generator function that yields edges between each node and the next halfk
neighbors.
We can test it with 3 nodes and halfk=1
Now we use adjacent_edges
to write make_ring_lattice
And we can test it out with n=10
and k=4
Exercise: To see how this function fails when k
is odd, run it again with k=3
or k=5
.
WS graph
To make a WS graph, you start with a ring lattice and then rewire.
Here's the function that does the rewiring
Here's an example with p=0.2
Just checking that we have the same number of edges we started with:
Now I'll generate a plot that shows WS graphs for a few values of p
Exercise: What is the order of growth of rewire
?
Clustering
The following function computes the local clustering coefficient for a given node, u
:
The network average clustering coefficient is just the mean of the local CCs.
In a ring lattice with k=4
, the clustering coefficient for each node should be 0.5
And the network average should be 0.5
Correct.
Exercise: Write a version of node_clustering
that replaces the for
loop with a list comprehension. Is it faster?
Exercise: What is the order of growth of clustering_coefficient
in terms of n
, m
, and k
?
Path length
The following function computes path lengths between all pairs of nodes
The characteristic path length is the mean path length for all pairs.
On a complete graph, the average path length should be 1
On a ring lattice with n=1000
and k=10
, the mean is about 50
Exercise: What is the mean path length in a ring lattice with n=10
and k=4
?
The experiment
This function generates a WS graph with the given parameters and returns a pair of (mean path length, clustering coefficient):
With n=1000
and k=10
, it takes a few seconds on my computer:
Now we'll run it with a range of values for p
.
This function runs each value of p
20 times and returns a dictionary that maps from each p
to a list of (mpl, cc) pairs.
Here are the raw results. Warning: this takes a few minutes to run.
Let's get the results into a form that's easy to plot.
And normalize them so they both start at 1.0
Here's the plot that replicates Watts and Strogatz's Figure 2.
Breadth-first search
Now let's see how the shortest path algorithm works. We'll start with BFS, which is the basis for Dijkstra's algorithm.
Here's our old friend, the ring lattice:
And here's my implementation of BFS using a deque.
It works:
Here's a version that's a little faster, but maybe less readable.
It works, too.
Dijkstra's algorithm
Now we're ready for Dijkstra's algorithm, at least for graphs where all the edges have the same weight/length.
Again, we'll test it on a ring lattice.
Here's my implementation:
And here's the result from NetworkX:
They are the same:
Exercise: In a ring lattice with n=1000
and k=10
, which node is farthest from 0 and how far is it? Use shortest_path_dijkstra
to check your answer.
Note: the maximum distance between two nodes is the diameter of the graph.
Exercises
Exercise: In a ring lattice, every node has the same number of neighbors. The number of neighbors is called the degree of the node, and a graph where all nodes have the same degree is called a regular graph.
All ring lattices are regular, but not all regular graphs are ring lattices. In particular, if k
is odd, we can't construct a ring lattice, but we might be able to construct a regular graph.
Write a function called make_regular_graph
that takes n
and k
and returns a regular graph that contains n
nodes, where every node has k
neighbors. If it's not possible to make a regular graph with the given values of n
and k
, the function should raise a ValueError
.
Exercise: My implementation of reachable_nodes_bfs
is efficient in the sense that it is in , but it incurs a lot of overhead adding nodes to the queue and removing them. NetworkX provides a simple, fast implementation of BFS, available from the NetworkX repository on GitHub.
Here is a version I modified to return a set of nodes:
Compare this function to reachable_nodes_bfs
and see which is faster. Then see if you can modify this function to implement a faster version of shortest_path_dijkstra
Exercise: The following implementation of a BFS contains two performance errors. What are they? What is the actual order of growth for this algorithm?
Exercise: In the book, I claimed that Dijkstra's algorithm does not work unless it uses BFS. Write a version of shortest_path_dijkstra
that uses DFS and test it on a few examples to see what goes wrong.