Example #1
0
def test_iterator_from_certain_index():
    sequence_generation_function = fibonacci_sequence
    iterator = SequenceIterator(sequence_func=sequence_generation_function,
                                start_value=3)
    num_of_values = 5
    sequence_values = [iterator.next() for _ in range(num_of_values)]
    assert sequence_values[0] == 3 and sequence_values[4] == 21
Example #2
0
    def __init__(self,
                 initial_chain,
                 requirements,
                 chain_generation_params,
                 parameters: Optional[GPChainOptimiserParameters] = None,
                 max_population_size: int = 55,
                 sequence_function=fibonacci_sequence,
                 log: Log = None):
        super().__init__(initial_chain, requirements, chain_generation_params,
                         parameters, log)

        if self.parameters.genetic_scheme_type != GeneticSchemeTypesEnum.parameter_free:
            self.log.error(
                f'Invalid genetic scheme type was changed to parameter-free . Continue.'
            )
            self.parameters.genetic_scheme_type = GeneticSchemeTypesEnum.parameter_free

        self.sequence_function = sequence_function
        self.max_pop_size = max_population_size
        self.iterator = SequenceIterator(
            sequence_func=self.sequence_function,
            min_sequence_value=1,
            max_sequence_value=self.max_pop_size,
            start_value=self.requirements.pop_size)
        self.generation_num = 0
        self.requirements.pop_size = self.iterator.next()
Example #3
0
    def __init__(self, initial_chain, requirements, chain_generation_params, metrics: List[MetricsEnum],
                 parameters: Optional[GPChainOptimiserParameters] = None,
                 max_population_size: int = DEFAULT_MAX_POP_SIZE,
                 sequence_function=fibonacci_sequence, log: Log = None, archive_type=None):
        super().__init__(initial_chain, requirements, chain_generation_params, metrics, parameters, log, archive_type)

        if self.parameters.genetic_scheme_type != GeneticSchemeTypesEnum.parameter_free:
            self.log.warn(f'Invalid genetic scheme type was changed to parameter-free. Continue.')
            self.parameters.genetic_scheme_type = GeneticSchemeTypesEnum.parameter_free

        self.sequence_function = sequence_function
        self.max_pop_size = max_population_size
        self.iterator = SequenceIterator(sequence_func=self.sequence_function, min_sequence_value=1,
                                         max_sequence_value=self.max_pop_size,
                                         start_value=self.requirements.pop_size)

        self.requirements.pop_size = self.iterator.next()
        self.metrics = metrics

        self.qual_position = 0
        self.compl_position = 1
Example #4
0
def test_iterator_with_max_min_constraints():
    sequence_generation_function = fibonacci_sequence
    iterator = SequenceIterator(sequence_func=sequence_generation_function,
                                min_sequence_value=10,
                                max_sequence_value=25)
    assert not iterator.has_prev()
    first_value = iterator.next()
    second_value = iterator.next()
    assert first_value == 13
    assert second_value == 21
    assert not iterator.has_next()
    assert iterator.has_prev()
