Exemple #1
0
def solve(arg_nodes, arg_links, templates, prev_overlays, sources, fixed,
          arg_obj):
    # write global variables
    global nodes, links, prev_instances, obj
    nodes = arg_nodes
    links = arg_links
    # copy previous instances (attributes like edges_in etc are not needed and not copied)
    prev_instances = {
        Instance(i.component, i.location, i.src_flows)
        for ol in prev_overlays.values() for i in ol.instances
    }
    obj = arg_obj

    # print input
    # print("Templates:", *templates, sep=" ")
    # print("Sources:", *sources, sep=" ")
    # print("Fixed instances:", *fixed, sep=" ")
    # print("Previous instances:", *prev_instances, sep=" ")

    # pre-computation of shortest paths
    start_init = time.time()
    shortest_paths = sp.all_pairs_shortest_paths(nodes, links)
    init_time = time.time() - start_init
    # print("Time for pre-computation of shortest paths: {}s\n".format(init_time))
    logger.info(
        "Time for pre-computation of shortest paths: {}s\n".format(init_time))

    start_heuristic = time.time()
    # get total source data rate for each source component (for sorting the templates later)
    src_drs = defaultdict(int)  # default = 0
    for src in sources:
        src_drs[src.component] += src.total_flow_dr()

    # sort templates with decreasing weight: heaviest/most difficult templates get embedded first
    templates.sort(key=lambda t: t.weight(src_drs[t.source()]), reverse=True)
    # print("Templates sorted to start with heaviest:", *templates, sep=" ")

    # initial solution
    # print("\n----- Initial solution -----")
    logger.info("----- Initial solution -----")
    overlays = heuristic.solve(arg_nodes, arg_links, templates, prev_overlays,
                               sources, fixed, shortest_paths)
    obj_value = objective_value(overlays)
    # print("Objective value of initial solution: {}".format(obj_value))
    # print("Runtime for initial solution: {}".format(time.time() - start_heuristic))
    logger.info("Objective value of initial solution: {}".format(obj_value))
    logger.info("Runtime for initial solution: {}\n".format(time.time() -
                                                            start_heuristic))

    # iterative improvement
    if len(nodes.ids) > 1:  # doesn't work for networks with just 1 node
        # print("\n----- Iterative improvement -----")
        logger.info("----- Iterative improvement -----")
        overlays = improvement.improve(arg_nodes, arg_links, templates,
                                       overlays, sources, fixed,
                                       shortest_paths)
        obj_value = objective_value(overlays)
        runtime = time.time() - start_heuristic
        # print("Objective value after improvement: {}".format(obj_value))
        # print("Heuristic runtime: {}s".format(runtime))
        logger.info("Objective value after improvement: {}".format(obj_value))
        logger.info("Heuristic runtime: {}s".format(runtime))
    else:
        runtime = time.time() - start_heuristic
        # print("Skip iterative improvement for network with just 1 node")
        logger.info("Skip iterative improvement for network with just 1 node")

    # calculate changed instances for writing result
    curr_instances = {i for ol in overlays.values() for i in ol.instances}
    changed = prev_instances ^ curr_instances  # instances that were added or removed

    return init_time, runtime, obj_value, changed, overlays
