def test_add_repair_operator(): """ Tests if adding a repair operator correctly updates the number of operators available on the ALNS instance. """ alns = ALNS() for count in [1, 2]: alns.add_repair_operator(lambda state, rnd: None) assert_equal(len(alns.repair_operators), count)
def test_add_destroy_operator(): """ Tests if adding a destroy operator correctly updates the number of operators available on the ALNS instance. """ alns = ALNS() for count in [1, 2]: alns.add_destroy_operator(lambda state, rnd: None, name=str(count)) assert_equal(len(alns.destroy_operators), count)
def main(): if len(sys.argv) < 2: raise ValueError(f"{sys.argv[0]}: expected file location.") problem = Problem.from_file(sys.argv[1], delimiter=',') alns = ALNS(default_rng(problem.instance)) for op in D_OPERATORS: alns.add_destroy_operator(op) for op in R_OPERATORS: alns.add_repair_operator(op) local_search = LocalSearch() for op in SOLUTION_OPERATORS: local_search.add_solution_operator(op) for op in ROUTE_OPERATORS: local_search.add_route_operator(op) alns.on_best(local_search) init = initial_solution() result = alns.iterate(init, WEIGHTS, DECAY, CRITERION, ITERATIONS) # noinspection PyTypeChecker solution: Solution = result.best_state solution.to_file(f"solutions/oracs_{problem.instance}.csv")
def test_add_destroy_operator_name(): """ Tests if adding a destroy operator without an explicit name correctly takes the function name instead. """ def destroy_operator(): # placeholder pass alns = ALNS() alns.add_destroy_operator(destroy_operator) name, operator = alns.destroy_operators[0] assert_equal(name, "destroy_operator") assert_(operator is destroy_operator)
def test_add_repair_operator_name(): """ Tests if adding a repair operator without an explicit name correctly takes the function name instead. """ def repair_operator(): # placeholder pass alns = ALNS() alns.add_repair_operator(repair_operator) name, operator = alns.repair_operators[0] assert_equal(name, "repair_operator") assert_(operator is repair_operator)
def test_add_operator_same_name_warns_per_type(): """ Adding an operator with the same name as an already added operator (of the same type) should warn that the previous operator will be overwritten. """ alns = ALNS() with assert_no_warnings(): # The same name is allowed for different types of operators. alns.add_destroy_operator(lambda state, rnd: None, "test") alns.add_repair_operator(lambda state, rnd: None, "test") with assert_warns(OverwriteWarning): # Already exists as a destroy operator. alns.add_destroy_operator(lambda state, rnd: None, "test") with assert_warns(OverwriteWarning): # Already exists as a repair operator. alns.add_repair_operator(lambda state, rnd: None, "test")
def main(): if len(sys.argv) < 2: raise ValueError(f"{sys.argv[0]}: expected file location.") problem = Problem.from_file(sys.argv[1]) alns = ALNS(default_rng(problem.instance)) for op in D_OPERATORS: alns.add_destroy_operator(op) for op in R_OPERATORS: alns.add_repair_operator(op)
def main(): args = parse_args() Problem.from_instance(args.experiment, args.instance) if args.experiment == "tuning": generator = rnd.default_rng(args.instance) else: # E.g. for exp 72 and inst. 1, this becomes 7201. This way, even for # inst. 100, there will never be overlap between random number streams # across experiments. generator = rnd.default_rng(100 * args.experiment + args.instance) alns = ALNS(generator) # noqa for operator in DESTROY_OPERATORS: if args.exclude == operator.__name__: continue alns.add_destroy_operator(operator) for operator in REPAIR_OPERATORS: if args.exclude == operator.__name__: continue alns.add_repair_operator(operator) if args.exclude == "reinsert_learner": alns.on_best(reinsert_learner) init = initial_solution() criterion = get_criterion(init.objective()) result = alns.iterate(init, WEIGHTS, DECAY, criterion, ITERATIONS) location = f"experiments/{args.experiment}/{args.instance}-heuristic.json" result.best_state.to_file(location) # noqa
def get_alns_instance(repair_operators=None, destroy_operators=None, seed=None): """ Test helper method. """ alns = ALNS(rnd.RandomState(seed)) if repair_operators is not None: for idx, repair_operator in enumerate(repair_operators): alns.add_repair_operator(repair_operator, name=str(idx)) if destroy_operators is not None: for idx, destroy_operator in enumerate(destroy_operators): alns.add_destroy_operator(destroy_operator, name=str(idx)) return alns
def get_alns_instance(repair_operators=None, destroy_operators=None, seed=None): """ Test helper method. """ alns = ALNS(rnd.RandomState(seed)) if repair_operators is not None: for repair_operator in repair_operators: alns.add_repair_operator(repair_operator) if destroy_operators is not None: for destroy_operator in destroy_operators: alns.add_destroy_operator(destroy_operator) return alns
def solve_cvrp_with_alns(seed=SEED, size=SIZE, capacity=CAPACITY, number_of_depots=NUMBER_OF_DEPOTS, iterations=ITERATIONS, collect_statistics=COLLECT_STATISTICS, draw=False, **kwargs): weights = WEIGHTS operator_decay = OPERATOR_DECAY start_temperature_control = settings.START_TEMPERATURE_CONTROL cooling_rate = settings.COOLING_RATE end_temperature = settings.END_TEMPERATURE verbose = False if 'weights' in kwargs: weights = kwargs['weights'] if 'operator_decay' in kwargs: operator_decay = kwargs['operator_decay'] if 'start_temperature_control' in kwargs: start_temperature_control = kwargs['start_temperature_control'] if 'cooling_rate' in kwargs: cooling_rate = kwargs['cooling_rate'] if 'end_temperature' in kwargs: end_temperature = kwargs['end_temperature'] if 'verbose' in kwargs: verbose = kwargs['verbose'] cvrp_instance = generate_cvrp_instance(size, capacity, number_of_depots, seed) if verbose: print("Created CVRP graph") # Create an empty state initial_state = CvrpState(cvrp_instance, collect_alns_statistics=collect_statistics, size=size, number_of_depots=number_of_depots, capacity=capacity) if verbose: print("Created CVRP state.\nGenerating initial solution... ", end='', flush=True) initial_solution = generate_initial_solution(initial_state) if verbose: print("done !") initial_distance = initial_solution.objective() if draw: initial_solution.draw() number_of_node_features = len( initial_solution.dgl_graph.ndata['n_feat'][0]) number_of_edge_features = len( initial_solution.dgl_graph.edata['e_feat'][0]) # Initialize ALNS random_state = rnd.RandomState(seed) alns = ALNS(random_state) # ml_removal_heuristic = define_ml_removal_heuristic(number_of_node_features, number_of_edge_features, # NETWORK_PARAMS_FILE, NETWORK_GCN) # ml_removal_heuristic = define_ml_removal_heuristic(number_of_node_features, number_of_edge_features, # 'GatedGCN_ep71.pkl', NETWORK_GATEDGCN) alns.add_destroy_operator(removal_heuristic) alns.add_repair_operator(greedy_insertion) initial_temperature = compute_initial_temperature( initial_distance, start_temperature_control) criterion = SimulatedAnnealing(initial_temperature, end_temperature, cooling_rate) # Solve the cvrp using ALNS print("Starting ALNS, with {} iterations".format(iterations), flush=True) time_start = time.time() result = alns.iterate(initial_solution, weights, operator_decay, criterion, iterations=iterations, collect_stats=collect_statistics) time_end = time.time() print("ALNS finished in {:.1f} seconds".format(time_end - time_start), flush=True) solution = result.best_state print("Initial objective : {:.4f}".format(initial_distance)) print("Solution objective : {:.4f}".format(solution.objective())) if draw: solution.draw() # Create the statistics if necessary solution_data = {} if solution.collect_alns_statistics: solution_data = { 'Size': solution.size, 'Number_of_depots': solution.number_of_depots, 'Capacity': solution.capacity, 'Seed': seed, 'Parameters': { 'decay': operator_decay, 'degree_destruction': DEGREE_OF_DESTRUCTION, 'determinism': DETERMINISM } } solution_statistics = [{ 'destroyed_nodes': solution.statistics['destroyed_nodes'][i], 'objective_difference': result.statistics.objectives[i + 1] - result.statistics.objectives[i], 'list_of_edges': solution.statistics['list_of_edges'][i] } for i in range(iterations)] solution_data['Statistics'] = solution_statistics if 'display_proportion_objective_difference' in kwargs and kwargs[ 'display_proportion_objective_difference']: number_of_elements_train_set = {0: 0, 1: 0, 2: 0} for stat in solution_statistics: if stat['objective_difference'] > EPSILON: stat_class = 0 elif abs(stat['objective_difference']) < EPSILON: stat_class = 1 else: stat_class = 2 number_of_elements_train_set[stat_class] += 1 print("{:^20}{:^7.2%}{:^7.2%}{:^7.2%}\n\n".format( 'Proportions', round(number_of_elements_train_set[0] / iterations, 4), round(number_of_elements_train_set[1] / iterations, 4), round(number_of_elements_train_set[2] / iterations, 4), )) if 'pickle_single_stat' in kwargs and kwargs['pickle_single_stat']: if 'file_path' in kwargs: pickle_alns_solution_stats(solution_data, file_path=kwargs['file_path']) else: pickle_alns_solution_stats(solution_data) return solution_data
if __name__ == '__main__': random_state = rnd.RandomState(seed) matplotlib.use('TkAgg') irp = ItemRecoveryProblem() try: instance_path = "./instances/" + str(sys.argv[1]) except IndexError: instance_path = "./instances/" + str(instance_to_run) print("Executing hard-coded instance:", instance_to_run) irp.load_file(instance_path) alns = ALNS(random_state) alns.add_destroy_operator(remove_rand_pos) alns.add_destroy_operator(swap_rand_pos) alns.add_destroy_operator(remove_rand_sps) alns.add_destroy_operator(remove_worst_sps) alns.add_repair_operator(greedy_repair) initial_solution = greedy_repair(Solution(irp), random_state) print(instance_path) print("Initial solution:", int(initial_solution.get_cost())) #criterion = HillClimbing() criterion = SimulatedAnnealing(100, 5, 5, method='linear') start_time = time.time() result = alns.iterate(initial_solution, [3, 2, 1, 0.5], 0.8, criterion,
def Large_Neighborhood_Search(Y): class TopKState(State): """ Solution class for the top k worker task assignment problem, Series (vector) of tasks as index and workers as values the assignment quality matrix """ def __init__(self, solution, AssQuality_matrix): self.solution = solution # vector of tasks as indcies and workers as values self.tasks = solution.index self.workers = [ self.solution[task] for task in self.solution.index ] self.L_dash = [] # to keep the removed workers list self.AssQuality_matrix = AssQuality_matrix # the assignment values matrix A def copy(self): """ Helper method to ensure each solution state is immutable. """ return TopKState(self.solution.copy(), self.AssQuality_matrix.copy()) def objective(self): """ The objective function is simply the sum of all Assignment qualities """ value = 0.0 for task in self.solution.index: if not pd.isna(self.solution[task]): value += self.AssQuality_matrix.loc[task, self.solution[task]] return -value def find_L(self): worker_lose = pd.Series() lose = pd.Series() for worker in self.workers: worker_lose[worker] = 0.0 for task in self.tasks: if (self.solution[task]) == worker: worker_lose[worker] += self.AssQuality_matrix.loc[ task, worker] # total objective value (mins) the quality of the worker at hand lose.loc[worker] = self.objective() - (self.objective() - worker_lose[worker]) lose = lose.sort_values(ascending=True) return list(lose.index) """----------------------------------------------------------------------------""" """ Destroy method """ """----------------------------------------------------------------------------""" degree_of_destruction = random.random() # Random float x, 0.0 <= x < 1.0 def workers_to_remove(state): # How many worker to be removed based on the degree of destruction return int(len(state.workers) * degree_of_destruction) def worst_removal(current, random_state): """ Worst removal iteratively removes the 'worst' workers, that is, those workers that have the lowest quality. """ destroyed = current.copy() worst_workers = destroyed.find_L() # L h = workers_to_remove(current) # h the number of workers to be removed p = 5 # the parameter p set to 5 according to ref ---- while (h > 0): for task in destroyed.tasks: z = random.random() # random number in [0,1) E = int((z**p) * len(worst_workers)) if destroyed.solution.loc[task] == worst_workers[ E]: # try to find the worst worker destroyed.solution.loc[ task] = np.nan # set the task with the worst worker to NAN destroyed.workers.remove( worst_workers[E] ) # remove the worst worker from the solution destroyed.L_dash.append(worst_workers[E]) h = h - 1 return destroyed """----------------------------------------------------------------------------""" """ Repair method """ """----------------------------------------------------------------------------""" def greedy_repair(current, random_state): """ Greedily repairs a solution, """ # each worker has a capacity capacity = pd.Series() for worker in current.L_dash: capacity[worker] = 10 current.L_dash = set( current.L_dash) # L' the list of removed workers from Y' U_dash = [] # U' the list of unassigned task in Y' for task in current.solution.index: if pd.isna(current.solution.loc[task]): U_dash.append(task) # the objective value of the destroyed solution objective_value_of_destroyed = current.objective() # find Delta fw, Delta_f = pd.DataFrame(index=[task for task in U_dash]) for task in U_dash: for worker in current.L_dash: Delta_f.loc[ task, worker] = (objective_value_of_destroyed + current.AssQuality_matrix.loc[task, worker] ) - objective_value_of_destroyed for task in U_dash: if (capacity[Delta_f.loc[task, :].idxmax()]) > 0: current.solution.loc[task] = Delta_f.loc[task, :].idxmax( ) # Get the BEST worker for the task at hand capacity[Delta_f.loc[ task, :].idxmax()] -= 1 # reduce the capacity by one if (capacity[Delta_f.loc[task, :].idxmax()]) == 0: Delta_f.loc[:, Delta_f.loc[task, :].idxmax( )] = 0.0 # Burn the Best worker (Best worker will not be chosen next time) return current AssQuality_matrix = pd.read_csv( "C:/Users/Arwa/Desktop/datasets/MOO/A_matrix.csv", index_col=0) AssQuality_matrix = AssQuality_matrix.fillna(0.0) random_state = rnd.RandomState( SEED ) #generating random numbers drawn from a variety of probability distributions state = TopKState(Y, AssQuality_matrix) initial_solution = greedy_repair(state, random_state) print("########################initial###########################", type(initial_solution)) print("Initial solution objective is {0}.".format( initial_solution.objective())) """----------------------------------------------------------------------------""" """ Heuristic solution """ """----------------------------------------------------------------------------""" alns = ALNS(random_state) alns.add_destroy_operator(worst_removal) alns.add_repair_operator(greedy_repair) criterion = SimulatedAnnealing( 1, 0.1, 0.6) #'start_temperature', 'end_temperature', and 'step' result = alns.iterate(initial_solution, [3, 2, 1, 0.5], 0.8, criterion, iterations=100, collect_stats=True) H_solution = result.best_state objective = H_solution.objective() print( "########################the best heuristic solution########################\n", H_solution.solution, "with objective value\n", objective) return H_solution.solution, objective