Mercurial > public > think_complexity
diff SmallWorldGraph.py @ 36:305cc03c2750
Chapter 5.5, exercise 6, #3: compute WS clustering coefficient
and characteristic length on a BA model graph.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Thu, 10 Jan 2013 19:24:02 -0600 |
parents | f6073c187926 |
children |
line wrap: on
line diff
--- a/SmallWorldGraph.py Wed Jan 09 20:51:16 2013 -0600 +++ b/SmallWorldGraph.py Thu Jan 10 19:24:02 2013 -0600 @@ -1,16 +1,12 @@ """This module contains the SmallWorldGraph class for 4.4, exercise 4. """ -import heapq -import itertools import random from Graph import Edge from RandomGraph import RandomGraph -INFINITY = float('Inf') - class SmallWorldGraph(RandomGraph): """A small world graph for 4.4, exercise 4 in Think Complexity.""" @@ -29,11 +25,7 @@ super(SmallWorldGraph, self).__init__(vs=vs) self.add_regular_edges(k) self.rewire(p) - - # give each edge a default length of 1; this is used by the - # shortest_path_tree() method - for e in self.edges(): - e.length = 1 + self.set_edge_length(1) def rewire(self, p): """Assuming the graph is initially a regular graph, rewires the graph @@ -58,180 +50,3 @@ 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 - - def shortest_path_tree(self, source, hint=None): - """Finds the length of the shortest path from the source vertex to all - other vertices in the graph. This length is stored on the vertices as an - attribute named 'dist'. The algorithm used is Dijkstra's. - - hint: if provided, must be a dictionary mapping tuples to already known - shortest path distances. This can be used to speed up the algorithm. - - """ - if not hint: - hint = {} - - for v in self.vertices(): - v.dist = hint.get((source, v), INFINITY) - source.dist = 0 - - queue = [v for v in self.vertices() if v.dist < INFINITY] - sort_flag = True - while len(queue): - - if sort_flag: - queue.sort(key=lambda v: v.dist) - sort_flag = False - - v = queue.pop(0) - - # for each neighbor of v, see if we found a new shortest path - for w, e in self[v].iteritems(): - d = v.dist + e.length - if d < w.dist: - w.dist = d - queue.append(w) - sort_flag = True - - def shortest_path_tree2(self, source): - """Finds the length of the shortest path from the source vertex to all - other vertices in the graph. This length is stored on the vertices as an - attribute named 'dist'. The algorithm used is Dijkstra's with a Heap - used to sort/store pending nodes to be examined. - - """ - for v in self.vertices(): - v.dist = INFINITY - source.dist = 0 - - queue = [] - heapq.heappush(queue, (0, source)) - while len(queue): - - _, v = heapq.heappop(queue) - - # for each neighbor of v, see if we found a new shortest path - for w, e in self[v].iteritems(): - d = v.dist + e.length - if d < w.dist: - w.dist = d - heapq.heappush(queue, (d, w)) - - def all_pairs_floyd_warshall(self): - """Finds the shortest paths between all pairs of vertices using the - Floyd-Warshall algorithm. - - http://en.wikipedia.org/wiki/Floyd-Warshall_algorithm - - """ - vertices = self.vertices() - dist = {} - for i in vertices: - for j in vertices: - if i is j: - dist[i, j] = 0.0 - else: - e = self.get_edge(i, j) - dist[i, j] = e.length if e else INFINITY - - for k in vertices: - for i in vertices: - for j in vertices: - d_ik = dist[i, k] - d_kj = dist[k, j] - new_cost = d_ik + d_kj - if new_cost < dist[i, j]: - dist[i, j] = new_cost - - return dist - - def big_l(self): - """Computes the "big-L" value for the graph as per Watts & Strogatz. - - L is defined as the number of edges in the shortest path between - two vertices, averaged over all vertices. - - Uses the shortest_path_tree() method, called once for every node. - - """ - d = {} - for v in self.vertices(): - self.shortest_path_tree(v, d) - t = [((w, v), w.dist) for w in self.vertices() if v is not w] - d.update(t) - - if len(d): - return sum(d.values()) / float(len(d)) - return 0.0 - - def big_l2(self): - """Computes the "big-L" value for the graph as per Watts & Strogatz. - - L is defined as the number of edges in the shortest path between - two vertices, averaged over all vertices. - - Uses the all_pairs_floyd_warshall() method. - - """ - dist = self.all_pairs_floyd_warshall() - vertices = self.vertices() - result = [dist[i, j] for i in vertices for j in vertices if i is not j] - - if len(result): - return sum(result) / float(len(result)) - return 0.0 - - def big_l3(self): - """Computes the "big-L" value for the graph as per Watts & Strogatz. - - L is defined as the number of edges in the shortest path between - two vertices, averaged over all vertices. - - Uses the shortest_path_tree2() method, called once for every node. - - """ - d = {} - for v in self.vertices(): - self.shortest_path_tree2(v) - t = [((v, w), w.dist) for w in self.vertices() if v is not w] - d.update(t) - - if len(d): - return sum(d.values()) / float(len(d)) - return 0.0 -