示例#1
0
def find_two_vertex_disjoint_paths(s, t, y, z, graph, DEBUG=DO_DEBUG):
    # finds 2 vertex disjoint paths in a graph. One from s to t. The other from y to z

    S_to_T_bfs = bfs_with_restriction_set(graph=graph,
                                          start=s,
                                          end=t,
                                          restriction_set=set())

    if (S_to_T_bfs["result"] == False):
        if (DEBUG): print("FAILED ON FIRST BFS. from s -> t", (s, t))
        return {"result": False}

    S_to_T_bfs_path = get_path_to_root(S_to_T_bfs["parents"], X)[::-1]

    # Do second dfs
    if DEBUG: print("S_to_X_bfs_path_found: ", S_to_T_bfs_path)

    Y_to_Z_dfs = dfs_with_restriction_set(graph=graph,
                                          start=y,
                                          end=z,
                                          avoidance_set=set(S_to_T_bfs_path),
                                          restriction_set=set())

    if (Y_to_Z_dfs["result"] == True):
        if (DEBUG): print("IT WAS MERGED WITHIN FIRST BFS AND DFS")
        return {
            "result": True,
            "S_to_X_path": S_to_T_bfs_path,
            "Y_to_Z_path": get_path_to_root(Y_to_Z_dfs["parents"], z)[::-1]
        }
    '''
def brute_force_get_all_solution(graph,
                                 s,
                                 t,
                                 get_shortest_paths_too=False,
                                 DEBUG=False):

    graphBFS = bfs(graph, s)

    bfs_parents_root_s = graphBFS["parents"]
    bfs_dist_root_s = graphBFS["dist"]

    shortest_length = bfs_dist_root_s.get(t)

    if (shortest_length is None):
        # There is no path from s to t, FAIL!
        if (DEBUG):
            print("Hi brute force here. THERE IS NO SHORTEST PATH. FAIL.")
        return {"result": False, "shortest_paths": [], "longer_paths": []}

    a_shortest_path = get_path_to_root(bfs_parents_root_s, t)[::-1]
    '''
    use dfs to find longer path!
    start at s, 
    '''

    if (get_shortest_paths_too):
        shortest_length = 0

    result = find_longer_path(graph, s, t, shortest_length)

    return {
        "shortest_path": a_shortest_path,
        "longer_paths": result["longer_paths"]
    }
def brute_force_solution(graph, s, t, DEBUG=False):

    graphBFS = bfs(graph, s)

    bfs_parents_root_s = graphBFS["parents"]
    bfs_dist_root_s = graphBFS["dist"]

    shortest_length = bfs_dist_root_s.get(t)

    if (shortest_length is None):
        # There is no path from s to t, FAIL!
        if (DEBUG):
            print("Hi brute force here. THERE IS NO SHORTEST PATH. FAIL.")
        return {
            "result": False,
        }

    a_shortest_path = get_path_to_root(bfs_parents_root_s, t)[::-1]
    '''
    use dfs to find longer path!
    start at s, 
    '''

    result = find_longer_path(graph, s, t, shortest_length)
    if (result["found_longer_path"]):
        if (DEBUG): print("FOUND A SHORTER AND LONGER SIMPLE PATH")
        if (DEBUG):
            print("THE SHORTEst PATH IS THE FOLLOWING: ", a_shortest_path)
        if (DEBUG):
            print("the longer path is the following: ",
                  result["a_longer_path"])
        return {
            "result": True,
            "a_shortest_path": a_shortest_path,
            "a_longer_path": result["a_longer_path"]
        }
    else:
        if (DEBUG): print("DID NOT FIND 2 SIMPLE PATHS")
        return {
            "result": False,
        }
示例#4
0
def merge_two_overlapping_paths_in_dag(s,
                                       t,
                                       X,
                                       Y,
                                       shortest_paths_dag,
                                       DEBUG=DO_DEBUG):
    '''
     PSUEDOCODE FOR THE 4 DFS's in this function numbered 1 to 4:

     1) DFS from S->X on shortestPathsDAG and get a path, and store visited vertices in visitedX. 
        if S->X DFS see's vertex Y,  give up on path, continue dfs search with other nodes.
        1a) If DFS fails, and we can't avoid Y, then return False. 
        1b) We found a path. Go to Step 2
     
     2) Then DFS from Y->T on shortestPathsDAG without visiting the vertices from from visitedX or X. 
         2a) If T is reached, we are done. Return True. 
         2b) Otherwise T was not reached. Go to Step 3

     3) Lift restriction that Y->T path can't use visitedX, and try to use visitedX vertices to get to T (this DFS however, still cant visit X). 
        Save visited vertices to visitedY, including the ones we see when we lift the restriction to not use visitedX. 
        3a)  If T still cant be reached because Y->T path still sees X, then FAIL PATH CREATION. Return false. 
        3b)  If T is reached. Go To Step 4
     4) We need to do DFS 1 final time on S->X and attempt to get path, without seeing Y or visitedY. Store the dfs vertices in visitedX2.
                4a) S->X worked. Then Done. Return True.
                4b) S->X did not work. Return false. The piece of the path we gave up to Y->T was the only segment we could use to go 
                    from S->X without seeing Y or its vertices. Y->T needed this segment too, otherwise, 
                    the previous DFS would have suggested another way for Y to reach T.   
                    Fail Path Creation due to contention for this piece of critical segment in the DAG that both S->X and Y->T needed.
    '''

    S_to_X_bfs = bfs_with_restriction_set(graph=shortest_paths_dag,
                                          start=s,
                                          end=X,
                                          restriction_set=set([Y, t]))

    if (S_to_X_bfs["result"] == False):
        if (DEBUG): print("FAILED ON FIRST DFS. from s -> x", (s, X))
        return {"result": False}
    '''
    
                         a_longer_path =  get_path_to_root(longer_path_result["S_to_X_dfs_tree"], x)[::-1] + \
                                          X_to_Z_to_Y_path["crazy_path"][1:] + \
                                          get_path_to_root(longer_path_result["Y_to_T_dfs_tree"], t)[::-1][1:]
    '''
    S_to_X_path = get_path_to_root(S_to_X_bfs["parents"], X)[::-1]

    # Do second dfs
    if DEBUG: print("S_to_X_path_found: ", S_to_X_path)

    Y_to_T_dfs = dfs_with_restriction_set(graph=shortest_paths_dag,
                                          start=Y,
                                          end=t,
                                          restriction_set=set(S_to_X_path))

    if (Y_to_T_dfs["result"] == True):
        if (DEBUG): print("IT WAS MERGED WITHIN 2 DFS'S")
        return {
            "result": True,
            "S_to_X_path": S_to_X_path,
            "Y_to_T_path": get_path_to_root(Y_to_T_dfs["parents"], t)[::-1]
        }

    # The second dfs failed, so move on to the third dfs:
    if DEBUG:
        print("SECOND DFS FAILED move on to third. second was y->T", (Y, t))

    Y_to_T_bfs_2 = bfs_with_restriction_set(graph=shortest_paths_dag,
                                            start=Y,
                                            end=t,
                                            restriction_set=set([X, s]))
    if (Y_to_T_bfs_2["result"] == False):
        if (DEBUG): print("FAILED ON THIRD DFS. Y->T", (Y, t))
        return {"result": False}

    Y_to_T_path_2 = get_path_to_root(Y_to_T_bfs_2["parents"], t)[::-1]

    # do fourth dfs (IMPLEMENT AVOIDANCE HERE!!!!!)
    if (DEBUG): print("Y TO T 2 PATH ", Y_to_T_path_2)
    S_to_X_dfs_2 = dfs_with_restriction_set(graph=shortest_paths_dag,
                                            start=s,
                                            end=X,
                                            restriction_set=set(Y_to_T_path_2))

    if (S_to_X_dfs_2["result"] == True):
        if (DEBUG): print("IT WAS MERGED WITHIN 4 DFS'S")

        return {
            "result": True,
            "S_to_X_path": get_path_to_root(S_to_X_dfs_2["parents"], X)[::-1],
            "Y_to_T_path": Y_to_T_path_2
        }
    else:
        if (DEBUG): print("FAILED ON FOURTH DFS")
        return {"result": False}
示例#5
0
def create_crazy_path_without_overlaps(X,
                                       Y,
                                       Z,
                                       X_to_Z_bfs_tree_parents,
                                       Z_to_Y_bfs_tree_parents,
                                       graph,
                                       shortest_paths_dag_vertices,
                                       DEBUG=DO_DEBUG):

    # BFS from X to Z to get shortest path (visits least numebr of vertices)
    # DFS from Z to Y (if this fails, the shortest path from X to Z had important nodes)
    # If it intersects, then do the following:

    # BFS from Z to Y
    # Then DFS from X to Z

    if DEBUG:
        print(
            "CALLLED CREATE CRAZY PATH WITHOUT OVERLAPPING! #########################################3"
        )
    if DEBUG: print("X is " + str(X) + " Y is " + str(Y) + " Z is " + str(Z))

    shortest_path_x_to_z = get_path_to_root(X_to_Z_bfs_tree_parents, X)
    if (DEBUG):
        print("SHORTEST PATH FROM X TO Z IS THE FOLLOWING ",
              shortest_path_x_to_z)

    dfs_from_z_to_y_restriction_set = set(shortest_path_x_to_z +
                                          shortest_paths_dag_vertices) - set(
                                              [Z, Y])
    dfs_path_from_z_to_y = dfs_with_restriction_set(
        graph=graph,
        start=Z,
        end=Y,
        restriction_set=dfs_from_z_to_y_restriction_set)

    if (DEBUG): print("dfs path from z to y crazy path", dfs_path_from_z_to_y)

    if (dfs_path_from_z_to_y["result"]):
        return {
            "result":
            True,
            "crazy_path":
            shortest_path_x_to_z[:-1] +  # remove z from end
            get_path_to_root(dfs_path_from_z_to_y["parents"], Y)[::-1]
        }

    if (DEBUG):
        print("CRAZY PATH BUILD FAILED ON FIRST ATTEMPT. SECOND ATTEMPT")

    shortest_path_z_to_y = get_path_to_root(Z_to_Y_bfs_tree_parents, Y)

    if (DEBUG):
        print("SHORTEST PATH FROM Z TO Y IS THE FOLLOWING ",
              shortest_path_z_to_y)

    dfs_path_from_x_to_z = dfs_with_restriction_set(
        graph=graph,
        start=X,
        end=Z,
        restriction_set=(
            set(shortest_path_z_to_y + shortest_paths_dag_vertices) -
            set([X, Z])))

    if (DEBUG): print("dfs path from x to z crazy path", dfs_path_from_x_to_z)

    if (dfs_path_from_x_to_z["result"]):
        return {
            "result":
            True,
            "crazy_path":
            get_path_to_root(dfs_path_from_x_to_z["parents"], Z)[::-1][:-1] +
            shortest_path_z_to_y[::-1]  #IDK IF THIS WORKS!!!
        }

    return {
        "result": False,
    }
示例#6
0
def create_shortest_paths_dag(graph, reversed_graph, s, t, DEBUG=DO_DEBUG):

    graphBFS = bfs(graph, s)
    reverseGraphBFS = bfs(reversed_graph, t)

    parentsS = graphBFS["parents"]
    distS = graphBFS["dist"]
    parentsT = reverseGraphBFS["parents"]
    distT = reverseGraphBFS["dist"]

    shortestLength = distS.get(t)  # also equal to distT[s]

    if (shortestLength is None):
        # There is no path from s to t, FAIL!
        return {"is_there_shortest_path": False}

    if (DEBUG): print("shortest_path_length is ", shortestLength)
    if (DEBUG): print("shortest length other way is ", distT[s])

    a_shortest_path = get_path_to_root(parentsS, t)[::-1]

    shortest_paths_dag = defaultdict(set)
    # add shortest path to dag first, add other shortest paths after

    add_path_to_graph(a_shortest_path, shortest_paths_dag)

    vertices_to_test = set(graph.keys())  # - set([s, t])
    if (DEBUG): print("dist S", distS)
    if (DEBUG): print("dist T", distT)

    # shortest_path_vertices = set() # have to maintain seperately

    if (DEBUG): print(vertices_to_test)

    for v in vertices_to_test:
        # if(DEBUG): print("v is ", v)
        dist_from_s_to_v = distS.get(v)
        dist_from_v_to_t = distT.get(v)

        # If the distances exist because they were traversed then
        if (dist_from_s_to_v and dist_from_v_to_t):
            length = dist_from_s_to_v + dist_from_v_to_t  # Add them to find distance of path [S -> v -> T]
            # if(DEBUG): print("length was",  length)

            if (length == shortestLength):
                # Add path [s -> v -> t] to shortest paths subgraph
                path_V_to_S = get_path_to_root(parentsS, v)
                path_V_to_T = get_path_to_root(parentsT,
                                               v)  # remove the V node
                # if(DEBUG): print("path V to S", path_V_to_S)
                # if(DEBUG): print("path V to T", path_V_to_T)

                path_S_to_V_to_T = path_V_to_S[::-1] + path_V_to_T[1::]

                if (DEBUG):
                    print("FOR THIS V, path S to V to T added is", v,
                          path_S_to_V_to_T)
                # add path to subgraph

                add_path_to_graph(path_S_to_V_to_T, shortest_paths_dag)
                # shortest_path_vertices.update(path_S_to_V_to_T)

                # TODO: you can optimize here by subtracting vertices you added from vertices to test.

    # All the vertices for the shortest path dag were added but some
    # edges pointing to other vertices in this dag may be missing
    # Those will be added next.
    vertices_in_dag = set(shortest_paths_dag.keys())

    shortest_paths_dag_with_all_neighbors = defaultdict(set)
    # WE HAVE ALL THE VERTICES FOR THE SHORTEST PATHS DAG AND SOME OF THE EDGES. NOW WE WILL ADD THE MISSING EDGES
    # BETWEEN THESE VERTICES THAT ARE USED IN A SHORTEST PATH FROM S TO T. IF THE EDGE ISNT
    # USED IN A SHORTEST PATH, IT IS CALLED A LOST EDGE IN THE DAG.
    #
    for V in vertices_in_dag:
        all_neighbors_for_vertex = graph[V]
        dag_neighbors_for_vertex = shortest_paths_dag[V]

        all_neighbors_for_vertex_in_dag = vertices_in_dag.intersection(
            all_neighbors_for_vertex)  # set subtraction
        shortest_paths_dag_with_all_neighbors[
            V] = all_neighbors_for_vertex_in_dag

        # THESE EDGE RELATIONSHIPS MAY BE PART OF A SHORTEST PATH FROM S TO T. WE HAVE TO CHECK THAT AND ADD IT TO OUR
        # SHORTEST PATHS DAG
        lost_dag_neighbors = all_neighbors_for_vertex_in_dag - dag_neighbors_for_vertex

        if (DEBUG):
            print("FOR VERTEX V, LOST DAG NEIGHBORS IS ", V,
                  lost_dag_neighbors)

        for K in lost_dag_neighbors:
            # check if V -> K is a forward edge from S to T in shortest paths dag and if it is, add it
            # because it creates shortest paths
            if (distT.get(K) < distT.get(V)):

                if (DEBUG):
                    print("OK so adding a neighbor relationship! (V, K)",
                          (V, K))
                if (DEBUG): print("distT.get(K)", distT.get(K))
                if (DEBUG): print("distT.get(V)", distT.get(V))
                if (DEBUG): print("distS.get(K)", distS.get(K))
                if (DEBUG): print("distS.get(V)", distS.get(V))

                shortest_paths_dag[V].add(K)

    if (DEBUG): print("VERTICES IN THE GRAPH ", set(graph.keys()))
    if (DEBUG): print("VERTICES IN SHORTEST PATH DAG", vertices_in_dag)
    if (DEBUG):
        print("VERTICES NOT IN SHORTEST PATH DAG",
              set(graph.keys()) - vertices_in_dag)

    if (DEBUG): print("shortest path subgraph is: ", shortest_paths_dag)
    if (DEBUG): print("graph_bfs_tree_with_root_s", graphBFS)
    if (DEBUG): print("reverse_graph_bfs_tree_with_root_t", reverseGraphBFS)

    return {
        "is_there_shortest_path": True,
        "shortest_paths_dag": shortest_paths_dag,
        "shortest_paths_dag_with_all_neighbors":
        shortest_paths_dag_with_all_neighbors,
        "a_shortest_path": a_shortest_path,
        "shortest_path_length": shortestLength,
        "graph_bfs_tree_with_root_s": graphBFS["parents"],
        "reverse_graph_bfs_tree_with_root_t": reverseGraphBFS["parents"]
    }
示例#7
0
def simple_disjoint_graph(s, t, y, z, graph, DEBUG=False):

    S_to_T_bfs = bfs_with_restriction_set(graph=graph,
                                          start=s,
                                          end=t,
                                          restriction_set=set([y, z]))

    if (S_to_T_bfs["result"] == False):
        if (DEBUG): print("FAILED ON FIRST BFS. from s -> t", (s, t))
        return {"result": False}

    S_to_T_bfs_path = get_path_to_root(S_to_T_bfs["parents"], t)[::-1]

    # Do second dfs
    if DEBUG: print("S_to_T_bfs_path_found: ", S_to_T_bfs_path)

    Y_to_Z_dfs = dfs_with_restriction_set(
        graph=graph,
        start=y,
        end=z,
        # avoidance_set=set(S_to_T_bfs_path),
        restriction_set=set(S_to_T_bfs_path))
    print("Y TO Z DFS RESULT IS:")
    print(Y_to_Z_dfs)

    if (Y_to_Z_dfs["result"] == True):
        if (DEBUG): print("FAILED ON FIRST DFS FROM Y TO Z")
        Y_to_Z_dfs_path = get_path_to_root(Y_to_Z_dfs["parents"], z)[::-1]

        return {
            "result": True,
            "S_to_T_path": S_to_T_bfs_path,
            "Y_to_Z_path": Y_to_Z_dfs_path
        }

    Y_to_Z_bfs = bfs_with_restriction_set(graph=graph,
                                          start=y,
                                          end=z,
                                          restriction_set=set([s, t]))

    if (Y_to_Z_bfs["result"] == False):
        if (DEBUG): print("FAILED ON SECOND BFS. from Y -> Z", (y, z))
        return {"result": False}

    Y_to_Z_bfs_path = get_path_to_root(Y_to_Z_bfs["parents"], z)[::-1]

    print("Y TO Z BFS", Y_to_Z_bfs_path)

    S_to_T_dfs_2 = dfs_with_restriction_set(
        graph=graph,
        start=s,
        end=t,
        # avoidance_set=set(Y_to_Z_bfs_path),
        restriction_set=set(Y_to_Z_bfs_path))

    if (S_to_T_dfs_2["result"] == True):
        S_to_T_dfs_path = get_path_to_root(S_to_T_dfs_2["parents"], t)[::-1]
        return {
            "result": True,
            "S_to_T_path": S_to_T_dfs_path,
            "Y_to_Z_path": Y_to_Z_bfs_path
        }

    return {"result": False}
示例#8
0
def max_flow_disjoint_paths(s, t, y, z, graph, DEBUG=True):
    # create a residual graph!
    # each edge has weight 1!
    import copy

    residual = {}

    # We will be adding reverse edges to the residual graph as we do the max flow algorithm!
    '''
    Try this to get simple paths if ford fulk dont make em:

    Split each node v in the graph into to nodes: vin and vout.
    For each node v, add an edge of capacity one from vin to vout.
    Replace each other edge (u, v) in the graph with an edge from uout to vin of capacity 1.
    '''

    for i, neighbors in graph.items():

        i_out = str(i) + "o"  # split vertex into 2. out and in version.
        i_in = str(i) + "i"
        residual[i_in] = {i_out: 1}
        residual[i_out] = {}

        for n in neighbors:
            n_in = str(n) + "i"
            if (residual.get(i_out) is None):
                residual[i_out] = {n_in: 1}
            else:
                residual[i_out][
                    n_in] = 1  # VALUE IS CURRENT CAPACITY IN RESIDUAL GRAPH

    s = str(s) + "i"
    y = str(y) + "i"
    t = str(t) + "o"
    z = str(z) + "o"

    # Connect A to all start nodes

    # Connect B to all end nodes
    # Max flow from A to B, get all edge independent paths from A to B
    # then get min cut edges! == max flow

    # then check if you can use min cut edges to
    # create independent paths from s to t, and y to z?
    residual["A"] = {}
    residual["B"] = {}
    residual["A"][s] = 1
    residual["A"][y] = 1
    residual[t]["B"] = 1
    residual[z]["B"] = 1

    original_graph = copy.deepcopy(residual)

    # remove in and out str parts from path!
    def normalize_path(path):
        # removes the in and out part from the path!
        p = []
        for i in path:
            if i[-1] == "i":
                p.append(i[:-1])
            else:
                continue

        return p

    if DEBUG: pprint.pprint(residual)
    '''Returns true if there is a path from source 's' to sink 't' in 
    residual graph. Also fills parent[] to store the path '''

    def BFS(s, t):
        from collections import deque

        # Mark all the vertices as not visited
        visited = set()
        parent = {}
        # Create a queue for BFS
        visited, queue = set([s]), deque([s])
        parent[s] = None

        # Standard BFS Loop
        while queue:

            #Dequeue a vertex from queue and print it
            u = queue.pop()

            # Get all adjacent vertices of the dequeued vertex u
            # If a adjacent has not been visited, then mark it
            # visited and enqueue it
            for neighbor in residual[u]:
                if neighbor not in visited and residual[u][neighbor] > 0:
                    queue.appendleft(neighbor)
                    visited.add(neighbor)
                    parent[neighbor] = u

        # If we reached sink in BFS starting from source, then return
        # true, else false
        return (True, parent) if t in visited else (False, parent)

    # go from s to t, then go from y to z
    # keep alternating, until doing both yield False?
    max_flow = 0

    # max flow variables for multiple source, sink
    finding_st_right_now = True
    source = None
    sink = None

    st_path = None
    yz_path = None

    while True:

        if (finding_st_right_now):
            # source = s
            # sink = t
            source = "A"
            sink = "B"
            finding_st_right_now = False
        else:
            # source = y
            # sink = z
            source = "A"
            sink = "B"
            finding_st_right_now = True

        print("source and sink are", (source, sink))
        (result, bfs_tree) = BFS(source, sink)

        if (result == False):
            if DEBUG: print("COULD NOT FIND PATH. BREAK")

            return {"result": False}
            break

        # Add path flow to overall flow
        max_flow += 1

        path = get_path_to_root(bfs_tree, sink)[::-1]

        if (source == s):
            st_path = path
        else:
            yz_path = path

        # update graph!

        # update residual capacities of the edges and reverse edges
        # along the path
        v = sink
        while (v != source):
            u = bfs_tree[v]
            residual[u][v] -= 1

            # Do not create a backward edge from an Input to an outputself
            # Input edges should not have backward edges because they can ONLY Lead out of one output.

            # if backward edge isnt there, add it!
            if DEBUG: print("v[-1]", v[-1])

            # only outs can connect to ins on the reverse side.
            reverse_residual_v = v
            reverse_residual_u = u
            if (v[-1] == "i"):
                # connect v-out to u-in
                # even if we are dealing with v-in
                reverse_residual_v = v[:-1] + "o"
                reverse_residual_u = u[:-1] + "i"

            if residual[reverse_residual_v].get(reverse_residual_u) is None:
                residual[reverse_residual_v][reverse_residual_u] = 0

            residual[reverse_residual_v][reverse_residual_u] += 1
            v = bfs_tree[v]

        if DEBUG: print("FLOW FOUND IS", path)
        if DEBUG: print("NORMALIZED FLOW", normalize_path(path))
        if DEBUG: print("RESIDUAL AFTER FINDING A PATH from", (source, sink))
        if DEBUG: pprint.pprint(residual)

        if (max_flow == 2):
            if DEBUG: print("FOUND BOTH PATHS! WE GUCCI")

            break

    return_output = {
        "result": True,
        # "S_to_T_path": normalize_path(st_path),
        # "Y_to_Z_path": normalize_path(yz_path) # get_path_to_root(Y_to_Z_dfs_2["parents"], z)[::-1]
    }

    # original graph has all edge relationships with postiive val.
    # if they now 0, its a min cut edge
    for i, neighbors in original_graph.items():
        for j in neighbors:
            if (residual[i][j] == 0):
                print("MINIUM CUT EDGE IS ", (i, j))

    if DEBUG: pprint.pprint(return_output)

    return return_output
示例#9
0
def disjoint_graph(s, t, y, z, graph, DEBUG=False):

    S_to_T_bfs = bfs_with_restriction_set(graph=graph,
                                          start=s,
                                          end=t,
                                          restriction_set=set([y, z]))

    if (S_to_T_bfs["result"] == False):
        if (DEBUG): print("FAILED ON FIRST BFS. from s -> t", (s, t))
        return {"result": False}

    S_to_T_bfs_path = get_path_to_root(S_to_T_bfs["parents"], t)[::-1]

    # Do second dfs
    if DEBUG: print("S_to_T_bfs_path_found: ", S_to_T_bfs_path)

    Y_to_Z_dfs = dfs_with_restriction_set(graph=graph,
                                          start=y,
                                          end=z,
                                          avoidance_set=set(S_to_T_bfs_path),
                                          restriction_set=set([s, t]))

    if (Y_to_Z_dfs["result"] == False):
        if (DEBUG): print("FAILED ON FIRST DFS FROM Y TO Z")
        return {
            "result": False,
        }

    Y_to_Z_dfs_path = get_path_to_root(Y_to_Z_dfs["parents"], z)[::-1]

    if DEBUG: print("Y to Z dfs path found: ", Y_to_Z_dfs_path)

    # Do third dfs
    S_to_T_dfs = dfs_with_restriction_set(graph=graph,
                                          start=s,
                                          end=t,
                                          avoidance_set=set(),
                                          restriction_set=set(Y_to_Z_dfs_path))

    if (S_to_T_dfs["result"] == True):
        if (DEBUG): print("IT WAS MERGED WITHIN 2 DFS'S")
        return {
            "result": True,
            "S_to_T_path": get_path_to_root(S_to_T_dfs["parents"], t)[::-1],
            "Y_to_Z_path": Y_to_Z_dfs_path
        }

    # The second dfs failed, so move on to the third dfs:
    if DEBUG:
        print(
            "SECOND DFS FAILED. WE HAVE TO REPEAT BFS AGAIN. second was S->T",
            (s, t))
    ############################################################################
    #### TRY THE OTHER WAY -> 1 BFS, 2 DFS

    Y_to_Z_bfs = bfs_with_restriction_set(graph=graph,
                                          start=y,
                                          end=z,
                                          restriction_set=set([s, t]))

    if (Y_to_Z_bfs["result"] == False):
        if (DEBUG): print("FAILED ON SECOND BFS. from Y -> Z", (y, z))
        return {"result": False}

    Y_to_Z_bfs_path = get_path_to_root(Y_to_Z_bfs["parents"], z)[::-1]

    if DEBUG: print("Y_to_Z_bfs_path_found: ", Y_to_Z_bfs_path)

    S_to_T_dfs_2 = dfs_with_restriction_set(graph=graph,
                                            start=s,
                                            end=t,
                                            avoidance_set=set(Y_to_Z_bfs_path),
                                            restriction_set=set([y, z]))

    if (S_to_T_dfs_2["result"] == False):
        if (DEBUG): print("FAILED ON FIRST DFS FROM Y TO Z")
        return {
            "result": False,
        }

    S_to_T_dfs_2_path = get_path_to_root(S_to_T_dfs_2["parents"], t)[::-1]

    if DEBUG: print("S_to_T_dfs_2_path: ", S_to_T_dfs_2_path)

    Y_to_Z_dfs_2 = dfs_with_restriction_set(
        graph=graph,
        start=y,
        end=z,
        avoidance_set=set(),
        restriction_set=set(S_to_T_dfs_2_path))

    if (Y_to_Z_dfs_2["result"] == True):
        if (DEBUG): print("IT WAS MERGED WITHIN 4 DFS'S")
        return {
            "result": True,
            "S_to_T_path": S_to_T_dfs_2_path,
            "Y_to_Z_path": get_path_to_root(Y_to_Z_dfs_2["parents"], z)[::-1]
        }

    if DEBUG: print("FOURTH DFS FAILED. COMPLETE FAILURE", (y, z))

    return {"result": False}