Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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]
Ejemplo n.º 4
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
Ejemplo n.º 5
0
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]
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
#!/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)