Skip to main content
edited tags
Link
200_success
  • 145.6k
  • 22
  • 191
  • 481
Fixed code typo
Source Link
import sqlite3
import collections
import itertools

def bidirectional_BFS(connection, source, target):
    """
    Returns all the shortest paths between 'source' and 'target'.
    """
    source_queue = collections.deque((source,))
    target_queue = collections.deque((target,))

    source_visited = {source: 0} # Maps a node to it's distance from the source
    target_visited = {target: 0} # Maps a node to it's distance from the target

    # Keeps track all the ways to get to a given node
    source_parent = collections.defaultdict(set)
    target_parent = collections.defaultdict(set)

    source_parent[source].add(None)
    target_parent[target].add(None)

    found_path    = False
    source_deapth = 0
    target_deapth = 0

    # The set of all intersections between the two searches
    intersections = set()

    while source_queue and target_queue:
        if found_path and (source_visited[source_queue[0]] > source_deapth and target_visited[target_queue[0]] > target_deapth):
            # We are done. All nodes at the current deapth has been explored
            intersections = filter_intersections(source_visited, target_visited, intersections)
            return construct_path(source_parent, target_parent, source, target, intersections)

        if found_path and source_visited[source_queue[0]] > source_deapth:
            # Found a path but the BFS from the target still has more nodes to explore
            target_added, t_deapth = BFS_target(target_queue, target_visited, target_parent, connection)
            intersections |= target_added & source_visited.keys()
        elif found_path and target_visited[target_queue[0]] > target_deapth:
            # Found a path but the BFS from the source still has more nodes to explore
            source_added, s_deapth = BFS_source(source_queue, source_visited, source_parent, connection)
            intersections |= source_added & target_visited.keys()
        else:
            source_added, s_deapth = BFS_source(source_queue, source_visited, source_parent, connection)
            target_added, t_deapth = BFS_target(target_queue, target_visited, target_parent, connection)

            intersections |= source_added & target_visited.keys()
            intersections |= target_added & source_visited.keys()

        if not found_path and intersections:
            # We found a path so we look the search deapth to the current deapth
            found_path = True
            source_deapth = s_deapth
            target_deapth = t_deapth

    if found_path:
        return construct_path(source_parent, target_parent, source, target, intersections)
    else:
        return None

def filter_intersections(source_visited, target_visited, intersections):
    """
    Returns only the intersections where the combined distance from source
    to the intersection and from target to the intersect are the smallest
    """
    filterd = set()
    shortest = float('inf')
    for intersection in intersections:
        if source_visited[intersection] + target_visited[intersection] < shortest:
            shortest = source_visited[intersection] + target_visited[intersection]
            filterd = {intersection}
        elif source_visited[intersection] + target_visited[intersection] == shortest:
            filterd.add(intersection)
    return filterd

def construct_path(source_parent, target_parent, source, target, intersections):
    """
    Constructs all paths and returns a list of list where each list is one path
    """
    paths = set()

    for intersection in intersections:
        from_source_to_inter = construct_path_from_to(source_parent, source, intersection)
        from_inter_to_target = construct_path_from_to(target_parent, target, intersection, reverse=True)
        for path in itertools.product(from_source_to_inter, from_inter_to_target):
            paths.add(tuple(path[0] + path[1][1:]))
    return paths
        
def construct_path_from_to(source_parent, target, start, reverse=False):
    """
    Constructs all paths between start and target recursivly. If reverse is true
    then all the paths are reversed.
    """
    if start == target:
        return [[target]]

    paths = []
    for parent in source_parent[start]:
        for path in construct_path_from_to(source_parent, target, parent, reverse):
            if reverse:
                path.insert(0, start)
            else:
                path.append(start)
            paths.append(path)

    return paths

