def test_best_indivividual(self): protogene_a = ProtoGene(IntGene, 'a') protogenome = ProtoGenome([protogene_a]) pop = Population(protogenome, optimization_mode='min') pop.initialize() for i in range(pop.size): pop.individuals[i].score = i best_ind = pop.best_individual() assert pop.individuals[0].score == best_ind.score == 0 assert pop.individuals[0].get_hash() == best_ind.get_hash()
def test_sort(self): protogene_a = ProtoGene(IntGene, 'a') protogenome = ProtoGenome([protogene_a]) pop = Population(protogenome, optimization_mode='min') pop.initialize() for i in range(pop.size): pop.individuals[i].score = i pop.sort() assert pop.individuals[0].score == 0 pop_2 = Population(protogenome, optimization_mode='max') pop_2.initialize(individuals=pop.individuals) pop_2.sort() assert pop_2.individuals[-1].score == 0 assert pop_2.individuals[-1].score == pop.individuals[0].score
def test_fit_individuals(self): protogene_a = ProtoGene(IntGene, 'a', value=1) protogenome = ProtoGenome([protogene_a]) pop = Population(protogenome, optimization_mode='min') pop.initialize() def fitness_evaluator(g): return g.get_value('a') pop.fit_individuals(fitness_evaluator) for individual in pop.individuals: assert individual.score == 1 dict_cache = DictCache() pop.fit_individuals(fitness_evaluator, cache=dict_cache) for individual in pop.individuals: assert individual.score == 1 self.total_score = 0 def eval_callback(hash, individual): self.total_score += individual.score pop.fit_individuals(fitness_evaluator, cache=dict_cache, eval_callback=eval_callback) assert self.total_score == pop.size
def test_evolve(self): protogene_a = ProtoGene(IntGene, 'a', value=1) protogenome = ProtoGenome([protogene_a]) pop = Population(protogenome, optimization_mode='min') pop.initialize() def fitness_evaluator(g): return g.get_value('a') pop.fit_individuals(fitness_evaluator) new_population = pop.evolve()
class MultiGeneticAlgorithm(object): """ Genetic algorithm class :param protogenome: instance of :class:`genepi.core.protogenome.Protogenome` :param fitness_evaluator: the fitness function :param termination_criteria: one or more termination criteria for evolution :param termination_criteria_options: options for each termination criteria, each option set is passed as a dictionary. Options must be passed in the same order as termination criteria :param population_size: size of the population """ def __init__(self, protogenome, fitness_evaluator, optimization_mode='min', termination_criteria=stopcriteria.convergence_stop, termination_criteria_options={}, population_size=POPULATION_DEFAULT_SIZE, num_populations=4, isolated_cycles=3, step_callback=None, selection_method=None, elitism=True, num_parents=2, crossover_method=None, crossover_probability=0.1, crossover_wrapper_method = None, mutation_wrapper_method=None, storage_instance=None, cache_instance=None, **options): """ """ self.protogenome = protogenome self.fitness_evaluator = fitness_evaluator self.optimization_mode = optimization_mode self.termination_criteria = termination_criteria self.termination_criteria_options = termination_criteria_options self.population_size = population_size self.num_populations = num_populations self.isolated_cycles = isolated_cycles self.selection_method = selection_method self.elitism = elitism self.num_parents = num_parents self.step_callback = step_callback #default crossover and selections are handled by population self.crossover_method = crossover_method self.crossover_probability = crossover_probability #mutation wrapper self.mutation_wrapper_method = mutation_wrapper_method #crossover wrapper self.crossover_wrapper_method = crossover_wrapper_method #cache configuration if cache_instance is None: self.cache = DictCache() else: self.cache = cache_instance #storage configuration if storage_instance: self.storage = storage_instance else: self.storage = None self.options = options #termination criteria and options check if type(self.termination_criteria) == type(list): if type(self.termination_criteria_options) != type(list): raise ValueError("You must pass options for each termination criteria") if len(self.termination_criteria_options) != len(self.termination_criteria): raise ValueError("You must pass options for each termination criteria") self.populations= []; self.population = Population(self.protogenome, size=self.population_size, optimization_mode=self.optimization_mode, selection_method=self.selection_method, crossover_method=self.crossover_method, crossover_probability=self.crossover_probability, elitism=self.elitism, num_parents=self.num_parents, mutation_wrapper_method=self.mutation_wrapper_method, crossover_wrapper_method=self.crossover_wrapper_method) self.population_stats = {} self.current_stats = {} for i in range(self.num_populations): #instantiating population pop = Population(self.protogenome, size=self.population_size, optimization_mode=self.optimization_mode, selection_method=self.selection_method, crossover_method=self.crossover_method, crossover_probability=self.crossover_probability, elitism=self.elitism, num_parents=self.num_parents, mutation_wrapper_method=self.mutation_wrapper_method, crossover_wrapper_method=self.crossover_wrapper_method) self.populations.append(pop) self.population_stats[i] = {} self.current_stats[i] = None self.generation = 0 self.main_stats= {} self.current_main_stats = None def initialize(self): """ Initializes population, cache and storage """ for i in range(self.num_populations): self.populations[i].initialize() self.cache.initialize() if self.storage: self.storage.initialize() def should_terminate(self): """ Called after each evolution cycle """ if type(self.termination_criteria) == type(list()): for i,criterium in enumerate(self.termination_criteria): if criterium(self, **self.termination_criteria_options[i]): return True else: if self.termination_criteria(self, **self.termination_criteria_options): return True return False def store_individual(self, hash, individual): """ Store an individual in the storage backend. """ if self.storage: self.storage.write_individual(hash, self.generation, individual ) def evaluate_population(self, **options): """ Evaluate current population, the following operations are performed: * run fitness_evaluator (compute score on each individual) * calculate statistics * scale population individuals (compute scaled_score on each individual) """ for i in range(self.num_populations): self.populations[i].fit_individuals(self.fitness_evaluator, self.cache, eval_callback=self.store_individual) stats = self.stat_population(pop_id=i, **options) self.populations[i].scale_individuals(stats) def stat_population(self, pop_id=None,**options): """ Compute statistics for current population: min max and average scores, idle cycles. If a storage is set, stats are written to storage backend. After computation, stats are stored in population_stats property, that contains stats for all generations. the current_generation property is set to stats. """ if pop_id is not None: if pop_id not in self.population_stats: self.population_stats[pop_id] = {} scores = [individual.score for individual in self.populations[pop_id].individuals] else: scores = [individual.score for individual in self.population.individuals] avg_score = sum(scores) / len(scores) min_score = min(scores) max_score = max(scores) stats = { 'avg_score' : avg_score, 'min_score' : min_score, 'max_score' : max_score, 'generation' : self.generation } #setting stats properties if pop_id is not None: self.population_stats[pop_id][self.generation] = stats self.current_stats[pop_id] = stats if options.get('debug', None): print "pop_id:", pop_id, stats else: self.main_stats[self.generation] = stats self.main_current_stats = stats #idle cycles calculation: it counts consequent generations without improvements if self.optimization_mode == 'max': top_key = 'max_score' else: top_key = 'min_score' if self.generation == 0: stats['idle_cycles'] = 0 else: if self.main_stats[self.generation][top_key] == self.main_stats[self.generation-1][top_key]: stats['idle_cycles'] = self.main_stats[self.generation-1]['idle_cycles'] + 1 else: stats['idle_cycles'] = 0 if options.get('debug', None): print stats if self.storage: self.storage.write_population_stats(self.generation, stats) return stats def should_merge_populations(self): """ """ if self.populations[0].generation_number % self.isolated_cycles == 0: return True return False def merge_populations(self, **options): """ """ all_individuals = [] for i in range(self.num_populations): all_individuals += self.populations[i].individuals all_individuals.sort(self.population.cmp_individual) new_individuals = all_individuals[:self.population_size] new_population = self.population.copy(individuals=new_individuals) self.population = new_population stats = self.stat_population( **options) self.population.scale_individuals(stats) self.generation += 1 self.mix_populations(all_individuals, new_individuals) def mix_populations(self, all_individuals, champions): """ """ for i in range(self.num_populations): packet = [] """ for d in range(10): packet.append(random.choice(champions)) """ for x in range(self.population_size): item = (random.choice(all_individuals)) packet.append(item) all_individuals.remove(item) self.populations[i].initialize(individuals=packet) self.populations[i].sort() def best_individual(self): """ Returns best individual in population (relies on Population method) """ return self.population.best_individual() def evolve_population(self, **options): """ creates a new population with population.evolve, sets the new population as the current and increment generation. After this the population in the previous generation is lost """ for i in range(self.num_populations): new_population = self.populations[i].evolve(**options) self.populations[i] = new_population def evolve(self, **options): """ Performs the evolution cycle. This is the main method that should be normally called. Evolution goes on until a termination criterium becomes True. At the end the best individual is returned. """ if not self.termination_criteria: raise TypeError("You Must set one or more termination criteria") self.initialize() self.evaluate_population(**options) while 1: if self.should_merge_populations(): self.merge_populations(**options) if self.should_terminate(): break self.evolve_population(global_stats=self.population_stats, last_stats=self.current_stats, ga_engine=self) self.evaluate_population(**options) #self.merge_populations(**options) return self.best_individual()
def __init__(self, protogenome, fitness_evaluator, optimization_mode='min', termination_criteria=stopcriteria.convergence_stop, termination_criteria_options={}, population_size=POPULATION_DEFAULT_SIZE, num_populations=4, isolated_cycles=3, step_callback=None, selection_method=None, elitism=True, num_parents=2, crossover_method=None, crossover_probability=0.1, crossover_wrapper_method = None, mutation_wrapper_method=None, storage_instance=None, cache_instance=None, **options): """ """ self.protogenome = protogenome self.fitness_evaluator = fitness_evaluator self.optimization_mode = optimization_mode self.termination_criteria = termination_criteria self.termination_criteria_options = termination_criteria_options self.population_size = population_size self.num_populations = num_populations self.isolated_cycles = isolated_cycles self.selection_method = selection_method self.elitism = elitism self.num_parents = num_parents self.step_callback = step_callback #default crossover and selections are handled by population self.crossover_method = crossover_method self.crossover_probability = crossover_probability #mutation wrapper self.mutation_wrapper_method = mutation_wrapper_method #crossover wrapper self.crossover_wrapper_method = crossover_wrapper_method #cache configuration if cache_instance is None: self.cache = DictCache() else: self.cache = cache_instance #storage configuration if storage_instance: self.storage = storage_instance else: self.storage = None self.options = options #termination criteria and options check if type(self.termination_criteria) == type(list): if type(self.termination_criteria_options) != type(list): raise ValueError("You must pass options for each termination criteria") if len(self.termination_criteria_options) != len(self.termination_criteria): raise ValueError("You must pass options for each termination criteria") self.populations= []; self.population = Population(self.protogenome, size=self.population_size, optimization_mode=self.optimization_mode, selection_method=self.selection_method, crossover_method=self.crossover_method, crossover_probability=self.crossover_probability, elitism=self.elitism, num_parents=self.num_parents, mutation_wrapper_method=self.mutation_wrapper_method, crossover_wrapper_method=self.crossover_wrapper_method) self.population_stats = {} self.current_stats = {} for i in range(self.num_populations): #instantiating population pop = Population(self.protogenome, size=self.population_size, optimization_mode=self.optimization_mode, selection_method=self.selection_method, crossover_method=self.crossover_method, crossover_probability=self.crossover_probability, elitism=self.elitism, num_parents=self.num_parents, mutation_wrapper_method=self.mutation_wrapper_method, crossover_wrapper_method=self.crossover_wrapper_method) self.populations.append(pop) self.population_stats[i] = {} self.current_stats[i] = None self.generation = 0 self.main_stats= {} self.current_main_stats = None
def test_mutate(self): pop = Population(self.protogenome) pop.initialize() pop.mutate()
def test_copy(self): pop = Population(self.protogenome) pop.initialize() pop2 = pop.copy()
def test_initialize(self): pop = Population(self.protogenome) pop.initialize() assert len(pop.individuals) == pop.size