def init_population(self): """Initialize the population.""" self.population = Population() for i in range(self.config.population_size): spawner = self.config.spawner genome = spawner.spawn_genome(self.config.initial_genome_size) self.population.add(Individual(genome))
def init_population(self): """Initialize the population.""" spawner = self.config.spawner init_gn_size = self.config.initial_genome_size pop_size = self.config.population_size signature = self.config.signature self.population = Population() if self._p_context is not None: gen_func = partial(_spawn_individual, self._p_context.ns.spawner, init_gn_size, signature) for indiv in self._p_context.pool.imap_unordered(gen_func, range(pop_size)): self.population.add(indiv) else: for i in range(pop_size): self.population.add(_spawn_individual(spawner, init_gn_size, signature))
def select_one(self, population: Population) -> Individual: """Return single individual from population. Parameters ---------- population A Population of Individuals. Returns ------- Individual The selected Individual. """ candidates = self._preselection(population) cases = np.arange(len(population[0].error_vector)) shuffle(cases) if isinstance(self.epsilon, np.ndarray): ep = np.apply_along_axis(median_absolute_deviation, 0, population.all_error_vectors()) elif isinstance(self.epsilon, (float, int, np.int64, np.float64)): ep = self.epsilon while len(cases) > 0 and len(candidates) > 1: case = cases[0] errors_this_case = [i._error_vector[case] for i in candidates]
def step(self): """Perform one generation, or step, of the Simulated Annealing. The step method assumes an evaluated Population one Individual and produces a single candidate Individual. If the candidate individual passes the acceptance function, it becomes the Individual in the Population. """ super().step() if self._get_temp() <= 0: return candidate = Individual( self.config.get_variation_op().produce( [self.population.best().genome], self.config.spawner ), self.config.signature ) candidate.error_vector = self.config.evaluator.evaluate(candidate.program) acceptance_probability = self._acceptance(candidate.total_error) if np.random.random() < acceptance_probability: self.population = Population().add(candidate)
def step(self): """Perform one generation (step) of the genetic algorithm. The step method assumes an evaluated Population and performs parent selection and variation (producing children). """ self.population = Population( [self._make_child() for _ in range(self.config.population_size)])
def _select_with_stream(self, population: Population, cases: CaseStream) -> Individual: candidates = one_individual_per_error_vector(population) ep = self.epsilon if isinstance(ep, bool) and ep: ep = self._epsilon_from_mad(population.all_error_vectors()) for case in cases: if len(candidates) <= 1: break errors_this_case = [i.error_vector[case] for i in candidates] best_val_for_case = min(errors_this_case)
def pop(atoms): i1 = Individual(Genome()) i1.error_vector = np.array([0, 20, 0]) # 20 i2 = Individual(Genome()) i2.error_vector = np.array([3, 3, 3]) # 9 i3 = Individual(Genome()) i3.error_vector = np.array([1, 2, 3]) # 6 i4 = Individual(Genome()) i4.error_vector = np.array([4, 3, 5]) # 12 return Population([i1, i2, i3, i4])
def select_one(self, population: Population) -> Individual: """Return single individual from population. Parameters ---------- population A Population of Individuals. Returns ------- Individual The selected Individual. """ return population.best()
def population(atoms, push_config): gn = Genome() sig = ProgramSignature(arity=0, output_stacks=[], push_config=push_config) i1 = Individual(gn, sig) i1.error_vector = np.array([0, 20, 0]) # 20 i2 = Individual(gn, sig) i2.error_vector = np.array([3, 3, 3]) # 9 i3 = Individual(gn, sig) i3.error_vector = np.array([1, 2, 3]) # 6 i4 = Individual(gn, sig) i4.error_vector = np.array([4, 3, 5]) # 12 return Population([i1, i2, i3, i4])
def select(self, population: Population, n: int = 1) -> Sequence[Individual]: """Return `n` individuals from the population. Parameters ---------- population A Population of Individuals. Returns ------- Sequence[Individual] The selected Individuals. """ return population.best_n(n)
def select(self, population: Population, n: int = 1) -> Sequence[Individual]: """Return `n` individuals from the population. Parameters ---------- population A Population of Individuals. n : int The number of parents to select from the population. Default is 1. Returns ------- Sequence[Individual] The selected Individuals. """ super().select(population, n) return population.best_n(n)
class SearchAlgorithm(ABC): """Base class for all search algorithms. Parameters ---------- config : SearchConfiguration The configuation of the search algorithm. Attributes ---------- config : SearchConfiguration The configuration of the search algorithm. generation : int The current generation, or iteration, of the search. best_seen : Individual The best Individual, with respect to total error, seen so far. population : Population The current Population of individuals. """ def __init__(self, config: SearchConfiguration): self.config = config self._p_context = config.parallel_context self.generation = 0 self.best_seen = None self.population = None self.init_population() def init_population(self): """Initialize the population.""" spawner = self.config.spawner init_gn_size = self.config.initial_genome_size pop_size = self.config.population_size signature = self.config.signature self.population = Population() if self._p_context is not None: gen_func = partial(_spawn_individual, self._p_context.ns.spawner, init_gn_size, signature) for indiv in self._p_context.pool.imap_unordered( gen_func, range(pop_size)): self.population.add(indiv) else: for i in range(pop_size): self.population.add( _spawn_individual(spawner, init_gn_size, signature)) @tap @abstractmethod def step(self) -> bool: """Perform one generation (step) of the search. The step method should assume an evaluated Population, and must only perform parent selection and variation (producing children). The step method should modify the search algorithms population in-place, or assign a new Population to the population attribute. """ pass def _full_step(self) -> bool: self.generation += 1 if self._p_context is not None: self.population.p_evaluate(self._p_context.ns.evaluator, self._p_context.pool) else: self.population.evaluate(self.config.evaluator) best_this_gen = self.population.best() if self.best_seen is None or best_this_gen.total_error < self.best_seen.total_error: self.best_seen = best_this_gen if self.best_seen.total_error <= self.config.error_threshold: return False self.step() return True def is_solved(self) -> bool: """Return ``True`` if the search algorithm has found a solution or ``False`` otherwise.""" return self.best_seen.total_error <= self.config.error_threshold @tap def run(self) -> Individual: """Run the algorithm until termination.""" while self._full_step(): if self.generation >= self.config.max_generations: break # Simplify the best individual for a better generalization and interpretation. simplifier = GenomeSimplifier(self.config.evaluator, self.config.signature) simp_genome, simp_error_vector = simplifier.simplify( self.best_seen.genome, self.best_seen.error_vector, self.config.simplification_steps) simplified_best = Individual(simp_genome, self.config.signature) simplified_best.error_vector = simp_error_vector self.best_seen = simplified_best return self.best_seen
def test_empty_population_len(self): assert len(Population()) == 0
class SearchAlgorithm(ABC): """Base class for all search algorithms. Parameters ---------- config : SearchConfiguration The configuation of the search algorithm. Attributes ---------- config : SearchConfiguration The configuation of the search algorithm. generation : int The current generation, or iteration, of the search. best_seen : Individual The best Individual, with respect to total error, seen so far. population : Population The current Population of individuals. """ def __init__(self, config: SearchConfiguration): self.config = config self.generation = 0 self.best_seen = None self.init_population() def init_population(self): """Initialize the population.""" self.population = Population() for i in range(self.config.population_size): spawner = self.config.spawner genome = spawner.spawn_genome(self.config.initial_genome_size) self.population.add(Individual(genome)) @abstractmethod def step(self) -> bool: """Perform one generation (step) of the search. Return if should continue. The step method should assume an evaluated Population, and must only perform parent selection and variation (producing children). The step method should modify the search algorithms population in-place, or assign a new Population to the population attribute. """ pass def _full_step(self) -> bool: self.generation += 1 self.population.evaluate(self.config.evaluator) best_this_gen = self.population.best() if self.best_seen is None or best_this_gen.total_error < self.best_seen.total_error: self.best_seen = best_this_gen if self.best_seen.total_error <= self.config.error_threshold: return False if self.config.verbosity_config.generation and \ self.generation % self.config.verbosity_config.every_n_generations == 0: stat_logs = [] stat_logs.append("GENERATION: {g}".format(g=self.generation)) stat_logs.append( "ERRORS: best={b}, median={m}, diversity={e_d}".format( b=self.population.best().total_error, m=self.population.median_error(), e_d=self.population.error_diversity())) # stat_logs.append("Population: size={p_s}, genome_diversity={g_d}".format( # p_s=len(self.population), # g_d=self.population.genome_diversity() # )) self.config.verbosity_config.generation(" | ".join(stat_logs)) self.step() return True def _is_solved(self): return self.best_seen.total_error <= self.config.error_threshold def run(self): """Run the algorithm until termination.""" while self._full_step(): if self.generation >= self.config.max_generations: break verbose_solution = self.config.verbosity_config.solution_found if verbose_solution: if self._is_solved(): verbose_solution("Unsimplified solution found.") else: verbose_solution("No unsimplified solution found.") # Simplify the best individual for a better generalization and interpreteation. simplifier = GenomeSimplifier(self.config.evaluator, self.config.verbosity_config) simp_genome, simp_error_vector = simplifier.simplify( self.best_seen.genome, self.best_seen.error_vector, self.config.simplification_steps) simplified_best = Individual(simp_genome) simplified_best.error_vector = simp_error_vector if verbose_solution: if self._is_solved(): verbose_solution("Simplified solution found.") else: verbose_solution("No simplified solution found.") return simplified_best
def partially_evaluated_pop(simple_individuals): simple_individuals[0].error_vector = np.array([1, 2, 3]) simple_individuals[2].error_vector = np.array([0, 0, 0]) return Population(simple_individuals)
def evaluated_pop(simple_individuals): for i in simple_individuals: i.error_vector = np.arange(3) return Population(simple_individuals)
def unevaluated_pop(simple_individuals): return Population(simple_individuals)
def test_genome_diversity_empty_pop(self): with pytest.raises(ZeroDivisionError): Population().genome_diversity()
def test_median_error_empty_pop(self): assert np.isnan(Population().median_error()).all()
class SearchAlgorithm(ABC): """Base class for all search algorithms. Parameters ---------- config : SearchConfiguration The configuation of the search algorithm. Attributes ---------- config : SearchConfiguration The configuation of the search algorithm. generation : int The current generation, or iteration, of the search. best_seen : Individual The best Individual, with respect to total error, seen so far. population : Population The current Population of individuals. """ def __init__(self, config: SearchConfiguration): self.config = config self.generation = 0 self.best_seen = None self.init_population() def init_population(self): """Initialize the population.""" self.population = Population() for i in range(self.config.population_size): spawner = self.config.spawner genome = spawner.spawn_genome(self.config.initial_genome_size) self.population.add(Individual(genome)) @abstractmethod def step(self) -> bool: """Perform one generation (step) of the search. Return if should continue. The step method should assume an evaluated Population, and must only perform parent selection and variation (producing children). The step method should modify the search algorithms population in-place, or assign a new Population to the population attribute. """ pass def _full_step(self) -> bool: self.generation += 1 self.population.evaluate(self.config.evaluator) best_this_gen = self.population.best() if self.best_seen is None or best_this_gen.total_error < self.best_seen.total_error: self.best_seen = best_this_gen if self.best_seen.total_error <= self.config.error_threshold: return False if self.config.verbosity_config.generation >= self.config.verbosity_config.log_level and \ self.generation % self.config.verbosity_config.every_n_generations == 0: stat_logs = [] stat_logs.append("GENERATION: {g}".format( g=self.generation )) stat_logs.append("ERRORS: best={b}, median={m}, diversity={e_d}".format( b=self.population.best().total_error, m=self.population.median_error(), e_d=self.population.error_diversity() )) # stat_logs.append("Population: size={p_s}, genome_diversity={g_d}".format( # p_s=len(self.population), # g_d=self.population.genome_diversity() # )) log(self.config.verbosity_config.generation, " | ".join(stat_logs)) self.step() return True def _is_solved(self): return self.best_seen.total_error <= self.config.error_threshold def run(self): """Run the algorithm until termination.""" while self._full_step(): if self.generation >= self.config.max_generations: break verbose_solution = self.config.verbosity_config.solution_found if verbose_solution >= self.config.verbosity_config.log_level: if self._is_solved(): log(verbose_solution, "Unsimplified solution found.") else: log(verbose_solution, "No unsimplified solution found.") # Simplify the best individual for a better generalization and interpreteation. simplifier = GenomeSimplifier(self.config.evaluator, self.config.verbosity_config) simp_genome, simp_error_vector = simplifier.simplify( self.best_seen.genome, self.best_seen.error_vector, self.config.simplification_steps ) simplified_best = Individual(simp_genome) simplified_best.error_vector = simp_error_vector if verbose_solution >= self.config.verbosity_config.log_level: if self._is_solved(): log(verbose_solution, "Simplified solution found.") else: log(verbose_solution, "No simplified solution found.") return simplified_best
def test_error_diversity_empty_pop(self): with pytest.raises(ValueError): Population().error_diversity()