def create_topology_and_pathlist(a,g,arrangement):
    G = create_topology(a,g,arrangement)

    all_min_paths_DF = all_pair_all_min_paths_DF(G, edge_weight = "w", a = a, g = g)
    all_djkstra_paths = all_pair_all_shortest_paths_djkstra(G, edge_weight = "w")
    
    return G, all_min_paths_DF, all_djkstra_paths
def compare_paths_by_djkstra_and_floyd_warshall():
    '''
    As the name suggests. Just a test function.
    '''
    
    G = intro_code_to_test_something.create_dragonfly(a=8, g = 17, arrangement = "absolute_improved")
    intro_code_to_test_something.draw_graph(G)
    
    paths_fw = all_pair_all_shortest_paths_floyd_warshall(G, verbose = False)
    
    paths_dj = djkstra.all_pair_all_shortest_paths_djkstra(G, print_paths = False)
    
    #paths_dj[5][5].append((1,2))
    
    
    path_validness = djkstra.check_path_validness(paths_fw, paths_dj)
    
    print("path validity: ", path_validness)
    
    #TODO: 
    # issue: FW seems to be an order of magnitude slow that djkstra. Not sure why.
    # for the time being, djkstra seems to be the way forward.           
    
    
    pass
Example #3
0
def intro_code():
    N = 6
    graph = [[] for x in range(N)]

    edges = [(0, 1, 1), (0, 2, 1), (1, 2, 1), (3, 4, 1), (3, 5, 1), (4, 5, 1),
             (2, 3, 3), (1, 5, 3)]  #(src, dest, weight)

    for (src, dst, weight) in edges:
        graph[src].append((dst, weight))
        graph[dst].append((src, weight))

    print("the graph:")
    for idx in range(len(graph)):
        print(idx, graph[idx])

    G = nx.Graph()
    for (src, dst, weight) in edges:
        G.add_edge(src, dst, w=weight)
    print("the graph from networkx:")
    print(G.edges(data=True))

    draw_graph(G)

    # check the paths in the graph
    #paths = djkstra(0, N, graph)

    #check djkstra

    print("\ncalling all_pair_djkstra: ")
    start_time = time.time()
    paths = all_pair_all_shortest_paths_djkstra(G, edge_weight="w")
    end_time = time.time()
    print("djkstra returned.")
    print("execution time: ", end_time - start_time)

    #now check floyd-warshall
    start_time = time.time()
    print("\ncalling floyd-warshall")
    paths2 = ut.floyd_warshall_all_pairs_sp(G, weight='w')
    end_time = time.time()
    print("FW returned.")
    print("execution time: ", end_time - start_time)

    #    print("\nfloyd warshall paths:")
    #    for src in range(N):
    #        print("\nsrc:",src)
    #        for dst in range(N):
    #            print(src,dst, l2[src][dst])
    #

    pass
