def run(self): """ runs the bionomic algorithm The steps for the bionomic algorithm are corresponding to section 5.10 from Pinol & Beasley (2006). 1. An initial population is created 2. Each individual from the initial population is locally improved repeating: 3. Generation of parent sets 4. Generation of children sets incl. removal of duplicates 5. Local improvement of every children 6. Insertion into population of best child and removing of worst individual in population until termination The algorithm terminates once 40,000 children have been created :return: the phenotyoe of the best solution """ monitor = Monitor(self.alp.nr_planes, self.alp.nr_runways, self.alp._objective) nr_children_limit = 50000 # termination criterion: nr_children_current = 0 # counter iter_without_children = 0 # subsequent iterations with duplicates only population = Population(alp=self.alp, size=100) winners = [] # set of inserted children iteration = 0 # iteration counter start_time = time.time() while nr_children_current < nr_children_limit and iter_without_children < 10: monitor.evaluate_population(population.members, time.time() - start_time) iteration += 1 print('- ' * 10) print('[ STATUS ] Iteration %d / Children: %d' % (iteration, nr_children_current)) parent_sets, theta = population.generate_parent_sets() monitor.evaluate_parent_sets(parent_sets, theta, population._graph.number_of_edges()) children = population.generate_children(parent_sets) if len(children) > 0: monitor.evaluate_children(children) monitor.evaluate_parents(max(children)._parents) winner = population.insert_children(children) winners.append(winner) monitor.evaluate_winner(winners) nr_children_current += len(children) iter_without_children = 0 else: iter_without_children += 1 monitor.write_row() return population.members.pop().phenotype
def solve(self): """ Population management and solving structure of the genetic heuristic. Includes: 1) Initialization of population 2) Initialization of weights 3) Weight adjustment 4) Crossover (w. Parent selection) 5) Diversification After the set stoppage criterion is met the solution variable will be allocated """ global HAMMING_DISTANCE_MATRIX # 0) LOCAL SAVE STATIC PARAMETERS target_proportion = self.target_proportion nr_customers = self.vrp_data.nr_customers max_size = self.population_size + self.min_sub_pop_size iter_penalty_adjust = self.iter_penalty_adjust avg_demand = sum([customer for customer in self.vrp_data.customers ]) / nr_customers diversification_size = math.floor(self.min_sub_pop_size / 3) # 1) initialize the first population and set population specific parameters population = self.random_population_initialization() list_feas_quantity = [False] * iter_penalty_adjust list_feas_distance = [False] * iter_penalty_adjust inf_pop_size = len(population["infeasible"]) feas_pop_size = len(population["feasible"]) # 2) initialize weights # get average distance between customers (length of solution / nr_customers) # we must add 1 as the distance from and to the depot is also considered c+1 arcs avg_distance_customers = get_average_solution_length(population) self.w_penalty_load = avg_distance_customers / avg_demand # 3) Do calculations try: best_instance = get_minimum_length_instance(population["feasible"]) best_instance_length = best_instance.length except AttributeError: best_instance = None best_instance_length = np.inf # infinite -> no valid solution found so far GENERAL_LOGGER.warning( "No feasible solution in the initial solution set") # initialize stoppage criteria max_iter_wo_improvement, iter_without_impr, iter_wa = self.max_iter_wo_improvement, 0, 0 max_time, start_t = self.max_time, time() monitor = Monitor("MDVRP" + str(self.vrp_data.nr_customers)) while time( ) - start_t < max_time and iter_without_impr < max_iter_wo_improvement: # 3.1) PARENT SELECTION parent_1 = self.parent_selection(population) parent_2 = self.parent_selection(population) monitor.evaluate_parents(parent_1, parent_2) # 3.2) CROSSOVER (PIX) offspring = self.crossover(parent_1, parent_2) monitor.evaluate_offspring(offspring) # 3.3) EDUCATION length_pen_factor = self.w_penalty_duration load_pen_factor = self.w_penalty_load monitor.evaluate_penaly_factors(length_pen_factor, load_pen_factor) all_offsprings = [offspring] if random.random() <= self.education_probability: offspring.route_improvement(load_pen_factor, length_pen_factor) offspring.pattern_improvement() offspring.route_improvement(load_pen_factor, length_pen_factor) if not offspring.feasibility and random.random( ) <= self.repair_probability: repair_offspring = copy.deepcopy(offspring) for x in range(1, 3): length_pen_factor *= 10 load_pen_factor *= 10 repair_offspring.route_improvement( load_pen_factor, length_pen_factor) repair_offspring.pattern_improvement() repair_offspring.route_improvement( load_pen_factor, length_pen_factor) repair_offspring.evaluate_solution() if repair_offspring.feasibility: all_offsprings.append(repair_offspring) continue monitor.evaluate_offspring_after_education(all_offsprings[0]) for offspring in all_offsprings: list_feas_quantity[iter_wa] = (offspring.penalty_duration == 0) list_feas_distance[iter_wa] = (offspring.penalty_load == 0) monitor.evaluate_feasibility(list_feas_quantity, list_feas_distance) # 3.4) APPEND HEMMING DISTANCE merged_population = merge_population(population) hamming_distance_matrix = extend_hamming_distance_matrix( merged_population, offspring) monitor.evaluate_entropy(hamming_distance_matrix) # for offspring in all_offspring: if offspring.feasibility: # FEASIBLE OFFSPRING HANDLING feas_pop_size += 1 population["feasible"].append(offspring) # check if we need to perform survivor selection if feas_pop_size > max_size: POPULATON_LOGGER.debug("feasible_survivor_selection") feas_pop_size = self.min_sub_pop_size self.survivor_selection(population, "feasible", self.min_sub_pop_size) # check if we improved the best solution if best_instance_length > offspring.length: best_instance = offspring best_instance_length = offspring.length GENERAL_LOGGER.info(best_instance_length) iter_without_impr = 0 else: iter_without_impr += 1 else: # INFEASIBLE OFFSPRING HANDLING inf_pop_size += 1 population["infeasible"].append(offspring) # check if we need to perform survivor selection if inf_pop_size > max_size: POPULATON_LOGGER.debug("infeasible_survivor_selection") inf_pop_size += self.min_sub_pop_size self.survivor_selection(population, "infeasible", self.min_sub_pop_size) iter_without_impr += 1 # 3.4) WEIGHT ADJUSTMENTS EVERY iter_wa ITERATIONS if iter_wa == (iter_penalty_adjust - 1): iter_wa = 0 feasibility_proportion_quantity = sum( list_feas_quantity) / iter_penalty_adjust feasibility_proportion_distance = sum( list_feas_distance) / iter_penalty_adjust if feasibility_proportion_quantity <= target_proportion - 0.05: self.w_penalty_load = self.w_penalty_load * 1.2 elif feasibility_proportion_quantity >= target_proportion - 0.05: self.w_penalty_load = self.w_penalty_load * 0.85 if feasibility_proportion_distance <= target_proportion - 0.05: self.w_penalty_duration = self.w_penalty_duration * 1.2 elif feasibility_proportion_distance >= target_proportion - 0.05: self.w_penalty_duration = self.w_penalty_duration * 0.85 else: iter_wa += 1 # 3.5) DIVERSIFICATION if iter_without_impr > 0 and (iter_without_impr % self.iter_diversification) == 0: GENERAL_LOGGER.info("diversification") # the length of both population subsets will be reduced feas_pop_size, inf_pop_size = diversification_size, diversification_size # we diversify if the iterations without improvement is a true dividor of the target population = self.survivor_selection( population, "feasible", diversification_size) population = self.survivor_selection( population, "infeasible", diversification_size) # perform random fill population = self._random_fill_population(population) monitor.evaluation_population(population['feasible'], population['infeasible'], time() - start_t) monitor.evaluate_best(best_instance) monitor.write_row() self.solution = best_instance