Skip to content

Commit

Permalink
Fix parameter free scheme (#156)
Browse files Browse the repository at this point in the history
* Add minimal mutation probability

* Refactor adaptive pop size

* Add test
  • Loading branch information
YamLyubov authored Aug 2, 2023
1 parent fe63e5e commit 533ab80
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 10 deletions.
3 changes: 3 additions & 0 deletions golem/core/optimisers/genetic/gp_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ def _update_requirements(self):
if not self.generations.is_any_improved:
self.graph_optimizer_params.mutation_prob, self.graph_optimizer_params.crossover_prob = \
self._operators_prob.next(self.population)
self.log.info(
f'Next mutation proba: {self.graph_optimizer_params.mutation_prob}; '
f'Next crossover proba: {self.graph_optimizer_params.crossover_prob}')
self.graph_optimizer_params.pop_size = self._pop_size.next(self.population)
self.requirements.max_depth = self._graph_depth.next()
self.log.info(
Expand Down
2 changes: 1 addition & 1 deletion golem/core/optimisers/genetic/gp_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class GPAlgorithmParameters(AlgorithmParameters):
In the `steady_state` scheme at each iteration only one individual is updated.
The `parameter_free` scheme is an adaptive variation of the `generational` scheme.
The `parameter_free` scheme is an adaptive variation of the `steady_state` scheme.
It specifies that the population size and the probability of mutation and crossover
change depending on the success of convergence. If there are no improvements in fitness,
then the size and the probabilities increase. When fitness improves, the size and the
Expand Down
3 changes: 2 additions & 1 deletion golem/core/optimisers/genetic/parameters/mutation_prob.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class AdaptiveMutationProb(AdaptiveParameter[float]):
def __init__(self, default_prob: float = 0.5):
self._current_std = 0.
self._max_std = 0.
self._min_proba = 0.05
self._default_prob = default_prob

@property
Expand All @@ -23,7 +24,7 @@ def next(self, population: PopulationT) -> float:
elif self._max_std == 0:
mutation_prob = self._default_prob
else:
mutation_prob = 1. - (self._current_std / self._max_std)
mutation_prob = max(1. - (self._current_std / self._max_std), self._min_proba)
return mutation_prob

def _update_std(self, population: PopulationT):
Expand Down
11 changes: 3 additions & 8 deletions golem/core/optimisers/genetic/parameters/population_size.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,13 @@ def initial(self) -> int:
return self._initial

def next(self, population: PopulationT) -> int:
fitness_improved = self._improvements.is_quality_improved
complexity_decreased = self._improvements.is_complexity_improved
progress_in_both_goals = fitness_improved and complexity_decreased
no_progress = not fitness_improved and not complexity_decreased
pop_size = len(population)
too_many_fitness_eval_errors = \
pop_size / self._iterator.current() < 0.5
too_many_fitness_eval_errors = pop_size / self._iterator.current() < 0.5

if too_many_fitness_eval_errors or no_progress:
if too_many_fitness_eval_errors or not self._improvements.is_any_improved:
if self._iterator.has_next():
pop_size = self._iterator.next()
elif progress_in_both_goals and pop_size > 0:
elif self._improvements.is_quality_improved and self._improvements.is_complexity_improved and pop_size > 0:
if self._iterator.has_prev():
pop_size = self._iterator.prev()

Expand Down
42 changes: 42 additions & 0 deletions test/integration/test_genetic_schemes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from functools import partial

import numpy as np
import pytest

from examples.synthetic_graph_evolution.experiment_setup import run_trial
from examples.synthetic_graph_evolution.generators import generate_labeled_graph
from examples.synthetic_graph_evolution.tree_search import tree_search_setup
from golem.core.optimisers.genetic.gp_params import GPAlgorithmParameters
from golem.core.optimisers.genetic.operators.base_mutations import MutationTypesEnum
from golem.core.optimisers.genetic.operators.crossover import CrossoverTypesEnum
from golem.core.optimisers.genetic.operators.inheritance import GeneticSchemeTypesEnum


def set_up_params(genetic_scheme: GeneticSchemeTypesEnum):
gp_params = GPAlgorithmParameters(
multi_objective=False,
mutation_types=[
MutationTypesEnum.single_add,
MutationTypesEnum.single_drop,
],
crossover_types=[CrossoverTypesEnum.none],
genetic_scheme_type=genetic_scheme
)
return gp_params


@pytest.mark.parametrize('genetic_type', GeneticSchemeTypesEnum)
def test_genetic_scheme_types(genetic_type):
target_graph = generate_labeled_graph('tree', 4, node_labels=['x'])
num_iterations = 30

gp_params = set_up_params(genetic_type)
found_graph, history = run_trial(target_graph=target_graph,
optimizer_setup=partial(tree_search_setup, algorithm_parameters=gp_params),
num_iterations=num_iterations)
assert found_graph is not None
# at least 20% more generation than early_stopping_iterations were evaluated
assert history.generations_count >= num_iterations // 3 * 1.2
# metric improved
assert np.mean([ind.fitness.value for ind in history.generations[0].data]) > \
np.mean([ind.fitness.value for ind in history.generations[-1].data])

0 comments on commit 533ab80

Please sign in to comment.