Skip to content

Commit

Permalink
Merge pull request #157 from HenrikMettler/bug/mutations-counter
Browse files Browse the repository at this point in the history
Fix counting of mutations in genome.mutate
  • Loading branch information
mschmidt87 committed Jul 1, 2020
2 parents 22dbdec + f3932d9 commit 104bb7f
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 16 deletions.
24 changes: 13 additions & 11 deletions cgp/genome.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ def mutate(self, mutation_rate: float, rng: np.random.RandomState):
bool
True if only inactive regions of the genome were mutated, False otherwise.
"""

def count_dna_differences(dna0: List[int], dna1: List[int]) -> int:
return len([1 for gene0, gene1 in zip(dna0, dna1) if gene0 != gene1])

n_mutations = int(mutation_rate * len(self.dna))
assert n_mutations > 0

Expand All @@ -369,9 +373,8 @@ def mutate(self, mutation_rate: float, rng: np.random.RandomState):

dna = list(self._dna)

successful_mutations = 0
only_silent_mutations = True
while successful_mutations < n_mutations:
while count_dna_differences(self.dna, dna) < n_mutations:

gene_idx = rng.randint(0, self._n_genes)
region_idx = gene_idx // self._length_per_region
Expand All @@ -380,31 +383,30 @@ def mutate(self, mutation_rate: float, rng: np.random.RandomState):
continue # nothing to do here

elif self._is_output_region(region_idx):
success = self._mutate_output_region(dna, gene_idx, rng)
if success:
silent = False
only_silent_mutations = only_silent_mutations and silent
successful_mutations += 1
silent = self._mutate_output_region(dna, gene_idx, rng)

elif self._is_hidden_region(region_idx):
silent = self._mutate_hidden_region(dna, gene_idx, active_regions, rng)
only_silent_mutations = only_silent_mutations and silent

else:
assert False # should never be reached

only_silent_mutations = only_silent_mutations and silent

self.dna = dna
return only_silent_mutations

def _mutate_output_region(self, dna: List[int], gene_idx: int, rng: np.random.RandomState):
def _mutate_output_region(
self, dna: List[int], gene_idx: int, rng: np.random.RandomState
) -> bool:
assert self._is_gene_in_output_region(gene_idx)

if not self._is_function_gene(gene_idx) and self._is_active_input_gene(gene_idx):
permissible_inputs = self._permissible_inputs_for_output_region()
dna[gene_idx] = rng.choice(permissible_inputs)
return True
else:
return False
else:
return True

def _mutate_hidden_region(
self, dna: List[int], gene_idx: int, active_regions: List[int], rng: np.random.RandomState
Expand Down
3 changes: 2 additions & 1 deletion examples/example_evo_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ def recording_callback(pop):

f_graph = champion.to_func()
x_0_range = np.linspace(-5.0, 5.0, 20)
x_1_range = np.ones_like(x_0_range) * 2.0 # fix x_1 such than 1d plot makes sense
x_1_range = np.ones_like(x_0_range) * 2.0
# fix x_1 such than 1d plot makes sense
y = [f_graph([x_0, x_1_range[0]]) for x_0 in x_0_range]
y_target = target_function(np.hstack([x_0_range.reshape(-1, 1), x_1_range.reshape(-1, 1)]))

Expand Down
31 changes: 28 additions & 3 deletions test/test_genome.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,31 @@ def test_mutate_output_region(rng_seed):
ID_NON_CODING_GENE,
]

assert genome._mutate_output_region(list(dna), 9, rng) is False
assert genome._mutate_output_region(list(dna), 10, rng) is True
assert genome._mutate_output_region(list(dna), 11, rng) is False
assert genome._mutate_output_region(list(dna), 9, rng) is True
assert genome._mutate_output_region(list(dna), 10, rng) is False
assert genome._mutate_output_region(list(dna), 11, rng) is True


@pytest.mark.parametrize("mutation_rate", [0.02, 0.05, 0.2])
def test_correct_number_of_mutations(mutation_rate, rng_seed):

n_inputs = 2
n_outputs = 1
n_columns = 10
n_rows = 2
levels_back = 5
primitives = (cgp.Add, cgp.Sub, cgp.Mul, cgp.Div, cgp.ConstantFloat)

rng = np.random.RandomState(rng_seed)
genome = cgp.Genome(n_inputs, n_outputs, n_columns, n_rows, levels_back, primitives)
genome.randomize(rng)

n_mutations = 0
genome_new = genome.clone()
genome_new.mutate(mutation_rate, rng)
for (gene_0, gene_1) in zip(genome.dna, genome_new.dna):
if gene_0 != gene_1:
n_mutations += 1

n_mutations_expected = int(mutation_rate * len(genome.dna))
assert n_mutations == n_mutations_expected
2 changes: 1 addition & 1 deletion test/test_population.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_crossover_two_offspring(population_simple_fitness):


def test_mutate(population_params, genome_params):
population_params["mutation_rate"] = 0.999999
population_params["mutation_rate"] = 0.5
pop = cgp.Population(**population_params, genome_params=genome_params)

offspring = pop.parents
Expand Down

0 comments on commit 104bb7f

Please sign in to comment.