Example #5
0
class GPChainParameterFreeOptimiser(GPChainOptimiser):
    """
    Implementation of the parameter-free adaptive evolutionary optimiser
    (population size and genetic operators rates is changing over time).
    For details, see original paper: https://arxiv.org/abs/2001.10178
    :param initial_chain: chain which was initialized outside the optimiser
    :param requirements: composer requirements
    :param chain_generation_params: parameters for new chain generation
    :param parameters: parameters of chain optimiser
    :param max_population_size: maximum population size
    :param log: optional parameter for log object
    """
    def __init__(self,
                 initial_chain,
                 requirements,
                 chain_generation_params,
                 parameters: Optional[GPChainOptimiserParameters] = None,
                 max_population_size: int = 55,
                 sequence_function=fibonacci_sequence,
                 log: Log = None):
        super().__init__(initial_chain, requirements, chain_generation_params,
                         parameters, log)

        if self.parameters.genetic_scheme_type != GeneticSchemeTypesEnum.parameter_free:
            self.log.error(
                f'Invalid genetic scheme type was changed to parameter-free . Continue.'
            )
            self.parameters.genetic_scheme_type = GeneticSchemeTypesEnum.parameter_free

        self.sequence_function = sequence_function
        self.max_pop_size = max_population_size
        self.iterator = SequenceIterator(
            sequence_func=self.sequence_function,
            min_sequence_value=1,
            max_sequence_value=self.max_pop_size,
            start_value=self.requirements.pop_size)
        self.generation_num = 0
        self.requirements.pop_size = self.iterator.next()

    def optimise(self,
                 objective_function,
                 offspring_rate: float = 0.5,
                 on_next_iteration_callback=None):
        if on_next_iteration_callback is None:
            on_next_iteration_callback = self.default_on_next_iteration_callback

        if self.population is None:
            self.population = self._make_population(self.requirements.pop_size)

        num_of_new_individuals = self.offspring_size(offspring_rate)
        self.log.info(
            f'pop size: {self.requirements.pop_size}, num of new inds: {num_of_new_individuals}'
        )
        with CompositionTimer() as t:

            if self.requirements.add_single_model_chains:
                best_single_model, self.requirements.primary = \
                    self._best_single_models(objective_function)

            for ind in self.population:
                ind.fitness = objective_function(ind)

            on_next_iteration_callback(self.population)

            self.log.info(f'Best metric is {self.best_individual.fitness}')

            while not t.is_time_limit_reached(self.requirements.max_lead_time) \
                    and self.generation_num != self.requirements.num_of_generations - 1:
                self.log.info(f'Generation num: {self.generation_num}')
                self.num_of_gens_without_improvements = self.update_stagnation_counter(
                )
                self.log.info(
                    f'max_depth: {self.max_depth}, no improvements: {self.num_of_gens_without_improvements}'
                )
                if self.parameters.with_auto_depth_configuration and self.generation_num != 0:
                    self.max_depth_recount()

                self.max_std = self.update_max_std()

                individuals_to_select = regularized_population(
                    reg_type=self.parameters.regularization_type,
                    population=self.population,
                    objective_function=objective_function,
                    chain_class=self.chain_class)

                if num_of_new_individuals == 1 and len(self.population) == 1:
                    new_population = list(self.reproduce(self.population[0]))
                    new_population[0].fitness = objective_function(
                        new_population[0])
                else:
                    num_of_parents = num_of_parents_in_crossover(
                        num_of_new_individuals)

                    selected_individuals = selection(
                        types=self.parameters.selection_types,
                        population=individuals_to_select,
                        pop_size=num_of_parents)

                    new_population = []

                    for parent_num in range(0, len(selected_individuals), 2):
                        new_population += self.reproduce(
                            selected_individuals[parent_num],
                            selected_individuals[parent_num + 1])

                        new_population[
                            parent_num].fitness = objective_function(
                                new_population[parent_num])
                        new_population[parent_num +
                                       1].fitness = objective_function(
                                           new_population[parent_num + 1])

                self.requirements.pop_size = self.next_population_size(
                    new_population)
                num_of_new_individuals = self.offspring_size(offspring_rate)
                self.log.info(
                    f'pop size: {self.requirements.pop_size}, num of new inds: {num_of_new_individuals}'
                )

                self.prev_best = deepcopy(self.best_individual)

                self.population = inheritance(
                    self.parameters.genetic_scheme_type,
                    self.parameters.selection_types, self.population,
                    new_population, self.num_of_inds_in_next_pop)

                if self.with_elitism:
                    self.population.append(self.prev_best)

                on_next_iteration_callback(self.population)
                self.log.info(
                    f'spent time: {round(t.minutes_from_start, 1)} min')
                self.log.info(f'Best metric is {self.best_individual.fitness}')

                self.generation_num += 1

            best = self.best_individual
            if self.requirements.add_single_model_chains and \
                    (best_single_model.fitness <= best.fitness):
                best = best_single_model
        return best

    @property
    def with_elitism(self) -> bool:
        return self.requirements.pop_size >= 10

    @property
    def current_std(self):
        if self.requirements.pop_size == 1 and self.generation_num == 0:
            std = 0
        else:
            std = np.std([ind.fitness for ind in self.population])
        return std

    def update_max_std(self):
        if self.generation_num == 0:
            std_max = self.current_std
            self.requirements.mutation_prob = 1
            self.requirements.crossover_prob = 0
        else:
            if self.max_std < self.current_std:
                std_max = self.current_std
            else:
                std_max = self.max_std
        return std_max

    def next_population_size(self, offspring: List[Any]) -> int:
        best_in_offspring = self.get_best_individual(
            offspring, equivalents_from_current_pop=False)
        fitness_improved = best_in_offspring.fitness < self.best_individual.fitness
        complexity_decreased = len(best_in_offspring.nodes) < len(
            self.best_individual.nodes
        ) and best_in_offspring.fitness <= self.best_individual.fitness
        is_max_pop_size_reached = not self.iterator.has_next()
        progress_in_both_goals = fitness_improved and complexity_decreased and not is_max_pop_size_reached
        no_progress = not fitness_improved and not complexity_decreased and not is_max_pop_size_reached
        if (progress_in_both_goals
                and len(self.population) > 2) or no_progress:
            if progress_in_both_goals:
                if self.iterator.has_prev():
                    next_population_size = self.iterator.prev()
                else:
                    next_population_size = len(self.population)
            else:
                next_population_size = self.iterator.next()

                self.requirements.mutation_prob, self.requirements.crossover_prob = self.operators_prob_update(
                    std=float(self.current_std), max_std=float(self.max_std))

        else:
            next_population_size = len(self.population)
        return next_population_size

    def operators_prob_update(self, std: float,
                              max_std: float) -> Tuple[float, float]:
        mutation_prob = 1 - (
            std / max_std) if max_std > 0 and std != max_std else 0.5
        crossover_prob = self.requirements.crossover_prob = 1 - self.requirements.mutation_prob
        return mutation_prob, crossover_prob

    def offspring_size(self, offspring_rate: float = None) -> int:
        if self.iterator.has_prev():
            num_of_new_individuals = self.iterator.prev()
            self.iterator.next()
        else:
            num_of_new_individuals = 1
        return num_of_new_individuals
