def iteration_connections(solution):
    """ Iteration of the hillclimber that swithces out connections within routes.

    A random route is chosen, then with a certain probability either the first
    or last connection is removed. With a certain probability a new connection
    is placed at the location of the first or last connection. This happens
    independently. If the score improves, the change is accepted.

    Args:
        solution: An instance of the solution class.
        old_score: The score of the solution

    Returns:
        The index of the new route, and the new route itself.
    """
    # Choose random route.
    route_index = rd.randint(0, solution.max_trains - 1)
    old_route = solution.route_list[route_index]

    new_route = rt.Route(list(old_route.connection_list))

    # Determine which end is cut of, if any.
    new_route = cut_route_ends(new_route)

    new_route = add_new_endpoint(new_route, solution)

    # Return proposed changes.
    return route_index, new_route
Exemple #2
0
def create_random_route(solution):
    """ Create a random route.

    Args:
        solution: An instance of the solution class.

    Returns:
        The route that we created.
    """
    connection_list = []
    route = rt.Route(connection_list)

    # Choose begin station.
    begin_station = rd.choice(solution.station_dict_key_list)

    #  Create a new route.
    while True:
        route_time = route.time()

        weight = 0.1 * route_time / solution.max_minutes
        if 0.05 + weight > rd.random():
            break

        end_station = rd.choice(solution.station_dict[begin_station].neighbors)

        if route_time + end_station[1] < solution.max_minutes:
            route.append_route(begin_station, end_station[0], end_station[1])
            begin_station = end_station[0]
        else:
            break

    return route
Exemple #3
0
def greedy(solution):
    """ Greedy algorithm that hopes to fin the perfect solutioinself.

    Args:
        solution: An instance of the solution class, possibly containing routes.

    Returns;
        The solution: a combination of routes.
    """

    connection_archive = set()

    for i in range(solution.max_trains):
        connection_list = []
        route = rt.Route(connection_list)

        # Find best begin station.
        begin_station, end_station, travel_time, best_end_station_index, found_another_station = find_best_begin_station(
            solution, connection_archive)

        # Quit if no solution was found.
        if not found_another_station:
            # Ensure our solution will contain maximum ammount of trains.
            for _ in range(solution.max_trains - i):
                solution.route_list.append(rt.Route([]))
            return solution

        append_to_connection_archive(connection_archive, begin_station,
                                     end_station)
        connection = {
            "begin": begin_station,
            "end": end_station,
            "time": travel_time
        }

        # Add new step to route.
        connection_list.append(connection)
        route.connection_list = connection_list

        # Look for the best, closest, critical route.
        find_new_connection(begin_station, end_station, solution,
                            connection_archive, route)

        # Add newly created route to route_list.
        solution.route_list.append(route)

    return solution
Exemple #4
0
def naive(nodes, capacity, algorithm='default'):
    depot = nodes[0]

    # initial state
    clients_to_visit = [node.id for node in nodes[1:]]
    routes = []

    while len(clients_to_visit) > 0:
        # create a truck to visit nodes that have not been visited before
        truck_capacity = capacity
        truck_position = 0
        # create empty route starting in 0
        route = Route([0], 0)

        # truck looping
        while True:
            # choose next destination (next client or depot)
            clients_buffer = deepcopy(clients_to_visit)
            while len(clients_buffer) > 0:
                # pick random element of bufffer
                random_index = random.randint(0, len(clients_buffer) - 1)
                candidate_id = clients_buffer[random_index]
                clients_buffer.remove(candidate_id)
                candidate_node = nodes[candidate_id]
                candidate_cost = nodes[truck_position].distance_to_node(
                    candidate_node.x, candidate_node.y)
                if candidate_node.demand <= truck_capacity:
                    # this is a good candidate, it will be the next node
                    next_node = nodes[candidate_id]
                    cost_to_next_node = candidate_cost
                    break
            else:
                # if truck capacity is not enough for any client, return to depot
                next_node = depot
                cost_to_next_node = nodes[truck_position].distance_to_node(
                    depot.x, depot.y)

            # go to next node and update state
            route.path.append(next_node.id)
            route.cost += cost_to_next_node
            truck_capacity -= next_node.demand
            truck_position = next_node.id

            if next_node.id in clients_to_visit:
                clients_to_visit.remove(next_node.id)

            # stop truck looping when truck has returned to depot
            if truck_position == depot.id: break

        # store route data
        routes.append(route)

    # format output according to algorithm
    if algorithm == 'annealing':
        return format_to_annealing(routes)
    elif algorithm == 'local_search':
        return format_to_local_search(routes)
    else:
        return routes