Example #4
0
def create_MIN_paths_list(G,
                          traffic,
                          all_pair_min_paths=None,
                          *,
                          verbose=False):
    '''
    A function that creates a MIN path list for only the s-d pairs present in the traffic pattern.
    
    This is needed to make MIN-path method compatible to the corresponding VLB-path one.
    
    Previously we were using all_pair_all_shortest_paths_djkstra() for the passing the paths,
    but that creates a 3D list. On the other hand, VLB-path functions creates a dict with (s,d)
    pair tuple as the key. This causes access incoherence in the functions which intend to 
    work with the path list.
     
    
    Returns: a dict with keys (s,d) and value: list of path tuples. 
    '''

    if all_pair_min_paths == None:
        all_pair_min_paths = all_pair_all_shortest_paths_djkstra(
            G, edge_weight="w", print_paths=False)

    all_MINpaths = {}

    for src, dst in traffic:
        minpaths = [tuple(path) for path in all_pair_min_paths[src][dst]]
        all_MINpaths[(src, dst)] = minpaths

    if verbose:
        print("\nMin paths for each pair:")
        for sdpair, paths in all_MINpaths.items():
            print(sdpair, " -> ", paths)

    return all_MINpaths

    pass
 print("Hello world!")
 
 
 a = 4
 g = 5
 p = a//2
 arrangement = "absolute_improved"
 
 #G = create_topology(a,g,arrangement)
 G, local_links, global_links = topologies.build_drgonfly(a=a, p=a // 2, h=a // 2, gl_arrangmnt=arrangement, g=g, seed = 0)
 
 graph_adj_list = generate_graph_adjacency_list(G)
 
 group_pair_vs_global_links = get_list_of_links_between_each_group_pair(G, edge_weight = "w", a = a, g = g )
 
 paths_dj = all_pair_all_shortest_paths_djkstra(G, print_paths = False)
 
 group_pair_vs_nodes = generate_list_of_nodes_connected_to_group_pairs(graph_adj_list, a)
 
 two_hop_neighbor_list = generate_list_of_2hop_neighbors(G, group_pair_vs_global_links, edge_weight = "w", a = a, g = g)
 
 from traffic import create_random_permutation_pattern
 SDpairs = create_random_permutation_pattern(a = a, g = g, seed = 10, verbose = False)
 
 print("\nTraffic pattern:")
 
 sd_pair_vs_3hop_inodes = inodes_for_3hop_paths_for_all_SD_pairs(SDpairs, G, graph_adj_list, two_hop_neighbor_list, edge_weight = "w", a = a, g = g, p = p)
 
 for pair in SDpairs:
     src = pair[0]
     dst = pair[1]
def test_jains(a, g, pattern = None, routing = "MIN", file_pointer = None,  *, draw = False):
    '''
    So far, routing can be only MIN or VLB, or Combined
    '''
    
    print("\n\ninside test_jains ...")
    start_time = time.time()
    
    
    #create a Dragonfly topology
    #a = 8
    #g = 29
    h = a//2
    p = a//2
    arrangement = "absolute_improved"
    
    print("a, g, arrangement : ", a, g, arrangement)
    
    G = create_topology(a,g,arrangement)
    if draw == True:
        draw_graph_circular(G, a = a)
        
    if pattern == "randperm":
        seeds = [0, 2]  
        #TODO: we need to parameterize seeds later
    elif pattern == "shift":
        seeds = [0]   # seed is not needed for shift
    else:
        print("Error! Unsupported traffic pattern!")
        sys.exit(1)

        
    for seed in seeds:
        #create a traffic pattern
        if pattern == "randperm":
            traffic = create_random_permutation_pattern(a = a, g = g, seed = seed, verbose = False)
        elif pattern == "shift":
            traffic = create_shift_traffic_pattern(a = a, g = g,  verbose = False)
            
        tot_sd_pairs = len(traffic)
        traffic_and_freqs = check_pattern_for_duplicates(traffic, verbose = False)
        tot_unique_pairs = len(traffic_and_freqs)
        
        #create path list
            #test with both min and vlb, one by one
        all_djkstra_paths = all_pair_all_shortest_paths_djkstra(G, edge_weight = "w")
        
        modes = [0, 1, 3] #mode 1 and 2 are practically equivalent, so skipping 2
        
        for mode in modes:
        
            if routing == "MIN":
                paths = create_MIN_paths_list(G, traffic, all_pair_min_paths = all_djkstra_paths, verbose = False)
            elif routing == "VLB":
                paths = create_VLB_paths_list(G, a, traffic, all_pair_min_paths = all_djkstra_paths, verbose = False)
            elif routing == "Combined":
                paths = merge_MIN_and_VLB_paths(G, a, traffic, all_pair_min_paths = all_djkstra_paths, mode = mode, verbose = False)
            #call JM
            print("\ncalling JM for routing {}, seed {} and mode {} ".format(routing, seed, mode))
            #total_bw, res = jains_model(G = G, traffic = traffic,   path_list = paths, verbose = False)
            total_bw, result_bw = jains_model_with_unique_pairs(G = G, traffic_and_freqs = traffic_and_freqs,  path_list = paths, verbose = False)
            
            print("out of JM ...")
            print("\ntotal_bw: " , total_bw)
            print("result: " , result_bw)
            
            #do file writing here.
            print("{},{},{},{},{},{},{},{},{},".format(a,g,h,p,arrangement,pattern,seed,tot_sd_pairs,tot_unique_pairs))
            if file_pointer:
                file_pointer.write("{},{},{},{},{},{},{},{},{},".format(a,g,h,p,arrangement,pattern,seed,tot_sd_pairs,tot_unique_pairs))
            print("{},{},{:.4f},{:.4f}".format(routing, mode, total_bw, result_bw))
            if file_pointer:
                file_pointer.write("{},{},{:.4f},{:.4f}".format(routing, mode, total_bw, result_bw))
                file_pointer.write("\n")

            #win
            end_time = time.time()
            print("total time spent:", end_time - start_time)
        
    pass
def driverForGlobalLinkUsageCount():
    #create a topology
    a = 8
    gs = [9]
    traffics = ["shift"]

    #traffic = "rperm"
    traffic_params = {"shift": "shift_by"}
    traffic_param_values = {"shift": 1}

    for g in gs:
        #g = 33
        p = 1  #router_based traffic
        h = a // 2
        arrangement = "absolute_improved"
        #not changing it anytime soon
        G = create_topology(a, g, arrangement)
        #draw_graph_circular(G, a = a)

        #make the adjacency list
        graph_adj_list = generate_graph_adjacency_list(G)

        #make twoHopNeighborList
        group_pair_vs_global_links = get_list_of_links_between_each_group_pair(
            G, edge_weight="w", a=a, g=g)
        twoHopNeighbors = generate_list_of_2hop_neighbors(
            G, group_pair_vs_global_links, edge_weight="w", a=a, g=g)

        #create path list
        all_djkstra_paths = all_pair_all_shortest_paths_djkstra(
            G, edge_weight="w")

        #get a traffic
        #traffic = "shift"

        for traffic in traffics:

            if traffic == "rperm":
                SDpairs = create_random_permutation_pattern(
                    a=a,
                    g=g,
                    seed=traffic_param_values[traffic],
                    verbose=False)
            elif traffic == "shift":
                SDpairs = create_shift_traffic_pattern(
                    a=a,
                    g=g,
                    shift_by=traffic_param_values[traffic],
                    router_level=True,
                    verbose=True)
            else:
                SDpairs = None

            #select a mode. Options: "src_only" or "src_and_dst"
            modes = [
                "3hop_paths_only", "4hop_paths_only", "5hop_src_only",
                "5hop_src_and_dst"
            ]

            #link_usage_data = {}

            for mode in modes:
                print("traffic:", traffic)
                print("mode:", mode)
                #link_usage_data[mode] = calculateLinkUsage(graph_adj_list, SDpairs, mode, all_djkstra_paths, twoHopNeighbors, a = a, g = g, p = p)
                countGlobalLinkUsage(G,
                                     SDpairs,
                                     mode,
                                     all_djkstra_paths,
                                     a=a,
                                     g=g,
                                     p=p,
                                     h=h)

            #all_usage_data[(a,g,traffic,traffic_params[traffic], traffic_param_values[traffic])] = link_usage_data

    pass
def driverForLinkUsageCalculation():
    #create a topology
    a = 8
    gs = [9, 17, 25, 33]
    traffics = ["rperm", "shift"]

    #traffic = "rperm"
    traffic_params = {"shift": "shift_by", "rperm": "seed"}
    traffic_param_values = {"shift": 1, "rperm": 45}

    all_usage_data = {}

    for g in gs:
        #g = 33
        p = a // 2
        h = a // 2
        arrangement = "absolute_improved"
        #not changing it anytime soon
        G = create_topology(a, g, arrangement)
        #draw_graph_circular(G, a = a)

        #make the adjacency list
        graph_adj_list = generate_graph_adjacency_list(G)

        #make twoHopNeighborList
        group_pair_vs_global_links = get_list_of_links_between_each_group_pair(
            G, edge_weight="w", a=a, g=g)
        twoHopNeighbors = generate_list_of_2hop_neighbors(
            G, group_pair_vs_global_links, edge_weight="w", a=a, g=g)

        #create path list
        all_djkstra_paths = all_pair_all_shortest_paths_djkstra(
            G, edge_weight="w")

        #get a traffic
        #traffic = "shift"

        for traffic in traffics:

            if traffic == "rperm":
                SDpairs = create_random_permutation_pattern(
                    a=a,
                    g=g,
                    seed=traffic_param_values[traffic],
                    verbose=False)
            elif traffic == "shift":
                SDpairs = create_shift_traffic_pattern(
                    a=a,
                    g=g,
                    shift_by=traffic_param_values[traffic],
                    router_level=False,
                    verbose=False)
            else:
                SDpairs = None

            #select a mode. Options: "src_only" or "src_and_dst"
            modes = ["src_only", "src_and_dst"]

            link_usage_data = {}

            for mode in modes:
                print("traffic:", traffic)
                print("mode:", mode)
                link_usage_data[mode] = calculateLinkUsage(graph_adj_list,
                                                           SDpairs,
                                                           mode,
                                                           all_djkstra_paths,
                                                           twoHopNeighbors,
                                                           a=a,
                                                           g=g,
                                                           p=p)

            all_usage_data[(a, g, traffic, traffic_params[traffic],
                            traffic_param_values[traffic])] = link_usage_data

    #title = "a : {}, g : {}, traffic : {}, {} : {}".format(a,g,traffic, traffic_params[traffic], traffic_param_values[traffic])

    #process_link_usage_data(link_usage_data, a=a, h=h, g=g, p=p, title = "test")
    process_all_link_usage_data(all_usage_data)
Example #9
0
def reachability_test(Graph, a, h, g):
    '''
    We want to check, for each node, how many nodes can be reached in 1, 2 or 3 steps.
    
    Get the stat for each node individually. Then compress them to get a summary result.
    
    '''
    '''
    Logic:
        
    #call djkstra, get the table
    
    #for each pair of nodes, check the min path len. update counts for src and dst nodes accordingly.
    
    #for each node, we need to save three counts: no of nodes reachable by hops 1,2,3
    
    #when done, convert each (count_1, count_2, count_3) triplet to tuples and get their count. That is the summary we are looking for.
    '''

    N = len(Graph.nodes)

    counts = [[0, 0, 0] for x in range(N)]

    all_djkstra_paths = all_pair_all_shortest_paths_djkstra(Graph)

    for src in range(N):
        #print(src, end=" : ")
        for dst in range(src + 1, N):
            #print(dst, end= " ")

            #don't want to consider same-group node pairs
            if different_group_nodes((src, dst), a):
                paths = all_djkstra_paths[src][dst]
                length = len(paths[0]) - 1  # hop count = node count  - 1
                counts[src][length - 1] += 1
                counts[dst][length - 1] += 1

    #test print
    #    for ii in range(N):
    #        print("{} : {} {} {} ".format(ii, counts[ii][0], counts[ii][1], counts[ii][2]))
    #

    #get frequency
    frequency_of_reachability_count = {}

    for ii in range(N):
        tup = tuple(counts[ii])
        if tup in frequency_of_reachability_count:
            frequency_of_reachability_count[tup] += 1
        else:
            frequency_of_reachability_count[tup] = 1

    values_and_freq = []

    for tup, count in frequency_of_reachability_count.items():
        values_and_freq.append([a, h, g, tup[0], tup[1], tup[2], count])

    records = pd.DataFrame(values_and_freq)
    records.columns = [
        "a", "h", "g", "dist_1", "dist_2", "dist_3", "frequency"
    ]
    records["dist_1_pcnt"] = records["dist_1"] / (
        records["dist_1"] + records["dist_2"] + records["dist_3"]) * 100
    records["dist_2_pcnt"] = records["dist_2"] / (
        records["dist_1"] + records["dist_2"] + records["dist_3"]) * 100
    records["dist_3_pcnt"] = records["dist_3"] / (
        records["dist_1"] + records["dist_2"] + records["dist_3"]) * 100

    #print(records)

    #test print
    #    print("frequency:")
    #    for row in values_and_freq:
    #        print(row)
    #

    return records

    pass
Example #10
0
def run_test_for_a_config(a, h, g, arrg, *, seed=0):

    print(
        "\nStarting test for: a {}, h {}, g {}, arrangment {}, seed {}".format(
            a, h, g, arrg, seed))
    config = (a, h, g, arrg)

    #folder_name = "Path_Stats"
    folder_name = "TestFolder_supposedly_unimportant_data"

    os.makedirs(folder_name, exist_ok=True)

    #    file_name1 = folder_name + "/" + "a_{}_h{}_g{}_global_paths.csv".format(a,h,g)
    #    file_name2 = folder_name + "/" + "a_{}_h{}_g{}_global_paths_detailed.csv".format(a,h,g)
    #    file_name3 = folder_name + "/" + "a_{}_h{}_g{}_all_paths_overall.csv".format(a,h,g)
    file_name1 = folder_name + "/" + arrg + "_global_paths.csv"
    file_name2 = folder_name + "/" + arrg + "_global_paths_detailed.csv"
    file_name3 = folder_name + "/" + arrg + "_all_paths_overall.csv"

    if arrg == "practice":
        arrg = "absolute_improved"

    start_time = time.time()

    G, local_links, global_links = tp.build_drgonfly(a=a,
                                                     p=a // 2,
                                                     h=a // 2,
                                                     gl_arrangmnt=arrg,
                                                     g=g,
                                                     seed=seed)
    end_time = time.time()
    #print("topology creation time:", end_time - start_time)

    if ut.validate_DF(G, global_links, local_links, a, g):
        #print("Valid topology")
        validity = "Valid"
    else:
        #print("Invalid topology")
        validity = "Invalid"

    #print_graph(G)
    #draw_graph_circular(G, a = a)

    N = len(G)

    #print("\ncalling all_pair_djkstra: ")
    start_time = time.time()
    paths_djk = all_pair_all_shortest_paths_djkstra(G, edge_weight="w")
    end_time = time.time()
    #print("djkstra returned.")
    #print("execution time: ", end_time - start_time)

    get_path_len_stat(paths_djk,
                      G,
                      config,
                      seed=seed,
                      validity=validity,
                      file_name1=file_name1,
                      file_name2=file_name2,
                      file_name3=file_name3)

    #    print("\ncalling all_pair_networkx: ")
    #    start_time = time.time()
    #    paths_nx = all_pair_all_shortest_paths_networkx(G, edge_weight = "w")
    #    end_time = time.time()
    #    print("netowkrx returned.")
    #    print("execution time: ", end_time - start_time)
    #    get_path_len_stat(paths_nx, G, config, seed = seed, validity = validity, file_name1 = file_name1, file_name2 = file_name2, file_name3 = file_name3)
    pass
Example #11
0
def create_VLB_paths_list(G,
                          a,
                          trafficPattern,
                          all_pair_min_paths=None,
                          *,
                          mode=0,
                          verbose=True):
    '''
    G: networkx Graph object
    
    a = DF parameter a, reuqired to find the group id of a node
        
    Traffic pattern: A list of (S,D) tuple.
                    S and D are routers, *NOT* PEs
                    
    mode: How the VLB paths will be selected.
        This decides the max path len in first and second phase of VLB routes.
        In Dragonfly, max path len for min routing can be 3.
        So each phase can have 3 hops, or less.
        
        Mode 0: <= 3 + <= 3  => unrestricted VLB routing, default case
        Mode 1: < 3  + <= 3  => First phase restricted
        Mode 2: <= 3  + < 3  => Second phase restricted
        Mode 3: < 3  + < 3  =>  Both phase restricted
                    
    returns: A dict of (s,d) pair keys and pathlist in values.
            pathlist is list of tuples. Each tuple contains the node ids in the path.
    '''
    '''
    #for each S-D pair, we need all the VLB paths.
    
    #for an SD, each other node can be a potential I node.
        #So for each I node, we need all S-I-D paths.
        #If there are multiple paths in the S-I and I-D portion, then it is even more complex.
        #In that case, if there are m1 paths in the S-I part and m2 paths in the I-D part, 
            #then there are a total of m1*m2 number of total paths for this S-I-D quad only.
    
    #how big is the total number of paths?
    #In a DF, total number of PE is a*g*p. Say, N.
    #Then in a randperm, there are N number of SD pairs.
    #Each of the a*g-2 routers  can act as an I.
    #So total SID combo: N*(a*g-2)
    #For single paths only, we are talking about N*(a*g-2) paths.
        #For multipaths, it will be bigger.
    
    #For a = 8, h = 4, p =4. If g = 33, then N = 33 * 8 * 4 = 264 * 4 = 1056
    #So total SID combo: 1056 * (8*33-2) = 278 784 = 0.28 M
    
    #if g = 15, N = 15 * 8 * 4 = 120 * 4 = 480
    # so total SID combo: 480 * (8*15-2) = 57600 = 0.057 M
    
    #For a = 16, h = 8, p = 8. If g = 129, then N = 129 * 16 * 8 = 2064 * 8 = 16512
    #so total SID combo: 16512 * (16*129-2) = 34 080 768 = 34M
    
    #if g = 40, then N = 40 * 16 * 8 = 640 * 8 = 5120
    #so total SID combo: 5120 * (16*40) = 3 276 800 = 3M
    '''

    #ok, complexity aside. here are the steps.

    #First, call djkstra to get the shortest paths among all the pairs.

    #Then get the traffic pattern.
    #For each SD router combo, get every other router as an intermediate router I.
    #Get all the paths for (S,I) and (I,D) from the djkstra table.
    #combine the paths. So essentially m1 * m2 number of paths for each SID combo.
    #So for each SD, get a dict of (S,D) vs list of all paths from all (SID) triplet. Basically, all VLB paths.

    #pass this (S,D) vs [All VLB paths] to the Jain's method.

    if all_pair_min_paths == None:
        all_djkstra_paths = all_pair_all_shortest_paths_djkstra(
            G, edge_weight="w")
    else:
        all_djkstra_paths = all_pair_min_paths

    N = len(G)

    all_VLBpaths = {}

    if mode == 0:
        max_SI_pathlen = 3
        max_ID_pathlen = 3
    elif mode == 1:
        max_SI_pathlen = 2
        max_ID_pathlen = 3
    elif mode == 2:
        max_SI_pathlen = 3
        max_ID_pathlen = 2
    elif mode == 3:
        max_SI_pathlen = 2
        max_ID_pathlen = 2
    else:
        print("invalid mode: ", mode)
        sys.exit(1)

    #for each SD pair
    for (src, dst) in trafficPattern:
        if src == dst:
            continue

        SDpair_all_VLBpaths = set()

        srcGroup = src // a
        dstGroup = dst // a
        #print("groups:", srcGroup, dstGroup)

        #if src and dst are in the same group
        if srcGroup == dstGroup:
            #select imdt from only within that group
            imdt_lst = [
                x for x in range(srcGroup * a, (srcGroup + 1) * a)
                if x != src and x != dst
            ]

        else:
            #we want to skip the routers that are in the same group as the src and the dst
            imdt_lst = [
                x for x in range(N)
                if x // a != srcGroup and x // a != dstGroup
            ]
        #print("imdt_lst:", imdt_lst)

        #for each possible imdt router
        for imdt in imdt_lst:
            #            if src == imdt or dst == imdt:
            #                continue
            #not needed anymore as omitting srcGroup and dstGroup is taking care of this case as well.

            #construct all VLB path
            SIpaths = [
                path for path in all_djkstra_paths[src][imdt]
                if len(path) <= (max_SI_pathlen + 1)
            ]
            IDpaths = [
                path for path in all_djkstra_paths[imdt][dst]
                if len(path) <= (max_ID_pathlen + 1)
            ]
            #paths contain src and dst nodes, so hop number is one less than the number of nodes in the path

            #print(src, imdt, " : ", SIpaths)
            #print(imdt, dst, " : ", IDpaths)

            #VLBpaths = [ tuple( list(path1) + list(path2)[1:]) for path1 in SIpaths for path2 in IDpaths ]
            #print(VLBpaths)

            for path1 in SIpaths:
                for path2 in IDpaths:
                    SDpair_all_VLBpaths.add(
                        tuple(list(path1) + list(path2)[1:]))

            #        print("\n\n")
            #        print(src,dst,"all vlb paths:")
            #        print(SDpair_all_VLBpaths)
            #

        all_VLBpaths[(src, dst)] = list(SDpair_all_VLBpaths)

    if verbose:
        print("\nVLB paths for each pair:")
        for sdpair, paths in all_VLBpaths.items():
            print(sdpair, " -> ", paths)

    return all_VLBpaths

    pass
Example #12
0
def merge_MIN_and_VLB_paths(G,
                            a,
                            trafficPattern,
                            all_pair_min_paths=None,
                            *,
                            mode=0,
                            verbose=True):
    '''
    As the name suggests, this function takes a topology and traffic pattern, then
    generates all the min and VLB paths, and merges them together.
    Can be used to apporximate something similar to UGAL routing.
    
    As usual:
        G: networkx Graph object
    
        a = DF parameter a, reuqired to find the group id of a node
            
        Traffic pattern: A list of (S,D) tuple.
                        S and D are routers, *NOT* PEs
                        
        all_pair_min_paths: the caller function may call djkstra itself and send the 
                        paths as a parameter which will be used to find min and 
                        vlb paths. If None, then the djkstra-paths will be generated
                        inside the min and vlb functions.
                        
                        If sent, it's a 3D list of format [src][dst][lst of all path tuples]
                        
        mode: How the VLB paths will be selected.
            This decides the max path len in frist and second phase of VLB routes.
            In Dragonfly, max path len for min routing can be 3.
            So each phase can have 3 hops, or less.
            
            Mode 0: <= 3 + <= 3  => unrestricted VLB routing, default case
            Mode 1: < 3  + <= 3  => First phase restricted
            Mode 2: <= 3  + < 3  => Second phase restricted
            Mode 3: < 3  + < 3  =>  Both phase restricted
    
    
    returns: A dict of (s,d) pair keys and pathlist in values.
            pathlist is list of tuples. Each tuple contains the node ids in the path.
    
    '''

    #will save time for one run
    if all_pair_min_paths == None:
        all_djkstra_paths = all_pair_all_shortest_paths_djkstra(
            G, edge_weight="w")
    else:
        all_djkstra_paths = all_pair_min_paths

    all_min_paths = create_MIN_paths_list(G,
                                          trafficPattern,
                                          all_pair_min_paths=all_djkstra_paths,
                                          verbose=False)

    all_vlb_paths = create_VLB_paths_list(G,
                                          a,
                                          trafficPattern,
                                          all_pair_min_paths=all_djkstra_paths,
                                          mode=mode,
                                          verbose=False)

    if verbose:
        print("\n generated the paths.")

        for (src, dst) in trafficPattern:
            print("({}, {})".format(src, dst))
            print("min: ", all_min_paths[(src, dst)])
            print("vlb: ", all_vlb_paths[(src, dst)])

    #paths returned, now merge
    all_combined_paths = {}

    for (src, dst) in trafficPattern:
        all_combined_paths[(src, dst)] = [
            *all_min_paths[(src, dst)], *all_vlb_paths[(src, dst)]
        ]
        #shallow copy. But in this case it's fine.

    if verbose:
        print("\n merged paths.")

        for (src, dst) in trafficPattern:
            print("({}, {})".format(src, dst))
            print(all_combined_paths[(src, dst)])

    return all_combined_paths

    pass