def generate_seq_info(seq: Tuple[int], param: Param, vehicle_type: int = -1) -> SeqInfo: """ generate a SeqInfo for a sequence :param seq: :param param: :param vehicle_type: :return: """ ds, tm, volume, weight, first, last, ntj, position = param is_delivery, is_pickup, is_charge = ntj is_type2 = True if vehicle_type == 2 else False volume_limit = VOLUME_2 if is_type2 else VOLUME_1 weight_limit = WEIGHT_2 if is_type2 else WEIGHT_1 distance_limit = DISTANCE_2 if is_type2 else DISTANCE_1 charge_index = [i for i in range(len(seq)) if is_charge(seq[i])] # init volume and weight delivery_node = list(filter(lambda x: is_delivery(x), seq)) init_volume = sum([volume[x] for x in zip(delivery_node)]) init_weight = sum([weight[x] for x in zip(delivery_node)]) current_volume = init_volume current_weight = init_weight if current_volume > volume_limit or current_weight > weight_limit: if is_type2 or current_volume > VOLUME_2 or current_weight > WEIGHT_2: return None else: is_type2 = True volume_limit = VOLUME_2 weight_limit = WEIGHT_2 distance_limit = DISTANCE_2 # first node node1 = (0, ) current_distance = 0 max_volume = init_volume max_weight = init_weight serve_time = 0 eps = 0 # earliest possible starting lps = 960 # latest possible starting total_wait = 0 total_shift = 0 total_delta = 0 time_len = 0 charge_cnt = 0 # init eps_list and lps_list eps_list = [0] lps_list = [960] for node2 in ((nid, ) for nid in seq): # distance current_distance += ds[node1, node2] # volume and weight if is_delivery(node2[0]): current_volume -= volume[node2] current_weight -= weight[node2] elif is_pickup(node2[0]): current_volume += volume[node2] current_weight += weight[node2] max_volume = max(max_volume, current_volume) max_weight = max(max_weight, current_weight) # time window shift = max(0, lps + tm[node1, node2] + serve_time - last[node2]) if shift > 0: lps = last[node2] total_shift += shift else: lps += tm[node1, node2] + serve_time if max_volume > volume_limit or max_weight > weight_limit or \ current_distance > (charge_cnt + 1) * distance_limit or \ lps - tm[node1, node2] - serve_time < eps: if is_type2: return None else: if max_volume > VOLUME_2 or max_weight > WEIGHT_2 or \ current_distance > (charge_cnt + 1) * DISTANCE_2 or \ lps - tm[node1, node2] - serve_time < eps: return None else: is_type2 = True volume_limit = VOLUME_2 weight_limit = WEIGHT_2 distance_limit = DISTANCE_2 wait = max(0, first[node2] - lps) if wait > 0: total_wait += wait lps = first[node2] delta = max(0, first[node2] - eps - serve_time - tm[node1, node2]) total_delta += delta # eps: eps_node2 if delta > 0: eps = first[node2] else: eps += tm[node1, node2] + serve_time # eps = max(eps + serve_time + tm[node1, node2], first[node2]) # update eps_list and lps_list if delta > 0 or wait > 0: eps_list = [x + delta - wait for x in eps_list] if shift > 0: lps_list = [x - shift for x in lps_list] eps_list.append(eps) lps_list.append(lps) # time_len += tm + serve + wait time_len += tm[node1, node2] + serve_time + wait if is_charge(node2[0]): charge_cnt += 1 serve_time = 30 node1 = node2 # get back to depot time_len += SERVE_TIME + tm[node1, (0, )] current_distance += ds[node1, (0, )] eps_list.append(0 + total_delta - total_wait + time_len) lps_list.append(960 - total_shift + time_len) buffer = lps_list[0] - eps_list[0] if current_distance > (charge_cnt + 1) * distance_limit \ or lps_list[-1] > 960: if is_type2 or current_distance > (charge_cnt + 1) * DISTANCE_2 \ or lps_list[-1] > 960: return None else: is_type2 = True # choose vehicle type if vehicle_type == -1: if is_type2: vehicle_type = 2 else: vehicle_type = 1 return SeqInfo( vehicle_type, max_volume, max_weight, current_distance, eps_list, lps_list, time_len, total_wait, buffer, charge_index, sum( calculate_each_cost(current_distance, vehicle_type, total_wait, charge_cnt)))
last = {**last_d, **last_p, **last_c} del last_d, last_p, last_c first[(0,)] = 0 last[(0,)] = 960 candidate_seqs = {*node_id_d, *node_id_p} param = Param(ds, tm, volume, weight, first, last, ntj, position) # init route list init_route_dict = { seq: SeqInfo( 2, volume[seq], weight[seq], ds[(0,), seq] + ds[seq, (0,)], tm[(0,), seq] + SERVE_TIME + tm[seq, (0,)], 0 if first[seq] - tm[(0,), seq] < 0 else first[seq] - tm[(0,), seq], last[seq] - tm[(0,), seq], first[seq] + SERVE_TIME + tm[seq, (0,)], last[seq] + SERVE_TIME + tm[seq, (0,)], 0, 0, (ds[(0,), seq] + ds[seq, (0,)]) * TRANS_COST_2 + FIXED_COST_2 if ds[(0,), seq] + ds[seq, (0,)] <= DISTANCE_2 else M ) for seq in candidate_seqs } route_dict = deepcopy(init_route_dict) while True: route_dict, new_seq_count = merge_saving_value_pairs( candidate_seqs, route_dict, param , node_id_c, time_sorted_limit=time_sorted_limit, merge_seq_each_time=merge_seq_each_time ) # update candidate seqs
def generate_seq_info_refactor(seq: Tuple[int], param: Param, vehicle_type: int = -1) -> SeqInfo: ds, tm, volume, weight, first, last, ntj, position = param is_delivery, is_pickup, is_charge = ntj is_type2 = True if vehicle_type == 2 else False volume_limit = VOLUME_2 if is_type2 else VOLUME_1 weight_limit = WEIGHT_2 if is_type2 else WEIGHT_1 distance_limit = DISTANCE_2 if is_type2 else DISTANCE_1 charge_index = [i for i in range(len(seq)) if is_charge(seq[i])] # use zip tuple_seq = tuple(zip((0, *seq, 0))) # volume and weight check max_volume_weight = tuple( map( max, zip(*accumulate( chain((reduce(_2_elem_tuple_add, ( (volume.get(x, 0), weight.get(x, 0)) for x in tuple_seq[1:-1] if is_delivery(x))), ), ( (-volume.get(x, 0), -weight.get(x, 0)) if is_delivery(x) else ( volume.get(x, 0), weight.get(x, 0)) for x in tuple_seq[1:-1])), _2_elem_tuple_add)))) if max_volume_weight[0] > volume_limit or \ max_volume_weight[1] > weight_limit: # if vehicle type is specific and limit is violated, return None if vehicle_type == 2 or vehicle_type == 1: return None if max_volume_weight[0] > VOLUME_2 or max_volume_weight[1] > WEIGHT_2: return None else: is_type2 = True distance_limit = DISTANCE_2 ds_edge = tuple(map(lambda x: ds[x], zip(tuple_seq[:-1], tuple_seq[1:]))) ds_limit = ((x + 1) * distance_limit for x in accumulate( 1 if is_charge(x) else 0 for x in tuple_seq[:-1])) if any(map(lambda x, y: x > y, accumulate(ds_edge), ds_limit)): if vehicle_type == 1 or vehicle_type == 2 or is_type2: return None else: ds_limit = ((x + 1) * DISTANCE_2 for x in accumulate( 1 if is_charge(x) else 0 for x in tuple_seq[:-1])) if any(map(lambda x, y: x > y, accumulate(ds_edge), ds_limit)): return None else: is_type2 = True # TODO: optimize time window constraint eps_list, lps_list, time_len, total_wait, buffer = schedule_time( seq, param) if vehicle_type == -1: if is_type2: vehicle_type = 2 else: vehicle_type = 1 return SeqInfo( vehicle_type, *max_volume_weight, sum(ds_edge), eps_list, lps_list, time_len, total_wait, buffer, charge_index, sum( calculate_each_cost(sum(ds_edge), vehicle_type, total_wait, len(charge_index))))
for seq in candidate_seqs: # 0 if first[seq] - tm[(0,), seq] < 0 else first[seq] - tm[(0,), seq], # last[seq] - tm[(0,), seq], eps_list = [ 0, tm[(0, ), seq], tm[(0, ), seq] + SERVE_TIME + tm[seq, (0, )] ] lps_list = [ last[seq] - tm[(0, ), seq], last[seq], last[seq] + SERVE_TIME + tm[seq, (0, )] ] cost = (ds[(0,), seq] + ds[seq, (0,)]) * TRANS_COST_2 + FIXED_COST_2 if \ ds[(0,), seq] + ds[seq, (0,)] <= DISTANCE_2 else M init_route_dict[seq] = SeqInfo(2, volume[seq], weight[seq], ds[(0, ), seq] + ds[seq, (0, )], eps_list, lps_list, first[seq] + SERVE_TIME + tm[seq, (0, )], last[seq] + SERVE_TIME + tm[seq, (0, )], 0, 0, cost) # ============================== vrp ================================ route_dict = saving_value_construct(candidate_seqs, init_route_dict, param, node_id_c, time_sorted_limit=time_sorted_limit, merge_seq_each_time=merge_seq_each_time) del candidate_seqs # ======================= greedy insert ============================== neighborhood_dict = get_neighborhood_dict( route_dict, position, neighborhood_number=neighborhood_number)