def two_opt_single(route: Route):
    arc_list = route.required_arc_list
    # extract sub-route indices
    idx = sample(range(len(arc_list)), 2)
    if idx[0] < idx[1]:
        start, end = idx
    else:
        end, start = idx
    # create a new route
    new_route = Route()
    new_sub_list = deepcopy(arc_list[start: end + 1])
    for arc in new_sub_list:
        arc[1] = not arc[1]
    new_sub_list.reverse()
    new_arc_list = arc_list[: start] + new_sub_list + arc_list[end + 1:]
    new_route.required_arc_list = new_arc_list
    new_route.cost = get_route_cost(new_arc_list)
    new_route.load = route.load
    return new_route
Exemple #6
0
def initialize_columns(instance):
    """ Se inicializan las columnas: cada cliente es atendido exclusivamente por un vehículo con la capacidad mínima
    entre todos los vehículos que pueden satisfacerlo. """
    routes = []

    clients = set(instance.demand)
    clients.remove(0)

    for i in clients:
        # Entre los vehículos que pueden satisface la demanda de i, elegimos al de menor capacidad
        k = min((v for v, c in instance.cap.items() if c >= instance.demand[i]), key=lambda v: instance.cap[v])
        path = Path(k)
        path.add_node(i, instance)
        routes.append(Route.from_path(path, instance))

    return routes
def hillclimber(solution, route_iterations=10000, connection_iterations=0):
    """ Hillclimber algortihm that tries to find the optimal set of routes,
    A.K.A. an optimal solution.

    This implementation of a hillclimber contains two different iterations,
    one based on replacing routes and one based on replacing only the beginning and
    ending of a route.

    Args:
        solution: An instance of the solution class, possibly containing routes.
        route_iterations: The number of route iterations, default is 10000.
        connection_iterations: The number of connection iterations, default is 0.

    Returns:
        The solution.
    """
    # Fill solution with empty routes if the route list is empty.
    if solution.route_list == []:
        for i in range(solution.max_trains):
            route = rt.Route([])
            solution.route_list.append(route)

    score = solution.score()

    scores_array = []

    # Perform the route iteration the specified amount of times.
    for _ in range(int(route_iterations / 100)):
        for _ in range(100):
            route_index, new_route = iteration_routes(solution)
            score, solution = check_for_improvement(score, solution,
                                                    route_index, new_route)
        scores_array.append(solution.score())

    # Perform the connection iteration the specified amount of times.
    for _ in range(int(connection_iterations / 100)):
        for _ in range(100):
            route_index, new_route = iteration_connections(solution)
            score, solution = check_for_improvement(score, solution,
                                                    route_index, new_route)
        scores_array.append(solution.score())

    return solution, scores_array
def path_scanning(free: list):
    route = Route()
    i = DEPOT
    discount = config.discount
    while free:
        _d = np.inf
        _u = -1
        reverse = False
        length = len(free)
        for u in range(length):
            if route.load + free[u][IDX_DEMAND] > discount * CAPACITY:
                continue
            if DIST[i, free[u][IDX_BEGIN]] < _d:
                _d = DIST[i, free[u][IDX_BEGIN]]
                _u = u
                reverse = False
            elif DIST[i, free[u][IDX_END]] < _d:
                _d = DIST[i, free[u][IDX_END]]
                _u = u
                reverse = True
            elif getrandbits(1):
                if DIST[i, free[u][IDX_BEGIN]] == _d:
                    _u = u
                    reverse = False
                elif DIST[i, free[u][IDX_END]] == _d:
                    _u = u
                    reverse = True
        if _u != -1:
            edge = free[_u]
            if not reverse:
                route.required_arc_list.append([edge, False])
                i = edge[IDX_END]
            else:
                route.required_arc_list.append([edge, True])
                i = edge[IDX_BEGIN]
            route.load += edge[IDX_DEMAND]
            route.cost += _d + edge[IDX_COST]
            del free[_u]
        else:
            break
    route.cost += DIST[i, DEPOT]
    return route
