Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix counting of mutations in genome.mutate #157

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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):
mschmidt87 marked this conversation as resolved.
Show resolved Hide resolved

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
jakobj marked this conversation as resolved.
Show resolved Hide resolved

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