def BFS_source(queue, visited, parent, connection):
    """
    Runs one iteration of the BFS from source to the target and then returns
    the edges explored during the iteration and the current deapth of the search
    """
    current = queue.popleft()

    added = set()

    for (neighbor,) in connection.execute('SELECT edge_target FROM edges WHERE edge_from= ?;', (current,)):
        if neighbor not in visited:
            parent[neighbor].add(current)
            visited[neighbor] = visited[current] + 1
            queue.append(neighbor)
            added.add(neighbor)
        elif visited[current] + 1 == visited[neighbor]:
            parent[neighbor].add(current)

    return added, visited[current]

def BFS_target(queue, visited, parent, connection):
    """
    Runs one iteration of the BFS from target to source and then returns
    the edges explored during the iteration and the current deapth of the search
    """
    current = queue.popleft()

    added = set()

    for (neighbor,) in connection.execute('SELECT edge_from FROM edges WHERE edge_target = ?;', (current,)):
        if neighbornotneighbor not in visited:
            parent[neighbor].add(current)
            visited[neighbor] = visited[current] + 1
            queue.append(neighbor)
            added.add(neighbor)
        elif visited[current] + 1 == visited[neighbor]:
            parent[neighbor].add(current)

    return added, visited[current]
import sqlite3
import collections
import itertools

def bidirectional_BFS(connection, source, target):
    """
    Returns all the shortest paths between 'source' and 'target'.
    """
    source_queue = collections.deque((source,))
    target_queue = collections.deque((target,))

    source_visited = {source: 0} # Maps a node to it's distance from the source
    target_visited = {target: 0} # Maps a node to it's distance from the target

    # Keeps track all the ways to get to a given node
    source_parent = collections.defaultdict(set)
    target_parent = collections.defaultdict(set)

    source_parent[source].add(None)
    target_parent[target].add(None)

    found_path    = False
    source_deapth = 0
    target_deapth = 0

    # The set of all intersections between the two searches
    intersections = set()

    while source_queue and target_queue:
        if found_path and (source_visited[source_queue[0]] > source_deapth and target_visited[target_queue[0]] > target_deapth):
            # We are done. All nodes at the current deapth has been explored
            intersections = filter_intersections(source_visited, target_visited, intersections)
            return construct_path(source_parent, target_parent, source, target, intersections)

        if found_path and source_visited[source_queue[0]] > source_deapth:
            # Found a path but the BFS from the target still has more nodes to explore
            target_added, t_deapth = BFS_target(target_queue, target_visited, target_parent, connection)
            intersections |= target_added & source_visited.keys()
        elif found_path and target_visited[target_queue[0]] > target_deapth:
            # Found a path but the BFS from the source still has more nodes to explore
            source_added, s_deapth = BFS_source(source_queue, source_visited, source_parent, connection)
            intersections |= source_added & target_visited.keys()
        else:
            source_added, s_deapth = BFS_source(source_queue, source_visited, source_parent, connection)
            target_added, t_deapth = BFS_target(target_queue, target_visited, target_parent, connection)

            intersections |= source_added & target_visited.keys()
            intersections |= target_added & source_visited.keys()

        if not found_path and intersections:
            # We found a path so we look the search deapth to the current deapth
            found_path = True
            source_deapth = s_deapth
            target_deapth = t_deapth

    if found_path:
        return construct_path(source_parent, target_parent, source, target, intersections)
    else:
        return None

def filter_intersections(source_visited, target_visited, intersections):
    """
    Returns only the intersections where the combined distance from source
    to the intersection and from target to the intersect are the smallest
    """
    filterd = set()
    shortest = float('inf')
    for intersection in intersections:
        if source_visited[intersection] + target_visited[intersection] < shortest:
            shortest = source_visited[intersection] + target_visited[intersection]
            filterd = {intersection}
        elif source_visited[intersection] + target_visited[intersection] == shortest:
            filterd.add(intersection)
    return filterd

def construct_path(source_parent, target_parent, source, target, intersections):
    """
    Constructs all paths and returns a list of list where each list is one path
    """
    paths = set()

    for intersection in intersections:
        from_source_to_inter = construct_path_from_to(source_parent, source, intersection)
        from_inter_to_target = construct_path_from_to(target_parent, target, intersection, reverse=True)
        for path in itertools.product(from_source_to_inter, from_inter_to_target):
            paths.add(tuple(path[0] + path[1][1:]))
    return paths
        