Exemple #9
0
def create_random_solution(solution):
    """ Finds a set of random routes.

    Args:
        solution: An instance of the solution class, with empty route list.

    Returns:
        A randomly egenerated solution.
    """

    # Determine how many trains can travel.
    number_of_trains = rd.randint(int(solution.max_trains - \
    math.sqrt(solution.max_trains)), solution.max_trains)

    # Ensure our solution will contain maximum ammount of trains.
    for _ in range(solution.max_trains - number_of_trains):
        solution.route_list.append(rt.Route([]))

    # Create random routes.
    for _ in range(number_of_trains):
        route = helper.create_random_route(solution)
        solution.route_list.append(route)

    return solution
Exemple #10
0
def greedy(nodes, capacity, algorithm='default'):
    depot = nodes[0]

    # initial state
    clients_to_visit = [node.id for node in nodes[1:]]
    routes = []

    while len(clients_to_visit) > 0:
        # create a truck to visit nodes that have not been visited before
        truck_capacity = capacity
        truck_position = 0
        # create empty route starting in 0
        route = Route([0], 0)

        # truck looping
        while True:
            # calculate costs
            costs = {}
            for client_id in clients_to_visit:
                client = nodes[client_id]
                cost = nodes[truck_position].distance_to_node(
                    client.x, client.y)
                costs[client_id] = cost
            # sort possibilities from smallest to biggest cost, [(client_id, cost)]
            costs = sorted(costs.items(), key=lambda kv: (kv[1], kv[0]))

            # choose next destination (next client or depot)
            while len(costs) > 0:
                # pick first element of list
                candidate_id, candidate_cost = costs[0]
                costs = costs[1:]
                if nodes[candidate_id].demand <= truck_capacity:
                    # this is a good candidate, it will be the next node
                    next_node = nodes[candidate_id]
                    cost_to_next_node = candidate_cost
                    break
            else:
                # if truck capacity is not enough for any client, return to depot
                next_node = depot
                cost_to_next_node = nodes[truck_position].distance_to_node(
                    depot.x, depot.y)

            # go to next node and update state
            route.path.append(next_node.id)
            route.cost += cost_to_next_node
            truck_capacity -= next_node.demand
            truck_position = next_node.id

            if next_node.id in clients_to_visit:
                clients_to_visit.remove(next_node.id)

            # stop truck looping when truck has returned to depot
            if truck_position == depot.id: break

        # store route data
        routes.append(route)

    # format output according to algorithm
    if algorithm == 'annealing':
        return format_to_annealing(routes)
    elif algorithm == 'local_search':
        return format_to_local_search(routes)
    else:
        return routes
def two_opt_double(route1: Route, route2: Route):
    arc_list1 = route1.required_arc_list
    arc_list2 = route2.required_arc_list
    idx1 = (len(arc_list1) - 1) // 2
    idx2 = (len(arc_list2) - 1) // 2
    half11 = arc_list1[:idx1]
    half12 = arc_list1[idx1:]
    half21 = arc_list2[:idx2]
    half22 = arc_list2[idx2:]
    load11 = 0
    for arc in half11:
        load11 += arc[0][IDX_DEMAND]
    load12 = route1.load - load11
    load21 = 0
    for arc in half21:
        load21 += arc[0][IDX_DEMAND]
    load22 = route2.load - load21
    l1 = load11 + load22
    l2 = load12 + load21
    l3 = load11 + load21
    l4 = load12 + load22
    new_route1 = new_route2 = new_route3 = new_route4 = None
    if l1 < CAPACITY and l2 < CAPACITY:
        new_route1 = Route()
        new_route1.required_arc_list = half11 + half22
        new_route1.load = l1
        new_route1.cost = get_route_cost(new_route1.required_arc_list)
        new_route2 = Route()
        new_route2.required_arc_list = half21 + half12
        new_route2.load = l2
        new_route2.cost = get_route_cost(new_route2.required_arc_list)
    if l3 < CAPACITY and l4 < CAPACITY:
        new_route3 = Route()
        reverse21 = deepcopy(half21)
        for arc in reverse21:
            arc[1] = not arc[1]
        reverse21.reverse()
        new_route3.required_arc_list = half11 + reverse21
        new_route3.load = l3
        new_route3.cost = get_route_cost(new_route3.required_arc_list)
        new_route4 = Route()
        reverse12 = deepcopy(half12)
        for arc in reverse12:
            arc[1] = not arc[1]
        reverse12.reverse()
        new_route4.required_arc_list = reverse12 + half22
        new_route4.load = l4
        new_route4.cost = get_route_cost(new_route4.required_arc_list)
    if new_route1 is None and new_route3 is None:
        return None, None
    elif new_route1 is None:
        return new_route3, new_route4
    elif new_route3 is None:
        return new_route1, new_route2
    else:
        if new_route1.cost + new_route2.cost < new_route3.cost + new_route4.cost:
            return new_route1, new_route2
        else:
            return new_route3, new_route4