Exemple #2
0
def improve(arg_nodes, arg_links, templates, arg_overlays, sources, fixed,
            arg_shortest_paths):
    # write global variables
    global nodes, links, shortest_paths, overlays
    nodes = arg_nodes
    links = arg_links
    shortest_paths = arg_shortest_paths
    overlays = arg_overlays

    # three different solutions (overlays): incumbent, modified (by current iteration), best
    best_overlays = copy.deepcopy(overlays)
    incumbent_overlays = copy.deepcopy(overlays)

    # outer loop: iteratively improve the overlays
    total_outer_iterations = 0
    unsuccessful_iterations = 0  # unsuccessful = best solution not improved; iteration = outer loop (modify all)
    max_unsuccessful_iterations = 20
    while unsuccessful_iterations < max_unsuccessful_iterations:
        total_outer_iterations += 1
        unsuccessful_iterations += 1

        # reset to incumbent solution before next modifications (only once per outer loop iteration)
        modified_overlays = copy.deepcopy(incumbent_overlays)

        # inner loop: modify templates' overlays in predefined order
        # pick instance of each overlay, add it to tabu-list, reset overlay, and solve anew
        # FUTURE WORK: smarter than random? e.g., such instances that could be used in both direction but aren't?
        for t in templates:
            # an overlay may be deleted if it has no source -> skip the template and its overlay
            if t not in modified_overlays.keys():
                continue
            ol = modified_overlays[t]
            # ol.print()

            # set random instance to tabu and remove it and following instances
            # FUTURE WORK: keep instances in tabu-list for multiple iterations?
            tabu = set(
            )  # set of forbidden instances tuples: (component, location)
            # ignore source or fixed instances, which have to be at a specific location
            non_fixed_instances = [
                i for i in ol.instances
                if not i.component.source and not i.fixed
            ]
            if len(non_fixed_instances) == 0:
                # print("Skip modification of {}'s overlay because all instances are fixed".format(t))
                logger.info(
                    "Skip modification of {}'s overlay because all instances are fixed"
                    .format(t))
                continue
            rand_instance = random.choice(non_fixed_instances)
            tabu.add((rand_instance.component, rand_instance.location))

            # print("\n--Iteration {}: Modifying overlay of {}--".format(total_outer_iterations, ol.template))
            # print("Set random instance {} of {}'s overlay to tabu and rebuild overlay".format(rand_instance,
            #        ol.template))
            logger.info("--Iteration {}: Modifying overlay of {}--".format(
                total_outer_iterations, ol.template))
            logger.info(
                "Set random instance {} of {}'s overlay to tabu and rebuild overlay"
                .format(rand_instance, ol.template))

            reset_overlay(ol.template, rand_instance, modified_overlays)
            modified_overlays = heuristic.solve(nodes, links, templates,
                                                modified_overlays, sources,
                                                fixed, shortest_paths, tabu)

            # update solution
            new_obj_value = control.objective_value(modified_overlays)
            # print("Objective value of modified overlays: {}".format(new_obj_value))
            logger.info("Objective value of modified overlays: {}".format(
                new_obj_value))
            incumbent_obj_value = control.objective_value(incumbent_overlays)
            if new_obj_value < incumbent_obj_value:
                # print("\tImproved objective value -> new incumbent solution")
                logger.info(
                    "\tImproved objective value -> new incumbent solution")
                incumbent_overlays = copy.deepcopy(modified_overlays)
                if new_obj_value < control.objective_value(best_overlays):
                    # print("\tNew best solution")
                    logger.info("\tNew best solution")
                    best_overlays = copy.deepcopy(modified_overlays)
                    unsuccessful_iterations = 0
            # even update incumbent solution if it is slightly worse (50% chance)
            elif new_obj_value <= 1.1 * incumbent_obj_value:
                if random.random() < 0.5:
                    # print("\tOnly slightly worse objective value; new incumbent solution")
                    logger.info(
                        "\tOnly slightly worse objective value; new incumbent solution"
                    )
                    incumbent_overlays = copy.deepcopy(modified_overlays)
                else:
                    # print("\tOnly slightly worse objective value; solution discarded")
                    logger.info(
                        "\tOnly slightly worse objective value; solution discarded"
                    )
            else:
                # print("\tWorse objective value -> solution discarded after last inner loop")
                logger.info(
                    "\tWorse objective value -> solution discarded after this iteration"
                )
                # keep using modified_overlays during the remainder of the inner loop

    # print("\n---Heuristic finished---")
    # print("Total outer loop iterations: {}".format(total_outer_iterations))
    logger.info("---Heuristic finished---")
    logger.info(
        "Total outer loop iterations: {}".format(total_outer_iterations))
    print("Best overlays:")
    for ol in best_overlays.values():
        ol.print()

    return best_overlays