def construct_path_from_to(source_parent, target, start, reverse=False):
    """
    Constructs all paths between start and target recursivly. If reverse is true
    then all the paths are reversed.
    """
    if start == target:
        return [[target]]

    paths = []
    for parent in source_parent[start]:
        for path in construct_path_from_to(source_parent, target, parent, reverse):
            if reverse:
                path.insert(0, start)
            else:
                path.append(start)
            paths.append(path)

    return paths

def BFS_source(queue, visited, parent, connection):
    """
    Runs one iteration of the BFS from source to the target and then returns
    the edges explored during the iteration and the current deapth of the search
    """
    current = queue.popleft()

    added = set()

    for (neighbor,) in connection.execute('SELECT edge_target FROM edges WHERE edge_from= ?;', (current,)):
        if neighbor not in visited:
            parent[neighbor].add(current)
            visited[neighbor] = visited[current] + 1
            queue.append(neighbor)
            added.add(neighbor)
        elif visited[current] + 1 == visited[neighbor]:
            parent[neighbor].add(current)

    return added, visited[current]

def BFS_target(queue, visited, parent, connection):
    """
    Runs one iteration of the BFS from target to source and then returns
    the edges explored during the iteration and the current deapth of the search
    """
    current = queue.popleft()

    added = set()

    for (neighbor,) in connection.execute('SELECT edge_from FROM edges WHERE edge_target = ?;', (current,)):
        if neighbornot in visited:
            parent[neighbor].add(current)
            visited[neighbor] = visited[current] + 1
            queue.append(neighbor)
            added.add(neighbor)
        elif visited[current] + 1 == visited[neighbor]:
            parent[neighbor].add(current)

    return added, visited[current]
import sqlite3
import collections
import itertools

def bidirectional_BFS(connection, source, target):
    """
    Returns all the shortest paths between 'source' and 'target'.
    """
    source_queue = collections.deque((source,))
    target_queue = collections.deque((target,))

    source_visited = {source: 0} # Maps a node to it's distance from the source
    target_visited = {target: 0} # Maps a node to it's distance from the target

    # Keeps track all the ways to get to a given node
    source_parent = collections.defaultdict(set)
    target_parent = collections.defaultdict(set)

    source_parent[source].add(None)
    target_parent[target].add(None)

    found_path    = False
    source_deapth = 0
    target_deapth = 0

    # The set of all intersections between the two searches
    intersections = set()

    while source_queue and target_queue:
        if found_path and (source_visited[source_queue[0]] > source_deapth and target_visited[target_queue[0]] > target_deapth):
            # We are done. All nodes at the current deapth has been explored
            intersections = filter_intersections(source_visited, target_visited, intersections)
            return construct_path(source_parent, target_parent, source, target, intersections)

        if found_path and source_visited[source_queue[0]] > source_deapth:
            # Found a path but the BFS from the target still has more nodes to explore
            target_added, t_deapth = BFS_target(target_queue, target_visited, target_parent, connection)
            intersections |= target_added & source_visited.keys()
        elif found_path and target_visited[target_queue[0]] > target_deapth:
            # Found a path but the BFS from the source still has more nodes to explore
            source_added, s_deapth = BFS_source(source_queue, source_visited, source_parent, connection)
            intersections |= source_added & target_visited.keys()
        else:
            source_added, s_deapth = BFS_source(source_queue, source_visited, source_parent, connection)
            target_added, t_deapth = BFS_target(target_queue, target_visited, target_parent, connection)

            intersections |= source_added & target_visited.keys()
            intersections |= target_added & source_visited.keys()

        if not found_path and intersections:
            # We found a path so we look the search deapth to the current deapth
            found_path = True
            source_deapth = s_deapth
            target_deapth = t_deapth

    if found_path:
        return construct_path(source_parent, target_parent, source, target, intersections)
    else:
        return None