def swap(solution):
    route_list = solution.route_list
    route_list_length = len(route_list)
    route_idx_1 = randrange(0, route_list_length)
    route_1 = route_list[route_idx_1]
    route_idx_2 = randrange(0, route_list_length - 1)
    if route_idx_2 >= route_idx_1:
        route_idx_2 += 1
    route_2 = route_list[route_idx_2]
    arc_list_1 = route_1.required_arc_list
    arc_list_2 = route_2.required_arc_list
    arc_idx_1 = randrange(0, len(arc_list_1))
    load_1 = route_1.load
    load_2 = route_2.load
    arc_1 = arc_list_1[arc_idx_1]
    demand_1 = arc_1[0][IDX_DEMAND]
    flag = False
    arc_2 = arc_idx_2 = new_load_1 = new_load_2 = None
    for arc_idx_2 in range(0, len(arc_list_2)):
        arc_2 = arc_list_2[arc_idx_2]
        demand_2 = arc_2[0][IDX_DEMAND]
        new_load_1 = load_1 + demand_2 - demand_1
        new_load_2 = load_2 + demand_1 - demand_2
        if new_load_1 <= CAPACITY and new_load_2 <= CAPACITY:
            flag = True
            break
    # swapping two routes is better than swapping one route
    if flag:
        new_arc_list_11 = arc_list_1[:]
        new_arc_list_12 = arc_list_1[:]
        new_arc_list_21 = arc_list_2[:]
        new_arc_list_22 = arc_list_2[:]
        new_arc_list_11[arc_idx_1] = arc_2
        new_arc_list_12[arc_idx_1] = [arc_2[0], not arc_2[1]]
        new_arc_list_21[arc_idx_2] = arc_1
        new_arc_list_22[arc_idx_2] = [arc_1[0], not arc_1[1]]
        cost11 = get_route_cost(new_arc_list_11)
        cost12 = get_route_cost(new_arc_list_12)
        cost21 = get_route_cost(new_arc_list_21)
        cost22 = get_route_cost(new_arc_list_22)
        new_route_1 = Route()
        new_route_2 = Route()
        new_route_1.load = new_load_1
        new_route_2.load = new_load_2
        if cost11 < cost12:
            new_route_1.required_arc_list = new_arc_list_11
            new_route_1.cost = cost11
        else:
            new_route_1.required_arc_list = new_arc_list_12
            new_route_1.cost = cost12
        if cost21 < cost22:
            new_route_2.required_arc_list = new_arc_list_21
            new_route_2.cost = cost21
        else:
            new_route_2.required_arc_list = new_arc_list_22
            new_route_2.cost = cost22
        new_route_list = route_list[:]
        new_route_list[route_idx_1] = new_route_1
        new_route_list[route_idx_2] = new_route_2
        new_solution = Solution()
        new_solution.route_list = new_route_list
        new_solution.quality = solution.quality + new_route_1.cost - route_1.cost + new_route_2.cost - route_2.cost
        return new_solution
    else:
        new_route = Route()
        new_route.load = route_1.load
        arc_list_1_length = len(arc_list_1)
        if arc_list_1_length < 2:
            return solution
        arc_idx_2 = randrange(0, arc_list_1_length - 1)
        if arc_idx_2 >= arc_idx_1:
            arc_idx_2 += 1
        arc_2 = arc_list_1[arc_idx_2]
        new_arc_list_1 = arc_list_1[:]
        new_arc_list_2 = arc_list_1[:]
        new_arc_list_3 = arc_list_1[:]
        new_arc_list_4 = arc_list_1[:]
        new_arc_list_1[arc_idx_1] = arc_2
        new_arc_list_1[arc_idx_2] = arc_1
        new_arc_list_2[arc_idx_1] = [arc_2[0], not arc_2[1]]
        new_arc_list_2[arc_idx_2] = arc_1
        new_arc_list_3[arc_idx_1] = arc_2
        new_arc_list_3[arc_idx_2] = [arc_1[0], not arc_1[1]]
        new_arc_list_4[arc_idx_1] = [arc_2[0], not arc_2[1]]
        new_arc_list_4[arc_idx_2] = [arc_1[0], not arc_1[1]]

        cost1 = get_route_cost(new_arc_list_1)
        cost2 = get_route_cost(new_arc_list_2)
        cost3 = get_route_cost(new_arc_list_3)
        cost4 = get_route_cost(new_arc_list_4)

        min_cost = min(cost1, cost2, cost3, cost4)
        new_route.cost = min_cost
        if cost1 == min_cost:
            new_route.required_arc_list = new_arc_list_1
        elif cost2 == min_cost:
            new_route.required_arc_list = new_arc_list_2
        elif cost3 == min_cost:
            new_route.required_arc_list = new_arc_list_3
        else:
            new_route.required_arc_list = new_arc_list_4
        new_solution = Solution()
        new_route_list = route_list[:]
        new_route_list[route_idx_1] = new_route
        new_solution.route_list = new_route_list
        new_solution.quality = solution.quality + new_route.cost - route_1.cost
        return new_solution
