From 859b1567fad6d54f43ba568b3367f441eeb1b40e Mon Sep 17 00:00:00 2001 From: Sergey Kasyanov Date: Fri, 27 Oct 2023 10:32:15 +0300 Subject: [PATCH] wip --- .../optimisers/genetic/operators/mutation.py | 17 ++++- .../genetic/operators/reproduction.py | 69 ++++++++++--------- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/golem/core/optimisers/genetic/operators/mutation.py b/golem/core/optimisers/genetic/operators/mutation.py index f09cd4de..b1aaa30c 100644 --- a/golem/core/optimisers/genetic/operators/mutation.py +++ b/golem/core/optimisers/genetic/operators/mutation.py @@ -163,7 +163,22 @@ def _get_mutation_func(self, mutation_type: Union[MutationTypesEnum, Callable]) return adapted_mutation_func -class FastSingleMutation(Mutation): +class SpecialSingleMutation(Mutation): + def __init__(self, + parameters: 'GPAlgorithmParameters', + requirements: GraphRequirements, + graph_gen_params: GraphGenerationParams, + mutations_repo: MutationRepo, + operator_agent: OperatorAgent, + agent_experience: ExperienceBuffer, + ): + super().__init__(parameters=parameters, + requirements=requirements, + graph_gen_params=graph_gen_params, + mutations_repo=mutations_repo) + self._operator_agent = operator_agent + self.agent_experience = agent_experience + def __call__(self, individual: Individual) -> Individual: new_graph = deepcopy(individual.graph) diff --git a/golem/core/optimisers/genetic/operators/reproduction.py b/golem/core/optimisers/genetic/operators/reproduction.py index 7b75b7d4..869bf055 100644 --- a/golem/core/optimisers/genetic/operators/reproduction.py +++ b/golem/core/optimisers/genetic/operators/reproduction.py @@ -19,7 +19,7 @@ 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 Crossover -from golem.core.optimisers.genetic.operators.mutation import Mutation, MutationType +from golem.core.optimisers.genetic.operators.mutation import Mutation, MutationType, SpecialSingleMutation from golem.core.optimisers.genetic.operators.operator import PopulationT, EvaluationOperator from golem.core.optimisers.genetic.operators.selection import Selection from golem.core.optimisers.populational_optimizer import EvaluationAttemptsError @@ -82,34 +82,38 @@ def reproduce(self, population: PopulationT, evaluator: EvaluationOperator) -> P return new_population def _mutate_over_population(self, population: PopulationT, evaluator: EvaluationOperator) -> PopulationT: - # increase probability of mutation to not spend tries for no mutations - initial_parameters = deepcopy(self.parameters) - initial_parameters.mutation_prob = 1.0 - self.mutation.update_requirements(parameters=initial_parameters) - - max_tries = self.parameters.pop_size * MAX_GRAPH_GEN_ATTEMPTS_AS_POP_SIZE_MULTIPLIER - mutation_fun = partial(self._mutation_n_evaluation, evaluator=evaluator) - new_population = [] - - new_population = list(map(mutation_fun, cycle(population))) - - with Parallel(n_jobs=self.mutation.requirements.n_jobs, return_as='generator') as parallel: - new_ind_generator = parallel(delayed(mutation_fun)(ind) - for ind, _ in zip(cycle(population), range(max_tries))) - for new_ind, mutation_type, applied in new_ind_generator: - if applied: - descriptive_id = new_ind.graph.descriptive_id - if descriptive_id not in self._pop_graph_descriptive_ids: - new_population.append(new_ind) - self._pop_graph_descriptive_ids.add(descriptive_id) - if len(new_population) >= self.parameters.pop_size: - break - else: - self.mutation.agent_experience.collect_experience(new_ind.graph, mutation_type, reward=-1.0) - - # Reset mutation probabilities to default - self.mutation.update_requirements(requirements=self.parameters) - return new_population + # create common objects for parallel use + with Manager() as manager: + # create new mutation that suitable for parallel evaluation + initial_parameters = deepcopy(self.parameters) + initial_parameters.mutation_prob = 1.0 + + operator_agent = manager.Value('operator_agent', self.mutation._operator_agent) + agent_experience = manager.Value('agent_experience', self.mutation.agent_experience) + + mutation = SpecialSingleMutation(parameters=initial_parameters, + requirements=self.mutation.requirements, + graph_gen_params=self.mutation.graph_generation_params, + mutations_repo=self.mutation._mutations_repo, + operator_agent=operator_agent, + agent_experience=agent_experience) + mutation_fun = partial(self._mutation_n_evaluation, mutation=mutation, evaluator=evaluator) + + max_tries = self.parameters.pop_size * MAX_GRAPH_GEN_ATTEMPTS_AS_POP_SIZE_MULTIPLIER + new_population = [] + + with Parallel(n_jobs=self.mutation.requirements.n_jobs, return_as='generator') as parallel: + new_ind_generator = parallel(delayed(mutation_fun)(ind) + for ind, _ in zip(cycle(population), range(max_tries))) + for new_ind in new_ind_generator: + if new_ind: + descriptive_id = new_ind.graph.descriptive_id + if descriptive_id not in self._pop_graph_descriptive_ids: + new_population.append(new_ind) + self._pop_graph_descriptive_ids.add(descriptive_id) + if len(new_population) >= self.parameters.pop_size: + break + return new_population def _check_final_population(self, population: PopulationT) -> None: """ If population do not achieve required length return a warning or raise exception """ @@ -125,10 +129,9 @@ def _check_final_population(self, population: PopulationT) -> None: f'have {len(population)},' f' required {target_pop_size}!\n' + helpful_msg) - def _mutation_n_evaluation(self, individual: Individual, evaluator: EvaluationOperator): - individual, mutation_type, applied = self.mutation._mutation(individual) + def _mutation_n_evaluation(self, individual: Individual, mutation: Mutation, evaluator: EvaluationOperator): + individual = mutation(individual) if individual and self.verifier(individual.graph): individuals = evaluator([individual]) if individuals: - return individuals[0], mutation_type, applied - return individual, mutation_type, False + return individuals[0]