bgneal@1
|
1 """ Code example from Complexity and Computation, a book about
|
bgneal@1
|
2 exploring complexity science with Python. Available free from
|
bgneal@1
|
3
|
bgneal@1
|
4 http://greenteapress.com/complexity
|
bgneal@1
|
5
|
bgneal@1
|
6 Copyright 2011 Allen B. Downey.
|
bgneal@1
|
7 Distributed under the GNU General Public License at gnu.org/licenses/gpl.html.
|
bgneal@1
|
8 """
|
bgneal@1
|
9 import itertools
|
bgneal@1
|
10
|
bgneal@1
|
11
|
bgneal@1
|
12 class Vertex(object):
|
bgneal@1
|
13 """A Vertex is a node in a graph."""
|
bgneal@1
|
14
|
bgneal@1
|
15 def __init__(self, label=''):
|
bgneal@1
|
16 self.label = label
|
bgneal@1
|
17
|
bgneal@1
|
18 def __repr__(self):
|
bgneal@1
|
19 """Returns a string representation of this object that can
|
bgneal@1
|
20 be evaluated as a Python expression."""
|
bgneal@1
|
21 return 'Vertex(%s)' % repr(self.label)
|
bgneal@1
|
22
|
bgneal@1
|
23 __str__ = __repr__
|
bgneal@1
|
24 """The str and repr forms of this object are the same."""
|
bgneal@1
|
25
|
bgneal@1
|
26
|
bgneal@1
|
27 class Edge(tuple):
|
bgneal@1
|
28 """An Edge is a list of two vertices."""
|
bgneal@1
|
29
|
bgneal@1
|
30 def __new__(cls, *vs):
|
bgneal@1
|
31 """The Edge constructor takes two vertices."""
|
bgneal@1
|
32 if len(vs) != 2:
|
bgneal@1
|
33 raise ValueError, 'Edges must connect exactly two vertices.'
|
bgneal@1
|
34 return tuple.__new__(cls, vs)
|
bgneal@1
|
35
|
bgneal@1
|
36 def __repr__(self):
|
bgneal@1
|
37 """Return a string representation of this object that can
|
bgneal@1
|
38 be evaluated as a Python expression."""
|
bgneal@1
|
39 return 'Edge(%s, %s)' % (repr(self[0]), repr(self[1]))
|
bgneal@1
|
40
|
bgneal@1
|
41 __str__ = __repr__
|
bgneal@1
|
42 """The str and repr forms of this object are the same."""
|
bgneal@1
|
43
|
bgneal@1
|
44
|
bgneal@1
|
45 class Graph(dict):
|
bgneal@1
|
46 """A Graph is a dictionary of dictionaries. The outer
|
bgneal@1
|
47 dictionary maps from a vertex to an inner dictionary.
|
bgneal@1
|
48 The inner dictionary maps from other vertices to edges.
|
bgneal@1
|
49
|
bgneal@1
|
50 For vertices a and b, graph[a][b] maps
|
bgneal@1
|
51 to the edge that connects a->b, if it exists."""
|
bgneal@1
|
52
|
bgneal@1
|
53 def __init__(self, vs=[], es=[]):
|
bgneal@1
|
54 """Creates a new graph.
|
bgneal@1
|
55 vs: list of vertices;
|
bgneal@1
|
56 es: list of edges.
|
bgneal@1
|
57 """
|
bgneal@1
|
58 for v in vs:
|
bgneal@1
|
59 self.add_vertex(v)
|
bgneal@1
|
60
|
bgneal@1
|
61 for e in es:
|
bgneal@1
|
62 self.add_edge(e)
|
bgneal@1
|
63
|
bgneal@1
|
64 def add_vertex(self, v):
|
bgneal@1
|
65 """Add a vertex to the graph."""
|
bgneal@1
|
66 self[v] = {}
|
bgneal@1
|
67
|
bgneal@1
|
68 def add_edge(self, e):
|
bgneal@1
|
69 """Adds and edge to the graph by adding an entry in both directions.
|
bgneal@1
|
70
|
bgneal@1
|
71 If there is already an edge connecting these Vertices, the
|
bgneal@1
|
72 new edge replaces it.
|
bgneal@1
|
73 """
|
bgneal@1
|
74 v, w = e
|
bgneal@1
|
75 self[v][w] = e
|
bgneal@1
|
76 self[w][v] = e
|
bgneal@1
|
77
|
bgneal@1
|
78 def get_edge(self, v, w):
|
bgneal@1
|
79 """Returns the edge object that exists between the two vertices v & w,
|
bgneal@1
|
80 or None if no such edge exists.
|
bgneal@1
|
81
|
bgneal@1
|
82 """
|
bgneal@1
|
83 try:
|
bgneal@1
|
84 return self[v][w]
|
bgneal@1
|
85 except KeyError:
|
bgneal@1
|
86 return None
|
bgneal@1
|
87
|
bgneal@1
|
88 def remove_edge(self, e):
|
bgneal@1
|
89 """Removes the edge e from the graph."""
|
bgneal@1
|
90
|
bgneal@1
|
91 for x in self.iterkeys():
|
bgneal@1
|
92 remove = [k for k, v in self[x].iteritems() if v is e]
|
bgneal@1
|
93 for k in remove:
|
bgneal@1
|
94 del self[x][k]
|
bgneal@1
|
95
|
bgneal@1
|
96 def vertices(self):
|
bgneal@1
|
97 """Returns a list of the vertices in the graph."""
|
bgneal@1
|
98
|
bgneal@1
|
99 return self.keys()
|
bgneal@1
|
100
|
bgneal@1
|
101 def edges(self):
|
bgneal@1
|
102 """"Returns a list of the edges in the graph."""
|
bgneal@1
|
103
|
bgneal@1
|
104 edge_set = set()
|
bgneal@1
|
105 for x in self.iterkeys():
|
bgneal@1
|
106 for e in self[x].itervalues():
|
bgneal@1
|
107 edge_set.add(e)
|
bgneal@1
|
108
|
bgneal@1
|
109 return list(edge_set)
|
bgneal@1
|
110
|
bgneal@1
|
111 def out_vertices(self, v):
|
bgneal@1
|
112 """Returns a list of vertices that are adjacent to the given vertex v.
|
bgneal@1
|
113
|
bgneal@1
|
114 """
|
bgneal@1
|
115 return self[v].keys()
|
bgneal@1
|
116
|
bgneal@1
|
117 def out_edges(self, v):
|
bgneal@1
|
118 """Returns a list of edges connected to a given vertex v."""
|
bgneal@1
|
119
|
bgneal@1
|
120 return self[v].values()
|
bgneal@1
|
121
|
bgneal@1
|
122 def add_all_edges(self):
|
bgneal@1
|
123 """Makes the graph complete by adding edges between all pairs of
|
bgneal@1
|
124 vertices.
|
bgneal@1
|
125
|
bgneal@1
|
126 """
|
bgneal@1
|
127 # Clear all edges first
|
bgneal@1
|
128 for v in self.iterkeys():
|
bgneal@1
|
129 self[v] = {}
|
bgneal@1
|
130
|
bgneal@1
|
131 for v, w in itertools.combinations(self.iterkeys(), 2):
|
bgneal@1
|
132 e = Edge(v, w)
|
bgneal@1
|
133 self[v][w] = e
|
bgneal@1
|
134 self[w][v] = e
|
bgneal@1
|
135
|
bgneal@1
|
136
|
bgneal@1
|
137 def main(script, *args):
|
bgneal@1
|
138 import pprint
|
bgneal@1
|
139
|
bgneal@1
|
140 v = Vertex('v')
|
bgneal@1
|
141 print v
|
bgneal@1
|
142 w = Vertex('w')
|
bgneal@1
|
143 print w
|
bgneal@1
|
144 e = Edge(v, w)
|
bgneal@1
|
145 print e
|
bgneal@1
|
146 g = Graph([v,w], [e])
|
bgneal@1
|
147 pprint.pprint(g)
|
bgneal@1
|
148
|
bgneal@1
|
149 print "g.get_edge(v, w): ", g.get_edge(v, w)
|
bgneal@1
|
150 x = Vertex('x')
|
bgneal@1
|
151 print "g.get_edge(v, x): ", g.get_edge(v, x)
|
bgneal@1
|
152
|
bgneal@1
|
153 g.remove_edge(e)
|
bgneal@1
|
154 pprint.pprint(g)
|
bgneal@1
|
155
|
bgneal@1
|
156 print "vertices: ", g.vertices()
|
bgneal@1
|
157 print "edges: ", g.edges()
|
bgneal@1
|
158
|
bgneal@1
|
159 g.add_edge(e)
|
bgneal@1
|
160 u = Vertex('u')
|
bgneal@1
|
161 e1 = Edge(u, v)
|
bgneal@1
|
162 e2 = Edge(u, w)
|
bgneal@1
|
163 g.add_vertex(u)
|
bgneal@1
|
164 g.add_edge(e1)
|
bgneal@1
|
165 g.add_edge(e2)
|
bgneal@1
|
166 print "Adding vertex u and edges:"
|
bgneal@1
|
167 pprint.pprint(g)
|
bgneal@1
|
168 print "vertices: ", g.vertices()
|
bgneal@1
|
169 print "edges: ", g.edges()
|
bgneal@1
|
170
|
bgneal@1
|
171 print "Out vertices for v: ", g.out_vertices(v)
|
bgneal@1
|
172 print "Out edges for v: ", g.out_edges(v)
|
bgneal@1
|
173
|
bgneal@1
|
174 x = Vertex('x')
|
bgneal@1
|
175 g.add_vertex(x)
|
bgneal@1
|
176 g.add_all_edges()
|
bgneal@1
|
177 pprint.pprint(g)
|
bgneal@1
|
178
|
bgneal@1
|
179
|
bgneal@1
|
180 if __name__ == '__main__':
|
bgneal@1
|
181 import sys
|
bgneal@1
|
182 main(*sys.argv)
|