def single_insertion(solution: Solution):
    route_list = solution.route_list
    remove_route_idx = randrange(0, len(route_list))
    remove_route = route_list[remove_route_idx]
    remove_arc_list = remove_route.required_arc_list
    remove_arc_idx = randrange(0, len(remove_arc_list))
    remove_arc = remove_arc_list[remove_arc_idx]
    demand = remove_arc[0][IDX_DEMAND]
    insert_route_idx = None
    for i in range(len(route_list)):
        if i != remove_route_idx and route_list[i].load + demand < CAPACITY:
            insert_route_idx = i
            break
    if insert_route_idx is not None:
        insert_route = route_list[insert_route_idx]
        insert_arc_list = insert_route.required_arc_list
        insert_arc_idx = randrange(0, len(insert_arc_list) + 1)
        insert_arc = remove_arc
        new_insert_route = Route()
        new_insert_arc_list_1 = insert_arc_list[:]
        new_insert_arc_list_2 = insert_arc_list[:]
        new_insert_arc_list_1.insert(insert_arc_idx, insert_arc)
        new_insert_arc_list_2.insert(insert_arc_idx, [insert_arc[0], not insert_arc[1]])

        cost1 = get_route_cost(new_insert_arc_list_1)
        cost2 = get_route_cost(new_insert_arc_list_2)
        if cost1 < cost2:
            new_insert_route.required_arc_list = new_insert_arc_list_1
            new_insert_route.cost = cost1
        else:
            new_insert_route.required_arc_list = new_insert_arc_list_2
            new_insert_route.cost = cost2
        new_insert_route.load = insert_route.load + demand

        new_route_list = route_list[:]
        new_remove_arc_list = remove_arc_list[:]
        new_remove_route = None
        del new_remove_arc_list[remove_arc_idx]
        # update insert route first
        new_route_list[insert_route_idx] = new_insert_route
        if not new_remove_arc_list:
            del new_route_list[remove_route_idx]
        else:
            new_remove_route = Route()
            new_remove_route.required_arc_list = new_remove_arc_list
            new_remove_route.load = remove_route.load - demand
            new_remove_route.cost = get_route_cost(new_remove_arc_list)
            new_route_list[remove_route_idx] = new_remove_route
        diff1 = (0 if new_remove_route is None else new_remove_route.cost) - remove_route.cost
        diff2 = new_insert_route.cost - insert_route.cost
        # print('cost1', diff1 + diff2)
        new_solution = Solution()
        new_solution.route_list = new_route_list
        new_solution.quality = solution.quality + diff1 + diff2
        # print(new_solution.quality, get_quality(new_route_list))
        return new_solution
    else:
        length = len(remove_arc_list)
        if length < 2:
            return solution
        insert_arc_idx = randrange(0, length - 1)
        if insert_arc_idx >= remove_arc_idx:
            insert_arc_idx += 1
        new_route = Route()
        new_route.load = remove_route.load
        arc_list_1 = remove_arc_list[:]
        arc_list_2 = remove_arc_list[:]
        arc_list_1.insert(insert_arc_idx, arc_list_1.pop(remove_arc_idx))
        del arc_list_2[remove_arc_idx]
        arc_list_2.insert(insert_arc_idx, [remove_arc[0], not remove_arc[1]])
        cost1 = get_route_cost(arc_list_1)
        cost2 = get_route_cost(arc_list_2)
        if cost1 < cost2:
            new_route.required_arc_list = arc_list_1
            new_route.cost = cost1
        else:
            new_route.required_arc_list = arc_list_2
            new_route.cost = cost2
        # new_route.required_arc_list = arc_list
        new_route_list = route_list[:]
        new_route_list[remove_route_idx] = new_route
        new_solution = Solution()
        new_solution.route_list = new_route_list
        diff = new_route.cost - remove_route.cost
        # print('cost2', diff)
        new_solution.quality = solution.quality + diff
        # print(new_solution.quality, get_quality(new_route_list))
        return new_solution
