bgneal@1: """ Code example from Complexity and Computation, a book about bgneal@1: exploring complexity science with Python. Available free from bgneal@1: bgneal@1: http://greenteapress.com/complexity bgneal@1: bgneal@1: Copyright 2011 Allen B. Downey. bgneal@1: Distributed under the GNU General Public License at gnu.org/licenses/gpl.html. bgneal@1: """ bgneal@1: import itertools bgneal@1: bgneal@1: bgneal@1: class Vertex(object): bgneal@1: """A Vertex is a node in a graph.""" bgneal@1: bgneal@1: def __init__(self, label=''): bgneal@1: self.label = label bgneal@1: bgneal@1: def __repr__(self): bgneal@1: """Returns a string representation of this object that can bgneal@1: be evaluated as a Python expression.""" bgneal@1: return 'Vertex(%s)' % repr(self.label) bgneal@1: bgneal@1: __str__ = __repr__ bgneal@1: """The str and repr forms of this object are the same.""" bgneal@1: bgneal@1: bgneal@1: class Edge(tuple): bgneal@1: """An Edge is a list of two vertices.""" bgneal@1: bgneal@1: def __new__(cls, *vs): bgneal@1: """The Edge constructor takes two vertices.""" bgneal@1: if len(vs) != 2: bgneal@1: raise ValueError, 'Edges must connect exactly two vertices.' bgneal@1: return tuple.__new__(cls, vs) bgneal@1: bgneal@1: def __repr__(self): bgneal@1: """Return a string representation of this object that can bgneal@1: be evaluated as a Python expression.""" bgneal@1: return 'Edge(%s, %s)' % (repr(self[0]), repr(self[1])) bgneal@1: bgneal@1: __str__ = __repr__ bgneal@1: """The str and repr forms of this object are the same.""" bgneal@1: bgneal@1: bgneal@1: class Graph(dict): bgneal@1: """A Graph is a dictionary of dictionaries. The outer bgneal@1: dictionary maps from a vertex to an inner dictionary. bgneal@1: The inner dictionary maps from other vertices to edges. bgneal@1: bgneal@1: For vertices a and b, graph[a][b] maps bgneal@1: to the edge that connects a->b, if it exists.""" bgneal@1: bgneal@1: def __init__(self, vs=[], es=[]): bgneal@1: """Creates a new graph. bgneal@1: vs: list of vertices; bgneal@1: es: list of edges. bgneal@1: """ bgneal@1: for v in vs: bgneal@1: self.add_vertex(v) bgneal@1: bgneal@1: for e in es: bgneal@1: self.add_edge(e) bgneal@1: bgneal@1: def add_vertex(self, v): bgneal@1: """Add a vertex to the graph.""" bgneal@1: self[v] = {} bgneal@1: bgneal@1: def add_edge(self, e): bgneal@1: """Adds and edge to the graph by adding an entry in both directions. bgneal@1: bgneal@1: If there is already an edge connecting these Vertices, the bgneal@1: new edge replaces it. bgneal@1: """ bgneal@1: v, w = e bgneal@1: self[v][w] = e bgneal@1: self[w][v] = e bgneal@1: bgneal@1: def get_edge(self, v, w): bgneal@1: """Returns the edge object that exists between the two vertices v & w, bgneal@1: or None if no such edge exists. bgneal@1: bgneal@1: """ bgneal@1: try: bgneal@1: return self[v][w] bgneal@1: except KeyError: bgneal@1: return None bgneal@1: bgneal@1: def remove_edge(self, e): bgneal@1: """Removes the edge e from the graph.""" bgneal@1: bgneal@4: v, w = e bgneal@4: del self[v][w] bgneal@4: del self[w][v] bgneal@1: bgneal@1: def vertices(self): bgneal@1: """Returns a list of the vertices in the graph.""" bgneal@1: bgneal@1: return self.keys() bgneal@1: bgneal@1: def edges(self): bgneal@1: """"Returns a list of the edges in the graph.""" bgneal@1: bgneal@1: edge_set = set() bgneal@4: for d in self.itervalues(): bgneal@4: edge_set.update(d.itervalues()) bgneal@1: bgneal@1: return list(edge_set) bgneal@1: bgneal@1: def out_vertices(self, v): bgneal@1: """Returns a list of vertices that are adjacent to the given vertex v. bgneal@1: bgneal@1: """ bgneal@1: return self[v].keys() bgneal@1: bgneal@1: def out_edges(self, v): bgneal@1: """Returns a list of edges connected to a given vertex v.""" bgneal@1: bgneal@1: return self[v].values() bgneal@1: bgneal@1: def add_all_edges(self): bgneal@1: """Makes the graph complete by adding edges between all pairs of bgneal@1: vertices. bgneal@1: bgneal@1: """ bgneal@1: # Clear all edges first bgneal@1: for v in self.iterkeys(): bgneal@1: self[v] = {} bgneal@1: bgneal@1: for v, w in itertools.combinations(self.iterkeys(), 2): bgneal@1: e = Edge(v, w) bgneal@1: self[v][w] = e bgneal@1: self[w][v] = e bgneal@1: bgneal@1: bgneal@1: def main(script, *args): bgneal@1: import pprint bgneal@1: bgneal@1: v = Vertex('v') bgneal@1: print v bgneal@1: w = Vertex('w') bgneal@1: print w bgneal@1: e = Edge(v, w) bgneal@1: print e bgneal@1: g = Graph([v,w], [e]) bgneal@1: pprint.pprint(g) bgneal@1: bgneal@1: print "g.get_edge(v, w): ", g.get_edge(v, w) bgneal@1: x = Vertex('x') bgneal@1: print "g.get_edge(v, x): ", g.get_edge(v, x) bgneal@1: bgneal@1: g.remove_edge(e) bgneal@1: pprint.pprint(g) bgneal@1: bgneal@1: print "vertices: ", g.vertices() bgneal@1: print "edges: ", g.edges() bgneal@1: bgneal@1: g.add_edge(e) bgneal@1: u = Vertex('u') bgneal@1: e1 = Edge(u, v) bgneal@1: e2 = Edge(u, w) bgneal@1: g.add_vertex(u) bgneal@1: g.add_edge(e1) bgneal@1: g.add_edge(e2) bgneal@1: print "Adding vertex u and edges:" bgneal@1: pprint.pprint(g) bgneal@1: print "vertices: ", g.vertices() bgneal@1: print "edges: ", g.edges() bgneal@1: bgneal@1: print "Out vertices for v: ", g.out_vertices(v) bgneal@1: print "Out edges for v: ", g.out_edges(v) bgneal@1: bgneal@1: x = Vertex('x') bgneal@1: g.add_vertex(x) bgneal@1: g.add_all_edges() bgneal@1: pprint.pprint(g) bgneal@1: bgneal@1: bgneal@1: if __name__ == '__main__': bgneal@1: import sys bgneal@1: main(*sys.argv)