Mercurial > public > think_complexity
comparison ch5ex6.py @ 32:a13c00c0dfe5
Chapter 5, exercise 6, #1: Barab?si and Albert.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Wed, 09 Jan 2013 20:09:19 -0600 |
parents | |
children | 66a5e7f7c10f |
comparison
equal
deleted
inserted
replaced
31:a2358c64d9af | 32:a13c00c0dfe5 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 """Chapter 5.5, exercise 6 in Allen Downey's Think Complexity book. | |
3 | |
4 1. Read Barabási and Albert’s paper and implement their algorithm for generating | |
5 graphs. See if you can replicate their Figure 2(A), which shows P(k) versus | |
6 k for a graph with 150 000 vertices. | |
7 | |
8 """ | |
9 import collections | |
10 import random | |
11 import sys | |
12 | |
13 from matplotlib import pyplot | |
14 | |
15 from Graph import Graph, Vertex, Edge | |
16 | |
17 | |
18 class BAGraph(Graph): | |
19 """BAGraph implements the algorithm described in the Barabási and | |
20 Albert paper "Emergence of Scaling in Random Networks" from Science | |
21 Magazine Vol. 286, 15 October 1999. | |
22 | |
23 """ | |
24 def __init__(self, m0, m): | |
25 """Create a graph with m0 vertices and m connecting edges. | |
26 | |
27 """ | |
28 self.m0 = m0 | |
29 self.m = m | |
30 self.histogram = [] | |
31 | |
32 initial_vertices = [Vertex() for i in xrange(m0)] | |
33 | |
34 super(BAGraph, self).__init__(vs=initial_vertices) | |
35 | |
36 # Add initial edges between nodes randomly | |
37 n = 0 | |
38 while n != m: | |
39 # pick two vertices at random | |
40 v = random.choice(initial_vertices) | |
41 w = random.choice(initial_vertices) | |
42 | |
43 # if they aren't the same and don't already have an edge, add one | |
44 if v is not w and not self.get_edge(v, w): | |
45 self.add_edge(Edge(v, w)) | |
46 n += 1 | |
47 | |
48 def add_edge(self, edge): | |
49 """Our version of add_edge() adds the two vertices on either end of the | |
50 edge to a histogram. This allows us to more easily pick popular vertices | |
51 when adding edges as part of the step() method. | |
52 | |
53 """ | |
54 super(BAGraph, self).add_edge(edge) # invoke base class version | |
55 | |
56 v, w = edge | |
57 self.histogram.append(v) | |
58 self.histogram.append(w) | |
59 | |
60 def step(self): | |
61 """This method adds a new vertex to the graph, then adds m edges that | |
62 link the new vertex to m different vertices already present in the | |
63 graph. Preferential treatment is given towards vertices that already | |
64 have high connectivity. | |
65 | |
66 The paper describes this preferential choosing as creating a probability | |
67 P that a new vertex will be connected to vertex i depends on the | |
68 connectivity ki of that vertex, so that P(ki) = ki / sum(kj). | |
69 | |
70 We implement that by keeping a list of vertices (histogram), where we | |
71 insert a vertex into this list whenever it gets an edge added to it. We | |
72 then randomly choose a vertex from this list. Thus the vertices with | |
73 more edges will be more likely chosen. | |
74 | |
75 """ | |
76 # pick m unique vertices to attach to the new vertex | |
77 vs = set() | |
78 while len(vs) < self.m: | |
79 w = random.choice(self.histogram) | |
80 if w not in vs: | |
81 vs.add(w) | |
82 | |
83 # Add the new vertex to the graph and create edges | |
84 v = Vertex() | |
85 self.add_vertex(v) | |
86 for w in vs: | |
87 self.add_edge(Edge(v, w)) | |
88 | |
89 def get_p(self): | |
90 """This method returns a dictionary of probabilities where each key is | |
91 the connectivity k and the value is the probability [0-1] for this | |
92 graph. | |
93 | |
94 """ | |
95 # First, for each vertex, count up how many neighbors it has | |
96 vs = self.vertices() | |
97 | |
98 c = collections.Counter() | |
99 for v in vs: | |
100 n = len(self.out_vertices(v)) | |
101 c[n] += 1 | |
102 | |
103 n = len(vs) | |
104 if n > 0: | |
105 for k in c: | |
106 c[k] = float(c[k]) / n | |
107 | |
108 return c | |
109 | |
110 | |
111 def main(script, m0, m, n): | |
112 | |
113 # create a BAGraph with parameters m0 & m: | |
114 m0, m, n = int(m0), int(m), int(n) | |
115 g = BAGraph(m0, m) | |
116 | |
117 # step the graph n times: | |
118 for i in xrange(n): | |
119 g.step() | |
120 | |
121 # retrieve probabilities | |
122 p = g.get_p() | |
123 | |
124 # plot P(k) versus k on a log-log scale | |
125 | |
126 vals = p.items() | |
127 vals.sort(key=lambda t: t[0]) | |
128 x, y = zip(*vals) | |
129 | |
130 pyplot.clf() | |
131 pyplot.xscale('log') | |
132 pyplot.yscale('log') | |
133 pyplot.title('P(k) versus k') | |
134 pyplot.xlabel('k') | |
135 pyplot.ylabel('P(k)') | |
136 pyplot.plot(x, y, label='P(k) vs. k', color='green', linewidth=3) | |
137 pyplot.legend(loc='upper right') | |
138 pyplot.show() | |
139 | |
140 | |
141 if __name__ == '__main__': | |
142 main(*sys.argv) |