Exemple #14
0
def solve_SPk(pi, lamb, k, instance):
    """
    Se resulve el problema \bar{SP} restringido al tipo de vehículo k. Se utiliza el procedimiento de etiquetas
    detallada en la tercera sección del informe.
    :param pi: diccionario con los valores óptimos de las variables del dual correspondiente al primer conjunto de
    restricciones de CLP
    :type pi: dict
    :param lamb: diccionario con los valores óptimos de las variables del dual correspondiente al primer conjunto de
    restricciones de CLP
    :type lamb: dict
    :param k: tipo de vehículo
    :type k: int
    :param instance: datos del problema
    :type instance: Instance
    :return: ruta con menor costo reducido para el tipo de vehículo k y su costo reducido
    :rtype: Route, float
    """
    pi[0] = lamb[k]

    bk = instance.cap[k]

    # Removemos a los clientes cuya demanda supera a bk
    nodes = set(filter(lambda c: instance.demand[c] <= bk, instance.demand))
    clients = nodes.difference({0})

    # Se define la matriz de costos del subgrafo \bar{G}_k
    cost_matrix = make_cost_matrix(pi, k, instance)

    # Se inicializa el estado del depósito
    warehouse_label = Label.warehouse_label()

    # Se crean los conjuntos de estados cuya cota superior coincide con su cota inferior
    P = {j: [] for j in clients}
    P[0] = [warehouse_label]

    L = deque([warehouse_label])
    while L:
        label = L.popleft()

        for j in filter(lambda c: label.q + instance.demand[c] <= bk and c != label.pred and c != label.node, clients):
            new_label = Label(label.q + instance.demand[j], j)
            new_label.path = label.path + [j]
            new_label.pred = label.node
            new_label.reduced_cost = get_reduced_cost(new_label, cost_matrix, instance, k)

            dominates = []
            dominated = False

            # Se chequea si la nueva etiqueta no está dominada
            for s in P[j]:
                if s.reduced_cost > new_label.reduced_cost:
                    dominates.append(s)
                elif s.reduced_cost < new_label.reduced_cost:
                    dominated = True
                    break
            if not dominated:
                # Se encola la nueva etiqueta
                L.append(new_label)
                P[j].append(new_label)

                # Se eliminan todas las etiquetas dominadas por la nueva
                for s in dominates:
                    P[j].remove(s)
                    try:
                        L.remove(s)
                    except ValueError:
                        pass

    best_state = min((s for s in chain.from_iterable(P.values())), key=lambda s: getattr(s, 'reduced_cost'))
    best_path = Path.from_sequence(best_state.path, instance, k)
    return Route.from_path(best_path, instance), get_reduced_cost(best_state, cost_matrix, instance, k)