def filter_intersections(source_visited, target_visited, intersections):
    """
    Returns only the intersections where the combined distance from source
    to the intersection and from target to the intersect are the smallest
    """
    filterd = set()
    shortest = float('inf')
    for intersection in intersections:
        if source_visited[intersection] + target_visited[intersection] < shortest:
            shortest = source_visited[intersection] + target_visited[intersection]
            filterd = {intersection}
        elif source_visited[intersection] + target_visited[intersection] == shortest:
            filterd.add(intersection)
    return filterd

def construct_path(source_parent, target_parent, source, target, intersections):
    """
    Constructs all paths and returns a list of list where each list is one path
    """
    paths = set()

    for intersection in intersections:
        from_source_to_inter = construct_path_from_to(source_parent, source, intersection)
        from_inter_to_target = construct_path_from_to(target_parent, target, intersection, reverse=True)
        for path in itertools.product(from_source_to_inter, from_inter_to_target):
            paths.add(tuple(path[0] + path[1][1:]))
    return paths
        
def construct_path_from_to(source_parent, target, start, reverse=False):
    """
    Constructs all paths between start and target recursivly. If reverse is true
    then all the paths are reversed.
    """
    if start == target:
        return [[target]]

    paths = []
    for parent in source_parent[start]:
        for path in construct_path_from_to(source_parent, target, parent, reverse):
            if reverse:
                path.insert(0, start)
            else:
                path.append(start)
            paths.append(path)

    return paths

def BFS_source(queue, visited, parent, connection):
    """
    Runs one iteration of the BFS from source to the target and then returns
    the edges explored during the iteration and the current deapth of the search
    """
    current = queue.popleft()

    added = set()

    for (neighbor,) in connection.execute('SELECT edge_target FROM edges WHERE edge_from= ?;', (current,)):
        if neighbor not in visited:
            parent[neighbor].add(current)
            visited[neighbor] = visited[current] + 1
            queue.append(neighbor)
            added.add(neighbor)
        elif visited[current] + 1 == visited[neighbor]:
            parent[neighbor].add(current)

    return added, visited[current]

def BFS_target(queue, visited, parent, connection):
    """
    Runs one iteration of the BFS from target to source and then returns
    the edges explored during the iteration and the current deapth of the search
    """
    current = queue.popleft()

    added = set()

    for (neighbor,) in connection.execute('SELECT edge_from FROM edges WHERE edge_target = ?;', (current,)):
        if neighbor not in visited:
            parent[neighbor].add(current)
            visited[neighbor] = visited[current] + 1
            queue.append(neighbor)
            added.add(neighbor)
        elif visited[current] + 1 == visited[neighbor]:
            parent[neighbor].add(current)

    return added, visited[current]
Improved devinition of "all shortest paths"
Source Link

I want to find all shortest paths between a pair of vertices in a unweighted graph i.e all paths that have the same length as the shortest. The edges of the graph are stored in a SQL database. The graph has about 460,000,000 edges and 5,600,000 nodes. My approach is to use a bidirectional BFS to find all the shortest paths.

I want to find all shortest paths between a pair of vertices in a unweighted graph stored in a SQL database. The graph has about 460,000,000 edges and 5,600,000 nodes. My approach is to use a bidirectional BFS to find all the shortest paths.

I want to find all shortest paths between a pair of vertices in a unweighted graph i.e all paths that have the same length as the shortest. The edges of the graph are stored in a SQL database. The graph has about 460,000,000 edges and 5,600,000 nodes. My approach is to use a bidirectional BFS to find all the shortest paths.

edited title
Link
VisualMelon
  • 7.6k
  • 23
  • 46
Loading
explain "all shortest paths"
Source Link
Gareth Rees
  • 50.1k
  • 3
  • 130
  • 211
Loading
Fixed typo
Source Link
Loading
Fixed typos
Source Link
Toby Speight
  • 88.3k
  • 14
  • 104
  • 327
Loading
edited title
Link
Loading
Source Link
Loading