Example #6
0
class GPChainParameterFreeOptimiser(GPChainOptimiser):
    """
    Implementation of the parameter-free adaptive evolutionary optimiser
    (population size and genetic operators rates is changing over time).
    For details, see original paper: https://arxiv.org/abs/2001.10178
    :param initial_chain: chain which was initialized outside the optimiser
    :param requirements: composer requirements
    :param chain_generation_params: parameters for new chain generation
    :param metrics: quality metrics
    :param parameters: parameters of chain optimiser
    :param max_population_size: maximum population size
    :param log: optional parameter for log object
    :param archive_type: type of archive with best individuals

    """

    def __init__(self, initial_chain, requirements, chain_generation_params, metrics: List[MetricsEnum],
                 parameters: Optional[GPChainOptimiserParameters] = None,
                 max_population_size: int = DEFAULT_MAX_POP_SIZE,
                 sequence_function=fibonacci_sequence, log: Log = None, archive_type=None):
        super().__init__(initial_chain, requirements, chain_generation_params, metrics, parameters, log, archive_type)

        if self.parameters.genetic_scheme_type != GeneticSchemeTypesEnum.parameter_free:
            self.log.warn(f'Invalid genetic scheme type was changed to parameter-free. Continue.')
            self.parameters.genetic_scheme_type = GeneticSchemeTypesEnum.parameter_free

        self.sequence_function = sequence_function
        self.max_pop_size = max_population_size
        self.iterator = SequenceIterator(sequence_func=self.sequence_function, min_sequence_value=1,
                                         max_sequence_value=self.max_pop_size,
                                         start_value=self.requirements.pop_size)

        self.requirements.pop_size = self.iterator.next()
        self.metrics = metrics

        self.qual_position = 0
        self.compl_position = 1

    def optimise(self, objective_function, offspring_rate: float = 0.5, on_next_iteration_callback=None):
        if on_next_iteration_callback is None:
            on_next_iteration_callback = self.default_on_next_iteration_callback

        if self.population is None:
            self.population = self._make_population(self.requirements.pop_size)

        num_of_new_individuals = self.offspring_size(offspring_rate)
        self.log.info(f'pop size: {self.requirements.pop_size}, num of new inds: {num_of_new_individuals}')

        with CompositionTimer(max_lead_time=self.requirements.max_lead_time, log=self.log) as t:

            if self.requirements.allow_single_operations:
                self.best_single_operation, self.requirements.primary = \
                    self._best_single_operations(objective_function, timer=t)

            self._evaluate_individuals(self.population, objective_function, timer=t)

            if self.archive is not None:
                self.archive.update(self.population)

            on_next_iteration_callback(self.population, self.archive)

            self.log_info_about_best()

            while t.is_time_limit_reached(self.generation_num) is False \
                    and self.generation_num != self.requirements.num_of_generations - 1:

                self.log.info(f'Generation num: {self.generation_num}')

                self.num_of_gens_without_improvements = self.update_stagnation_counter()
                self.log.info(f'max_depth: {self.max_depth}, no improvements: {self.num_of_gens_without_improvements}')

                if self.parameters.with_auto_depth_configuration and self.generation_num != 0:
                    self.max_depth_recount()

                self.max_std = self.update_max_std()

                individuals_to_select = regularized_population(reg_type=self.parameters.regularization_type,
                                                               population=self.population,
                                                               objective_function=objective_function,
                                                               chain_class=self.chain_class, timer=t)

                if self.parameters.multi_objective:
                    filtered_archive_items = duplicates_filtration(archive=self.archive,
                                                                   population=individuals_to_select)
                    individuals_to_select = deepcopy(individuals_to_select) + filtered_archive_items

                if num_of_new_individuals == 1 and len(self.population) == 1:
                    new_population = list(self.reproduce(self.population[0]))
                    self._evaluate_individuals(new_population, objective_function, timer=t)
                else:
                    num_of_parents = num_of_parents_in_crossover(num_of_new_individuals)

                    selected_individuals = selection(types=self.parameters.selection_types,
                                                     population=individuals_to_select,
                                                     pop_size=num_of_parents)

                    new_population = []

                    for parent_num in range(0, len(selected_individuals), 2):
                        new_population += self.reproduce(selected_individuals[parent_num],
                                                         selected_individuals[parent_num + 1])

                    self._evaluate_individuals(new_population, objective_function, timer=t)

                self.requirements.pop_size = self.next_population_size(new_population)
                num_of_new_individuals = self.offspring_size(offspring_rate)
                self.log.info(f'pop size: {self.requirements.pop_size}, num of new inds: {num_of_new_individuals}')

                self.prev_best = deepcopy(self.best_individual)

                self.population = inheritance(self.parameters.genetic_scheme_type, self.parameters.selection_types,
                                              self.population,
                                              new_population, self.num_of_inds_in_next_pop)

                if not self.parameters.multi_objective and self.with_elitism:
                    self.population.append(self.prev_best)

                if self.archive is not None:
                    self.archive.update(self.population)

                on_next_iteration_callback(self.population, self.archive)
                self.log.info(f'spent time: {round(t.minutes_from_start, 1)} min')
                self.log_info_about_best()

                self.generation_num += 1

            best = self.result_individual()
            self.log.info('Result:')
            self.log_info_about_best()

        return best

    @property
    def with_elitism(self) -> bool:
        if self.parameters.multi_objective:
            return False
        else:
            return self.requirements.pop_size >= 7

    @property
    def current_std(self):
        if self.parameters.multi_objective:
            std = np.std([self.get_main_metric(ind) for ind in self.population])
        else:
            std = np.std([ind.fitness for ind in self.population])
        return std

    def update_max_std(self):
        if self.generation_num == 0:
            std_max = self.current_std
            if len(self.population) == 1:
                self.requirements.mutation_prob = 1
                self.requirements.crossover_prob = 0
            else:
                self.requirements.mutation_prob = 0.5
                self.requirements.crossover_prob = 0.5
        else:
            if self.max_std < self.current_std:
                std_max = self.current_std
            else:
                std_max = self.max_std
        return std_max

    def _check_mo_improvements(self, offspring: List[Any]) -> Tuple[bool, bool]:
        complexity_decreased = False
        fitness_improved = False
        offspring_archive = tools.ParetoFront()
        offspring_archive.update(offspring)
        is_archive_improved = not is_equal_archive(self.archive, offspring_archive)
        if is_archive_improved:
            best_ind_in_prev = min(self.archive.items, key=self.get_main_metric)
            best_ind_in_current = min(offspring_archive.items, key=self.get_main_metric)
            fitness_improved = self.get_main_metric(best_ind_in_current) < self.get_main_metric(best_ind_in_prev)
            for offspring_ind in offspring_archive.items:
                if self.get_main_metric(offspring_ind) <= self.get_main_metric(best_ind_in_prev) \
                        and self.get_suppl_metric(offspring_ind) < self.get_suppl_metric(best_ind_in_prev):
                    complexity_decreased = True
                    break
        return fitness_improved, complexity_decreased

    def _check_so_improvements(self, offspring: List[Any]) -> Tuple[bool, bool]:
        suppl_metric = MetricsRepository().metric_by_id(ComplexityMetricsEnum.node_num)
        best_in_offspring = self.get_best_individual(offspring, equivalents_from_current_pop=False)
        fitness_improved = best_in_offspring.fitness < self.best_individual.fitness
        complexity_decreased = suppl_metric(best_in_offspring) < suppl_metric(
            self.best_individual) and best_in_offspring.fitness <= self.best_individual.fitness
        return fitness_improved, complexity_decreased

    def next_population_size(self, offspring: List[Any]) -> int:
        improvements_checker = self._check_so_improvements
        if self.parameters.multi_objective:
            improvements_checker = self._check_mo_improvements
        fitness_improved, complexity_decreased = improvements_checker(offspring)
        is_max_pop_size_reached = not self.iterator.has_next()
        progress_in_both_goals = fitness_improved and complexity_decreased and not is_max_pop_size_reached
        no_progress = not fitness_improved and not complexity_decreased and not is_max_pop_size_reached
        if (progress_in_both_goals and len(self.population) > 2) or no_progress:
            if progress_in_both_goals:
                if self.iterator.has_prev():
                    next_population_size = self.iterator.prev()
                else:
                    next_population_size = len(self.population)
            else:
                next_population_size = self.iterator.next()

                self.requirements.mutation_prob, self.requirements.crossover_prob = self.operators_prob_update(
                    std=float(self.current_std), max_std=float(self.max_std))

        else:
            next_population_size = len(self.population)
        return next_population_size

    def operators_prob_update(self, std: float, max_std: float) -> Tuple[float, float]:
        mutation_prob = 1 - (std / max_std) if max_std > 0 and std != max_std else 0.5
        crossover_prob = 1 - mutation_prob
        return mutation_prob, crossover_prob

    def offspring_size(self, offspring_rate: float = None) -> int:
        if self.iterator.has_prev():
            num_of_new_individuals = self.iterator.prev()
            self.iterator.next()
        else:
            num_of_new_individuals = 1
        return num_of_new_individuals

    def get_main_metric(self, ind: Any) -> float:
        return ind.fitness.values[self.qual_position]

    def get_suppl_metric(self, ind: Any) -> float:
        return ind.fitness.values[self.compl_position]
Example #7
0
def test_iterator_without_constraints():
    sequence_generation_function = fibonacci_sequence
    iterator = SequenceIterator(sequence_func=sequence_generation_function)
    num_of_values = 5
    sequence_values = [iterator.next() for _ in range(num_of_values)]
    assert sequence_values[0] == 0 and sequence_values[4] == 3