Exemple #15
0
def pulling_algorithm(pi, lamb, k, instance):
    """
    Método para resolver \bar{SP} restringido al tipo de vehículo k desarrollado por Desrochers. No utilizado para
    resolver las instancias.
    :param pi: diccionario con los valores óptimos de las variables del dual correspondiente al primer conjunto de
    restricciones de CLP
    :type pi: dict
    :param lamb: diccionario con los valores óptimos de las variables del dual correspondiente al primer conjunto de
    restricciones de CLP
    :type lamb: dict
    :param k: tipo de vehículo
    :type k: int
    :param instance: datos del problema
    :type instance: Instance
    :return: ruta con menor costo reducido para el tipo de vehículo k y su costo reducido
    :rtype: Route, float
    """
    pi[0] = lamb[k]

    bk = instance.cap[k]

    # Removemos a los clientes cuya demanda supera a bk
    nodes = set(filter(lambda c: instance.demand[c] <= bk, instance.demand))
    clients = nodes.difference({0})

    # Se define la matriz de costos del subgrafo \bar{G}_k
    cost_matrix = make_cost_matrix(pi, k, instance)

    # Se inicilizan los estados de los nodos correspondientes a los clientes
    states = [State(q, j) for j in clients for q in range(instance.demand[j], bk + 1)]

    # Se inicializa el estado del depósito
    depo_state = State.warehouse_state()
    states.append(depo_state)

    # Se crean los conjuntos de estados cuya cota superior coincide con su cota inferior
    P = {j: [] for j in clients}
    P[0] = [depo_state]

    # Consideramos el conjunto W de estados cuyas cotas no coinciden
    W = deque(sorted([s for s in states if s.ub != s.lb], key=lambda s: (s.q, s.node)))

    best_state = None
    best_rc = 1e6

    while W:
        # Entre los estados de cada j cuya cotas no coinciden, elegimos el que tiene menor q. Ante empates, elegimos el
        # que tiene menor j
        state = W.popleft()

        # Actualizamos la cota superior del estado. Primero calculamos el estado previo a (q,j) para el cual se realiza
        # el mínimo
        demand_bound = state.q - instance.demand[state.node]
        candidate_states = [s for s in states if s.node != state.node and s.q <= demand_bound]
        candidate_phis = [phi(s, state.node, cost_matrix) for s in candidate_states]
        prev_state, prev_phi = get_prev_state(candidate_states, candidate_phis)
        state.pred = prev_state.node
        state.ub = prev_phi
        state.path = prev_state.path + [state.node]

        # Se actualiza la cota superior del segundo mejor camino. Si el conjunto sobre el que se toma el mínimo es
        # vacío, la cota no se altera
        state.sub = get_secondary_ub(candidate_states, candidate_phis, state.pred)

        # Se actualiza la cota inferior del estado
        state.lb = min(map(lambda s: s.lb + cost_matrix[s.node][state.node], candidate_states))

        # Actualizamos Pj de ser necesario:
        if isclose(state.lb, state.ub):
            P[state.node].append(state)
            state_rc = get_reduced_cost(state, cost_matrix, instance, k)
            if state_rc < best_rc:
                best_rc = state_rc
                best_state = state

    best_path = Path.from_sequence(best_state.path, instance, k)
    return Route.from_path(best_path, instance), get_reduced_cost(best_state, cost_matrix, instance, k)
Exemple #16
0
from classes import Tile, Route
from classes import Side, SideType, EndPoints

import json

start_tile = Tile()
start_tile.id = 1
start_tile.order = 1
start_tile.image = "base64Image"
start_tile.cloister = False
start_tile.sides[Side.North] = SideType.City
start_tile.sides[Side.East] = SideType.Road
start_tile.sides[Side.South] = SideType.Farm
start_tile.sides[Side.West] = SideType.Road

newRoute = Route()
newRoute.rid = "1-1"
newRoute.route_type = SideType.City
newRoute.endpoints.append(EndPoints.TopLeft)
newRoute.endpoints.append(EndPoints.TopMiddle)
newRoute.endpoints.append(EndPoints.TopRight)
newRoute.meeple_pos = (100, 100)
start_tile.routes.append(newRoute)

newRoute = Route()
newRoute.rid = "1-2"
newRoute.route_type = SideType.Road
newRoute.endpoints.append(EndPoints.LeftMiddle)
newRoute.endpoints.append(EndPoints.RightMiddle)
newRoute.meeple_pos = (200, 200)
start_tile.routes.append(newRoute)