changeset 23:74c9d126bd05

Working on the SmallWorldGraph exercises.
author Brian Neal <bgneal@gmail.com>
date Wed, 02 Jan 2013 16:50:55 -0600
parents 84e183b40c63
children 5c2c4ce095ef
files SmallWorldGraph.py ch4ex4.py
diffstat 2 files changed, 134 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SmallWorldGraph.py	Wed Jan 02 16:50:55 2013 -0600
@@ -0,0 +1,87 @@
+"""This module contains the SmallWorldGraph class for 4.4, exercise 4.
+
+"""
+import itertools
+import random
+
+from Graph import Edge
+from RandomGraph import RandomGraph
+
+
+class SmallWorldGraph(RandomGraph):
+    """A small world graph for 4.4, exercise 4 in Think Complexity."""
+
+    def __init__(self, vs, k, p):
+        """Create a small world graph. Parameters:
+            vs - a list of vertices
+            k - regular graph parameter k: the number of edges between vertices
+            p - probability for rewiring.
+
+        First a regular graph is created from the list of vertices and parameter
+        k, which specifies how many edges each vertex should have. Then the
+        rewire() method is called with parameter p to rewire the graph according
+        to the algorithm by Watts and Strogatz.
+
+        """
+        super(SmallWorldGraph, self).__init__(vs=vs)
+        self.add_regular_edges(k)
+        self.rewire(p)
+
+    def rewire(self, p):
+        """Assuming the graph is initially a regular graph, rewires the graph
+        using the Watts and Strogatz algorithm.
+
+        """
+        # We iterate over the edges in a random order:
+        edges = self.edges()
+        random.shuffle(edges)
+        vertices = self.vertices()
+
+        for e in edges:
+            if random.random() < p:
+                v, w = e    # remember the 2 vertices this edge connected
+                self.remove_edge(e)     # remove from graph
+
+                # pick another vertex to connect to v; duplicate edges are
+                # forbidden
+                while True:
+                    x = random.choice(vertices)
+                    if v is not x and not self.get_edge(v, x):
+                        self.add_edge(Edge(v, x))
+                        break
+
+    def clustering_coefficient(self):
+        """Compute the clustering coefficient for this graph as defined by Watts
+        and Strogatz.
+
+        """
+        cv = {}
+        for v in self:
+            # consider a node and its neighbors
+            nodes = self.out_vertices(v)
+            nodes.append(v)
+
+            # compute the maximum number of possible edges between these nodes
+            # if they were all connected to each other:
+            n = len(nodes)
+            if n == 1:
+                # edge case of only 1 node; handle this case to avoid division
+                # by zero in the general case
+                cv[v] = 1.0
+                continue
+
+            possible = n * (n - 1) / 2.0
+
+            # now compute how many edges actually exist between the nodes
+            actual = 0
+            for x, y in itertools.combinations(nodes, 2):
+                if self.get_edge(x, y):
+                    actual += 1
+
+            # the fraction of actual / possible is this nodes C sub v value
+            cv[v] = actual / possible
+
+        # The clustering coefficient is the average of all C sub v values
+        if len(cv):
+            return sum(cv.values()) / float(len(cv))
+        return 0.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ch4ex4.py	Wed Jan 02 16:50:55 2013 -0600
@@ -0,0 +1,47 @@
+"""This program performs item 4 in 4.4 exercise 4.
+
+"Make a graph that replicates the line marked C(p)/C(0) in Figure 2 of the
+paper. In other words, confirm that the clustering coefficient drops off slowly
+for small values of p."
+
+"""
+import random
+import matplotlib.pyplot as pyplot
+
+from Graph import Vertex
+from SmallWorldGraph import SmallWorldGraph
+
+
+title = 'C(p)/C(0)'
+
+# compute C(0)
+n = 1000
+k = 10
+vs = [Vertex(str(i)) for i in range(n)]
+g = SmallWorldGraph(vs, k, 0.0)
+c0 = g.clustering_coefficient()
+print 'c0 =', c0
+
+# compute data
+p_vals = [0,
+          0.0001, 0.0002, 0.0004, 0.0006, 0.0008,
+          0.001, 0.002, 0.004, 0.006, 0.008,
+          0.01, 0.02, 0.04, 0.06, 0.08,
+          0.1, 0.2, 0.4, 0.6, 0.8,
+          1.0]
+
+c_vals = []
+for p in p_vals:
+    g = SmallWorldGraph(vs, k, p)
+    c_vals.append(g.clustering_coefficient() / c0)
+
+# plot graph
+pyplot.clf()
+pyplot.xscale('log')
+pyplot.yscale('log')
+pyplot.title('')
+pyplot.xlabel('p')
+pyplot.ylabel('C(p)/C(0)')
+pyplot.plot(p_vals, c_vals, label=title, color='green', linewidth=3)
+pyplot.legend(loc=4)
+pyplot.show()