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
Esempio n. 2
0
    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