def test_prim(self): g = graph.Graph() for i in range(0, 6): v = vertex.Vertex(i) g.add_vertex(v) e1 = edge.Edge(0, 1, {"WEIGHT": 4}) g.add_edge(e1) e2 = edge.Edge(0, 2, {"WEIGHT": 1}) g.add_edge(e2) e3 = edge.Edge(0, 3, {"WEIGHT": 5}) g.add_edge(e3) e4 = edge.Edge(1, 3, {"WEIGHT": 2}) g.add_edge(e4) e5 = edge.Edge(1, 4, {"WEIGHT": 3}) g.add_edge(e5) e6 = edge.Edge(1, 5, {"WEIGHT": 3}) g.add_edge(e6) e7 = edge.Edge(2, 3, {"WEIGHT": 2}) g.add_edge(e7) e8 = edge.Edge(2, 4, {"WEIGHT": 8}) g.add_edge(e8) e9 = edge.Edge(3, 4, {"WEIGHT": 1}) g.add_edge(e9) e10 = edge.Edge(4, 5, {"WEIGHT": 3}) g.add_edge(e10) primg = g.Prim() amount = 0 for k in primg.edges: amount = amount + primg.edges[k].attr["WEIGHT"] self.assertEqual(amount, 9)
def dorogovtsev_mendes(n, directed=False): """ Create a Dorogovtsev-Mendes graph :param n: number of nodes :param directed: enable graph directed :return: graph created """ # Parameter's validation if n < 3: raise ValueError("n parameter must to be >= 3 ") g = graph.Graph() # Add attribute DIRECTED in graph g.attr[graph.DIRECTED] = directed # Create 3 vertex and 3 edges to form triangle for i in range(3): g.add_vertex(vertex.Vertex(i)) for i in range(3): j = i + 1 if i < 2 else 0 g.add_edge(edge.Edge(i, j), directed) # To add next vertices one by one, choosing randomly one edge of the grap # and create edges between new vertice and origin and source of edge selected for i in range(3, n): g.add_vertex(vertex.Vertex(i)) # Select random edge of the graph id_edge = randint(0, len(g.get_edges()) - 1) edge_selected = g.get_edges()[id_edge] (source, target) = edge_selected # Create edges between new vertice and origin and source of edge selected g.add_edge(edge.Edge(i, source), directed) g.add_edge(edge.Edge(i, target), directed) return g
def mesh(m, n, directed=False): """ Creates a graph of m*n nodes :param m: number of columns (>1) :param n: number of rows (>1) :param directed: enable graph directed :return: Graph created """ # Parameter's validation if m <= 1 or n <= 1: raise ValueError("m,n parameters must to be > 1") g = graph.Graph() # Add attribute DIRECTED in graph g.attr[graph.DIRECTED] = directed for i in range(m * n): v = vertex.Vertex(i) g.add_vertex(v) index = 0 for i in range(m): for j in range(n): if i != (m - 1): g.add_edge(edge.Edge(index, (index + n)), directed) if j != (n - 1): g.add_edge(edge.Edge(index, (index + 1)), directed) index = index + 1 return g
def dijkstra_tree(self, s): """ dijkstra_tree is an algorithm for finding tree of cost for each node according Dijkstra's algorithm. :param s: node source :param t: node target :return g graph generated with the shortest path from source to target """ l = [] dist = {} prev = {} discovered = {} g = Graph(attr={DIRECTED: True}) g.add_vertex(vertex.Vertex(s, {"WEIGHT": 0})) for v in self.get_vertices(): dist[v] = float('inf') prev[v] = None discovered[v] = False dist[s] = 0 l.append((s, dist[s])) while len(l) != 0: u = min(l, key=lambda x: x[1]) l.remove(u) u = u[0] discovered[u] = True for v in self.get_adjacent_vertices_by_vertex(u): if not discovered[v]: alt = dist[u] + self.get_edge((u, v)).attr["WEIGHT"] if alt < dist[v]: dist[v] = alt prev[v] = u l.append((v, dist[v])) g.add_vertex(vertex.Vertex(v, {"WEIGHT": dist[v]})) g.add_edge(edge.Edge(u, v, {"WEIGHT": dist[v]})) return g
def erdos_rengy(n, m, directed=False, auto=False): """ Creates a graph of n nodes with model Erdos-Renyi :param n: number of nodes ( > 0) :param m: number of edges ( >= n-1) :param directed: enable graph directed :param auto: allow auto-cycle (loops) :return: Graph created """ # Parameter's validation if n <= 0: raise ValueError("n parameter must to be > 0 ") if m < n - 1: raise ValueError("m parameter must to be >= n-1 ") g = graph.Graph() # Add attribute DIRECTED in graph g.attr[graph.DIRECTED] = directed for i in range(n): g.add_vertex(vertex.Vertex(i)) edges = {} while len(g.edges) != m: # Create random m different edges source = randint(0, m - 1) target = randint(0, m - 1) e = (source, target) if e not in edges: edges[e] = e g.add_edge(edge.Edge(source, target), directed, auto) return g
def gilbert(n, p, directed=False, auto=False): """ Creates a graph of n nodes with model Gilbert :param n: number of nodes ( > 0) :param p: probability to create an edge (0,1) :param directed: enable graph directed :param auto: allow auto-cycle (loops) :return: Graph created """ # Parameter's validation if n <= 0: raise ValueError("n parameter must to be > 0 ") if p <= 0 or p >= 1: raise ValueError("p parameter must to be in range (0,1)") g = graph.Graph() # Add attribute DIRECTED in graph g.attr[graph.DIRECTED] = directed for i in range(n): g.add_vertex(vertex.Vertex(i)) for i in range(n): for j in range(n): # Create edge with probability => random number (0,1) if random() <= p: g.add_edge(edge.Edge(i, j), directed, auto) return g
def dfs(self, s): """ dfs Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. The algorithm starts at the root node and explores as far as possible along each branch before backtracking. :param s: root node for traversing :return g graph generated according DFS """ g = Graph(attr={DIRECTED: True}) adjacent_type = '+' if DIRECTED in self.attr and self.attr[ DIRECTED] else None # Insert s root node in stack stack = collections.deque() # Initial node does not have origin, it is represented by # stack.append(('#', s)) while (len(stack) > 0): (source, target) = stack.pop() w = self.get_vertex(target) if DISCOVERED not in w.attributes or w.attributes[ DISCOVERED] is False: w.attributes[DISCOVERED] = True g.add_vertex(w) if (source != '#'): g.add_edge(edge.Edge(source, w.id), True) for e in self.get_adjacent_vertices_by_vertex(w.id, adjacent_type): stack.append((w.id, e)) return g
def bfs(self, s): """ bfs Breadth-first search (BFS) is an algorithm for traversing or searching graph data structures. It starts at the s node and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level. :param s: root node for traversing :return g graph generated according BFS """ g = Graph(attr={DIRECTED: True}) root = self.get_vertex(s) root.attributes[DISCOVERED] = True q = collections.deque() adjacent_type = '+' if DIRECTED in self.attr and self.attr[ DIRECTED] else None # Insert root node in graph and queue g.add_vertex(root) q.append(s) while (len(q) > 0): v = q.popleft() for e in self.get_adjacent_vertices_by_vertex(v, adjacent_type): w = self.get_vertex(e) if DISCOVERED not in w.attributes or w.attributes[ DISCOVERED] is False: w.attributes[DISCOVERED] = True q.append(w.id) g.add_vertex(w) g.add_edge(edge.Edge(v, e), True) return g
def KruskalD(self): """ KruskalD is a function based on Krustal's algorithm to find a minimum spanning forest of an undirected edge-weighted graph. :return g graph representing minimum spannng forest """ g = Graph(attr={DIRECTED: False}) # Create set for each v of V[G] parent = [] rank = [] for v in self.get_vertices(): parent.append(v) rank.append(0) # Sort edges by weight q = sorted(self.edges.items(), key=lambda e: e[1].attr["WEIGHT"]) for e in q: (u, v) = e[0] v1 = self.find(parent, u) v2 = self.find(parent, v) if v1 != v2: g.add_vertex(vertex.Vertex(u)) g.add_vertex(vertex.Vertex(v)) g.add_edge(edge.Edge(u, v, {"WEIGHT": e[1].attr["WEIGHT"]})) if rank[v1] < rank[v2]: parent[v1] = v2 rank[v2] += 1 else: parent[v2] = v1 rank[v1] += 1 return g
def test_add_edge(self): g = graph.Graph() v1 = vertex.Vertex(1) v2 = vertex.Vertex(2) g.add_vertex(v1) g.add_vertex(v2) e = edge.Edge(1, 2) g.add_edge(e) self.assertEqual(1, len(g.get_edges()))
def test_edges(self): g = graph.Graph() v = vertex.Vertex(1) g.add_vertex(v) v = vertex.Vertex(2) g.add_vertex(v) e = edge.Edge(1, 2) g.add_edge(e) self.assertEqual([(1, 2)], g.get_edges())
def test_get_by_vertex(self): g = graph.Graph() v = vertex.Vertex(1) g.add_vertex(v) v = vertex.Vertex(2) g.add_vertex(v) v = vertex.Vertex(3) g.add_vertex(v) e = edge.Edge(1, 2) g.add_edge(e) self.assertEqual([(1, 2)], g.get_edges_by_vertex(1)) e = edge.Edge(1, 3) g.add_edge(e) self.assertEqual(2, len(g.get_edges_by_vertex(1))) e = edge.Edge(2, 1) g.add_edge(e, True) self.assertEqual(3, len(g.get_edges_by_vertex(1, 0))) self.assertEqual(2, len(g.get_edges_by_vertex(1, 1))) self.assertEqual(1, len(g.get_edges_by_vertex(1, 2)))
def test_dfs_r_simple_8(self): g = graph.Graph(attr={graph.DIRECTED: True}) for i in range(1, 9): v = vertex.Vertex(i) g.add_vertex(v) e = edge.Edge(1, 2) g.add_edge(e) e = edge.Edge(1, 3) g.add_edge(e) e = edge.Edge(1, 4) g.add_edge(e) e = edge.Edge(2, 5) g.add_edge(e) e = edge.Edge(5, 7) g.add_edge(e) e = edge.Edge(7, 8) g.add_edge(e) e = edge.Edge(3, 6) g.add_edge(e) e = edge.Edge(6, 8) g.add_edge(e) e = edge.Edge(6, 7) g.add_edge(e) g2 = g.dfs_r(1) dot = g2.create_graphviz('dfs') print(dot) gbase = '''digraph { 1 [label=1] 2 [label=2] 5 [label=5] 7 [label=7] 8 [label=8] 3 [label=3] 6 [label=6] 4 [label=4] 1 -> 2 2 -> 5 5 -> 7 7 -> 8 1 -> 3 3 -> 6 1 -> 4 }''' # dot.render('dfs_r',view=True) self.assertEqual(gbase, str(dot))
def test_dijkstra_simple_3(self): g = graph.Graph() for i in range(1, 6): v = vertex.Vertex(i) g.add_vertex(v) e = edge.Edge(1, 2, {"WEIGHT": 1}) g.add_edge(e) e = edge.Edge(2, 4, {"WEIGHT": 1}) g.add_edge(e) e = edge.Edge(4, 5, {"WEIGHT": 1}) g.add_edge(e) e = edge.Edge(1, 3, {"WEIGHT": 5}) g.add_edge(e) e = edge.Edge(3, 4, {"WEIGHT": 3}) g.add_edge(e) dot = g.create_graphviz('dijkstra_original_3', attr_label_edge="WEIGHT") dot.render('dijkstra_3_original', view=True) result = g.dijkstra(1, 5) print(result) dot = result.create_graphviz('dijkstra_calculado_3', "WEIGHT", 1)
def dfs_rec(self, g, s): adjacent_type = '+' if DIRECTED in self.attr and self.attr[ DIRECTED] else None (source, target) = s w = self.get_vertex(target) if DISCOVERED not in w.attributes or w.attributes[DISCOVERED] is False: w.attributes[DISCOVERED] = True g.add_vertex(w) if (source != '#'): g.add_edge(edge.Edge(source, w.id), True) for e in self.get_adjacent_vertices_by_vertex(w.id, adjacent_type): self.dfs_rec(g, (w.id, e)) return g
def barabasi(n, d, directed=False, auto=False): """ Create Barabasi-Albert (BA) graph :param n: number of nodes ( > 0) :param d: max number of edges of vertex ( > 1) :param directed: enable graph directed :param auto: allow auto-cycle (loops) return: graph created """ # Parameter's validation if n <= 0: raise ValueError("n parameter must to be > 0 ") if d <= 1: raise ValueError("d parameter must to be > 1") g = graph.Graph() # Add attribute DIRECTED in graph g.attr[graph.DIRECTED] = directed # The first d vertices are created with edges to relate each one with the others for i in range(d): g.add_vertex(vertex.Vertex(i)) for i in range(d): for j in range(d): if len(g.get_edges_by_vertex(i)) < d and len( g.get_edges_by_vertex(j)) < d: g.add_edge(edge.Edge(i, j), directed, auto) for i in range(d, n): g.add_vertex(vertex.Vertex(i)) for j in range(i): # The probability p that the new node i is connected to node j # is the grade of vertex j divided by the number of edges of graph p = len(g.get_edges_by_vertex(j)) / len(g.get_edges()) if len(g.get_edges_by_vertex(i)) < d and len( g.get_edges_by_vertex(j)) < d and p >= random(): g.add_edge(edge.Edge(i, j), directed, auto) return g
def dijkstra(self, s, t): """ dijkstra is an algorithm for finding the shortest paths between nodes in a graph. :param s: node source :param t: node target :return g graph generated with the shortest path from source to target """ l = [] dist = {} prev = {} discovered = {} for v in self.get_vertices(): dist[v] = float('inf') prev[v] = None discovered[v] = False dist[s] = 0 l.append((s, dist[s])) while len(l) != 0: u = min(l, key=lambda x: x[1]) l.remove(u) u = u[0] discovered[u] = True if u == t: break for v in self.get_adjacent_vertices_by_vertex(u): if not discovered[v]: alt = dist[u] + self.get_edge((u, v)).attr["WEIGHT"] if alt < dist[v]: dist[v] = alt prev[v] = u l.append((v, dist[v])) # Create a graph according to visited nodes store in prev array u = t g = Graph(attr={DIRECTED: True}) while u is not None: g.add_vertex(vertex.Vertex(u, {"WEIGHT": dist[u]})) if prev[u] is not None: g.add_vertex(vertex.Vertex(prev[u], {"WEIGHT": dist[prev[u]]})) g.add_edge(edge.Edge(prev[u], u)) u = prev[u] else: break return g
def Prim(self): """ Prim is a function based on Prim's algorithm to find a minimum spanning forest of an undirected edge-weighted graph. :return g graph representing minimum spannng forest """ g = Graph(attr={DIRECTED: False}) distance = [sys.maxsize] * len(self.vertices) parent = [None] * len(self.vertices) set = [False] * len(self.vertices) distance[0] = 0 parent[0] = -1 for i in self.vertices: # Search vertex with minimum distance min_index = 0 min = sys.maxsize for v in self.vertices: if distance[v] < min and set[v] is False: min = distance[v] min_index = v u = min_index # Add u vertex in set to not use it in other iteration set[u] = True g.add_vertex(vertex.Vertex(u)) # Iterate all adjacent vertices of u vertex and update distance for v in self.get_adjacent_vertices_by_vertex(u): if set[v] is False and distance[v] > \ self.get_edge((u, v)).attr["WEIGHT"]: distance[v] = self.get_edge((u, v)).attr["WEIGHT"] parent[v] = u for i in self.vertices: if i == 0: continue if parent[i] is not None: g.add_edge(edge.Edge(parent[i], i, {"WEIGHT": self.get_edge((parent[i], i)).attr["WEIGHT"]})) return g
def geo_simple(n, r, directed=False, auto=False): """ Create a random graph with simple method geographic :param n: number of vertices ( > 0) :param r: max distance to generate edge between nodes (0,1) :param directed: enable graph directed :param auto: allow auto-cycle (loops) :return: graph created """ # Parameter's validation if n <= 0: raise ValueError("n parameter must to be > 0 ") if r <= 0 or r >= 1: raise ValueError("r parameter must to be in range (0,1)") g = graph.Graph() # Add attribute DIRECTED in graph g.attr[graph.DIRECTED] = directed # Create n nodes with uniform coordinates for i in range(n): g.add_vertex( vertex.Vertex(i, { COORDINATE_X: random(), COORDINATE_Y: random() })) # Create edge between two vertex if there is a distance <= r for i in range(n): for j in range(n): # Calculate distance between two points p1 = (g.get_vertex(i).attributes[COORDINATE_X], g.get_vertex(i).attributes[COORDINATE_Y]) p2 = (g.get_vertex(j).attributes[COORDINATE_X], g.get_vertex(j).attributes[COORDINATE_Y]) d = calculate_distance(p1, p2) if d <= r: g.add_edge(edge.Edge(i, j), directed, auto) return g
def test_initialize_edge(self): e = edge.Edge(1, 2) self.assertEqual((1, 2), e.get_id())
def test_bfs_simple_10(self): g = graph.Graph() for i in range(1, 11): v = vertex.Vertex(i) g.add_vertex(v) e = edge.Edge(1, 2) g.add_edge(e) e = edge.Edge(1, 3) g.add_edge(e) e = edge.Edge(1, 4) g.add_edge(e) e = edge.Edge(1, 5) g.add_edge(e) e = edge.Edge(2, 6) g.add_edge(e) e = edge.Edge(2, 7) g.add_edge(e) e = edge.Edge(6, 9) g.add_edge(e) e = edge.Edge(9, 10) g.add_edge(e) e = edge.Edge(3, 7) g.add_edge(e) e = edge.Edge(3, 8) g.add_edge(e) e = edge.Edge(4, 8) g.add_edge(e) e = edge.Edge(5, 10) g.add_edge(e) g2 = g.bfs(1) dot = g2.create_graphviz('bfs') gbase = '''digraph { 1 [label=1] 2 [label=2] 3 [label=3] 4 [label=4] 5 [label=5] 6 [label=6] 7 [label=7] 8 [label=8] 10 [label=10] 9 [label=9] 1 -> 2 1 -> 3 1 -> 4 1 -> 5 2 -> 6 2 -> 7 3 -> 8 5 -> 10 6 -> 9 }''' # dot.render('bfs',view=True) self.assertEqual(gbase, str(dot))
def test_dfs_simple_10(self): g = graph.Graph(attr={graph.DIRECTED: True}) for i in range(1, 11): v = vertex.Vertex(i) g.add_vertex(v) e = edge.Edge(1, 2) g.add_edge(e) e = edge.Edge(1, 3) g.add_edge(e) e = edge.Edge(1, 4) g.add_edge(e) e = edge.Edge(1, 5) g.add_edge(e) e = edge.Edge(2, 6) g.add_edge(e) e = edge.Edge(2, 7) g.add_edge(e) e = edge.Edge(6, 9) g.add_edge(e) e = edge.Edge(9, 10) g.add_edge(e) e = edge.Edge(3, 7) g.add_edge(e) e = edge.Edge(3, 8) g.add_edge(e) e = edge.Edge(4, 8) g.add_edge(e) e = edge.Edge(5, 10) g.add_edge(e) g2 = g.dfs(1) dot = g2.create_graphviz('dfs') gbase = '''digraph { 1 [label=1] 5 [label=5] 10 [label=10] 4 [label=4] 8 [label=8] 3 [label=3] 7 [label=7] 2 [label=2] 6 [label=6] 9 [label=9] 1 -> 5 5 -> 10 1 -> 4 4 -> 8 1 -> 3 3 -> 7 1 -> 2 2 -> 6 6 -> 9 }''' # dot.render('dfs',view=True) self.assertEqual(gbase, str(dot))