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 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)
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)