def test_get_crossover_points(self, n_crossover_points, expected_result): gen_alg = GenAlgSolver( fitness_function=lambda x: x.sum(), n_genes=10, n_crossover_points=n_crossover_points, random_state=42, ) crossover_points = gen_alg.get_crossover_points() assert np.equal(crossover_points, expected_result).all()
def __init__( self, n_genes: int, fitness_function=None, n_bits: int = 1, max_gen: int = 1000, pop_size: int = 100, mutation_rate: float = 0.15, selection_rate: float = 0.5, selection_strategy: str = "roulette_wheel", verbose: bool = True, show_stats: bool = True, plot_results: bool = True, excluded_genes: Sequence = None, n_crossover_points: int = 1, random_state: int = None, ): """ :param fitness_function: can either be a fitness function or a class implementing a fitness function + methods to override the default ones: create_offspring, mutate_population, initialize_population :param n_genes: number of genes (variables) to have in each chromosome :param n_bits: number of bits representing each gene :param max_gen: maximum number of generations to perform the optimization :param pop_size: population size :param mutation_rate: rate at which random mutations occur :param selection_rate: percentage of the population to be selected for crossover :param selection_strategy: strategy to use for selection :param verbose: whether to print iterations status :param show_stats: whether to print stats at the end :param plot_results: whether to plot results of the run at the end """ GenAlgSolver.__init__( self, fitness_function=fitness_function, n_genes=n_genes * n_bits, max_gen=max_gen, pop_size=pop_size, mutation_rate=mutation_rate, selection_rate=selection_rate, selection_strategy=selection_strategy, verbose=verbose, show_stats=show_stats, plot_results=plot_results, excluded_genes=excluded_genes, n_crossover_points=n_crossover_points, random_state=random_state, )
def test_no_fitness_function_error(self): with pytest.raises(Exception) as excinfo: GenAlgSolver(n_genes=10, random_state=42) assert excinfo.type == NoFitnessFunction assert (str( excinfo.value ) == "A fitness function must be defined or provided as an argument")
def test_mutate_population(self, excluded_genes, expected_mutation_rows, expected_mutation_cols): gen_alg = GenAlgSolver( fitness_function=lambda x: x.sum(), n_genes=10, pop_size=10, random_state=42, excluded_genes=excluded_genes, ) mutation_rows, mutation_cols = gen_alg.mutate_population(None, 10) assert np.equal(mutation_rows, expected_mutation_rows).all() assert np.equal(mutation_cols, expected_mutation_cols).all() if excluded_genes is not None: for index in excluded_genes: assert index not in mutation_cols
def test_make_selection(self, pop_size, selection_strategy, expected_ma, expected_pa): np.random.seed(42) n_genes = 10 gen_alg = GenAlgSolver( fitness_function=lambda x: x.sum(), pop_size=pop_size, selection_strategy=selection_strategy, n_genes=n_genes, random_state=42, ) fitness = np.random.rand(pop_size, 1) ma, pa = gen_alg.select_parents(fitness) assert np.allclose(ma, expected_ma) assert np.allclose(pa, expected_pa)
def test_exceptions(self, algorithm_input, expected_exception_message): with pytest.raises(Exception) as excinfo: gen_alg = GenAlgSolver(fitness_function=lambda x: x.sum(), n_genes=1, random_state=42, **algorithm_input) print(excinfo) assert excinfo.type == InvalidInput assert str(excinfo.value) == expected_exception_message
def mutate_population(self, population, n_mutations): """ Mutates the population by randomizing specific positions of the population individuals. :param population: the population at a given iteration :param n_mutations: number of mutations to be performed. :return: the mutated population """ # mutation_rows, mutation_cols = super( # BinaryGenAlgSolver, self # ).mutate_population(population, n_mutations) mutation_rows, mutation_cols = \ GenAlgSolver.mutate_population(self,population,n_mutations) population[mutation_rows, mutation_cols] = np.abs(population - 1)[mutation_rows, mutation_cols] return population
def __init__( self, n_genes: int, fitness_function=None, max_gen: int = 1000, pop_size: int = 100, mutation_rate: float = 0.15, selection_rate: float = 0.5, selection_strategy: str = "roulette_wheel", verbose: bool = True, show_stats: bool = True, plot_results: bool = True, excluded_genes: Sequence = None, variables_limits=(-10, 10), problem_type=float, n_crossover_points: int = 1, random_state: int = None, ): """ :param fitness_function: can either be a fitness function or a class implementing a fitness function + methods to override the default ones: create_offspring, mutate_population, initialize_population :param n_genes: number of genes (variables) to have in each chromosome :param max_gen: maximum number of generations to perform the optimization :param pop_size: population size :param mutation_rate: rate at which random mutations occur :param selection_rate: percentage of the population to be selected for crossover :param selection_strategy: strategy to use for selection :param verbose: whether to print iterations status :param show_stats: whether to print stats at the end :param plot_results: whether to plot results of the run at the end :param variables_limits: limits for each variable [(x1_min, x1_max), (x2_min, x2_max), ...]. If only one tuple is provided, then it is assumed the same for every variable :param problem_type: whether problem is of float or integer type """ GenAlgSolver.__init__( self, fitness_function=fitness_function, n_genes=n_genes, max_gen=max_gen, pop_size=pop_size, mutation_rate=mutation_rate, selection_rate=selection_rate, selection_strategy=selection_strategy, verbose=verbose, show_stats=show_stats, plot_results=plot_results, excluded_genes=excluded_genes, n_crossover_points=n_crossover_points, random_state=random_state, ) if not variables_limits: min_max = np.iinfo(np.int64) variables_limits = [(min_max.min, min_max.max) for _ in range(n_genes)] if get_input_dimensions(variables_limits) == 1: variables_limits = [variables_limits for _ in range(n_genes)] self.variables_limits = variables_limits self.problem_type = problem_type