def bellman_ford(G: WeightedDirectedGraph, s: int) -> OneToAllPathSearchResults: """ Args: G - graph we search distances in s - number (index) of source vertex in G Return: PathSearchResults object """ parent = [undefined_node] * G.V() parent[s] = None dist = [sys.maxsize] * G.V() dist[s] = 0 for i in range(G.V()): changed = False for e in G.edges(): if parent[e.source] == undefined_node and e.source != s: continue alternate_d = dist[e.source] + e.weight if alternate_d < dist[e.dest]: # relaxation step changed = True parent[e.dest] = e.source dist[e.dest] = alternate_d if not changed: break else: # not breaked - then here is a negative cycle return None for i, p in enumerate(parent): if p == undefined_node and i != s: dist[i] = None nodes_list = [PathSearchNode(d, undefined_node, p) for d, p in zip(dist, parent)] return OneToAllPathSearchResults(s, nodes_list)
def floyd_warshall(G: WeightedDirectedGraph) -> AllToAllPathSearchResults: """ Args: G - graph to perform search in """ # basic init result = AllToAllPathSearchResults([]) for i in range(G.V()): nodes_list = [] for j in range(G.V()): d = 0 if i == j else sys.maxsize next_node = None if i == j else undefined_node # noinspection PyTypeChecker nodes_list.append(PathSearchNode(d, next_node, undefined_node)) result.append(OneToAllPathSearchResults(i, nodes_list)) # init as adjacency matrix for e in G.edges(): r = result[e.source][e.dest] if r.distance > e.weight: r.distance = e.weight r.child = e.dest # algo for k in range(G.V()): # intermediate vertex for i in range(G.V()): # source vertex for j in range(G.V()): # destination vertex # shortcuts rij = result[i][j] rik = result[i][k] rkj = result[k][j] if rik.distance == sys.maxsize or rkj.distance == sys.maxsize: continue if rij.distance > rik.distance + rkj.distance: # relaxation step rij.distance = rik.distance + rkj.distance rij.child = result[i][k].child for i in range(G.V()): if result[i][i].distance < 0: # negative cycle return None for i, row in enumerate(result): for j, res in enumerate(row): if res.distance == sys.maxsize: res.distance = None return result
def get_sink(G: WeightedDirectedGraph) -> int: """ Return index of output vertex in graph G Example: >>> get_sink(WeightedDirectedGraph.fromfile(open('wiki_flow.txt'))) 5 >>> get_sink(WeightedDirectedGraph(0,0,[])) Traceback (most recent call last): ... algorithm.no_sink >>> get_sink(WeightedDirectedGraph(3,2, ... [WeightedDirectedEdge(0, 1, 1), WeightedDirectedEdge(0, 2, 1)])) Traceback (most recent call last): ... algorithm.ambigous_sink """ V = [v for v in range(G.V()) if len(G.adj(v)) == 0] if not V: raise no_sink if len(V) > 1: raise ambigous_sink return V[0]
def johnson(G: WeightedDirectedGraph) -> AllToAllPathSearchResults: """ Perform all-to-all graph search Args: G - graph to perform search in Return: Results of search in AllToAllPathSearchResults with filled parent field """ # 1) construct new graph with additional vertex ( Gq = deepcopy(G) q = Gq.add_vertex() # new vertex number for i in range(G.V()): Gq.add_edge(WeightedDirectedEdge(q, i, 0)) # 2) compute distances from q (new vertex) bellman_results = bellman_ford(Gq, q) if bellman_results is None: # negative cycle return None h = bellman_results.distances()[:-1] del Gq # 3) Create modificated graph without negative edges edges_mod = [] for e in G.edges(): new_weight = e.weight + h[e.source] - h[e.dest] edges_mod.append(WeightedDirectedEdge(e.source, e.dest, new_weight)) G_mod = WeightedDirectedGraph(G.V(), G.E(), edges_mod) # 4) Perform dijkstra searches in this graph result = AllToAllPathSearchResults([]) for i in range(G_mod.V()): result.append(dijkstra(G_mod, i)) for j, r in enumerate(result[-1]): if r.distance is not None: r.distance -= h[i] - h[j] return result
def get_source(G: WeightedDirectedGraph) -> int: """ Return index of source vertex in graph G Example: >>> get_source(WeightedDirectedGraph.fromfile(open('wiki_flow.txt'))) 0 >>> get_source(WeightedDirectedGraph(0,0,[])) Traceback (most recent call last): ... algorithm.no_source >>> get_source(WeightedDirectedGraph(2,0,[])) Traceback (most recent call last): ... algorithm.ambigous_source """ in_degree = [0] * G.V() for e in G.edges(): in_degree[e.dest] += 1 V = [v for v in range(G.V()) if in_degree[v] == 0] if not V: raise no_source if len(V) > 1: raise ambigous_source return V[0]
def edmonds_karp(G: WeightedDirectedGraph, s: int, t: int) -> tuple: """ Find maximal flow in graph from source s to sink t Return tuple of (weight: int, edges_with_weights) edges_with_weights is list of tuples of (edge_weight: int, edge: Edge) Example: """ def bfs(): parent = [None] * G.V() parent[s] = s q = deque([]) q.append(s) while q: current = q.popleft() for adjacent in adjacent_to[current]: has_parent = parent[adjacent] is not None left_flow_on_edge = M[current][adjacent] > F[current][adjacent] can_go_backward = F[adjacent][current] > 0 if not has_parent and (left_flow_on_edge or can_go_backward): parent[adjacent] = current q.append(adjacent) if adjacent == t: return parent return parent def path_flow(parent): result = sys.maxsize cur = t while cur != s: # from parent[cur] to cur if M[parent[cur]][cur] > F[parent[cur]][cur]: flow_here = M[parent[cur]][cur] - F[parent[cur]][cur] else: flow_here = F[cur][parent[cur]] cur = parent[cur] result = min(result, flow_here) return result def add_flow_on_path(parent, val): cur = t while cur != s: F[parent[cur]][cur] += val F[cur][parent[cur]] -= val cur = parent[cur] flow_value = 0 F = [[0] * G.V() for _ in range(G.V())] M = [[0] * G.V() for _ in range(G.V())] adjacent_to = [[] for _ in range(G.V())] for edge in G.edges(): M[edge.source][edge.dest] = edge.weight adjacent_to[edge.source].append(edge.dest) adjacent_to[edge.dest].append(edge.source) while True: parent = bfs() if parent[t] is not None: path_flow_val = path_flow(parent) flow_value += path_flow_val add_flow_on_path(parent, path_flow_val) else: flow_edges = [] for source in range(G.V()): for dest, flow in enumerate(F[source]): if flow > 0: flow_edges.append( WeightedDirectedEdge(source, dest, flow) ) return flow_value, \ WeightedDirectedGraph(G.V(), len(flow_edges), flow_edges)
#!/usr/bin/env python3 from common import IO, UserIO from graph import algorithm from graph.weighted.directed import Graph io = IO() userio = UserIO() G = Graph.fromfile(io.filein) # later defined source and destination vertexes s = 0 t = 0 # Floyd-Warshall's part io.section('Floyd-Warshall\'s algorithm') search_results = algorithm.floyd_warshall(G) if search_results is None: io.print('Cannot find distance using Floyd-Warshall - negative cycles are ' 'present') else: # print distance matrix io.print('Distance matrix: ') io.print_matrix(algorithm.distance_matrix(search_results), 's\\t') # ask user for source and destination vertexes s = userio.readvertex(G.V(), 'source') t = userio.readvertex(G.V(), 'destination') # print path from s to t path = algorithm.forwardtrace_path_from_all_to_all(search_results, s, t)