PK:H. Tpynetics/algorithms.pyfrom collections import defaultdict from pynetics import Population from pynetics.catastrophe import Catastrophe, NoCatastrophe from pynetics.mutation import NoMutation from pynetics.stop import StopCondition from pynetics.utils import check_is_instance_of class GeneticAlgorithm: """ Base class where the evolutionary algorithm works. More than one algorithm may exist so a base class is created for specify the contract required by the other classes to work properly. """ MSG_PRE_INITIALIZE = 'PRE_INITIALIZE' MSG_POST_INITIALIZE = 'POST_INITIALIZE' MSG_ALGORITHM_STARTED = 'ALGORITHM_STARTED' MSG_ALGORITHM_FINISHED = 'ALGORITHM_FINISHED' MSG_STEP_STARTED = 'STEP_STARTED' MSG_STEP_FINISHED = 'STEP_FINISHED' def __init__( self, stop_condition, populations, catastrophe, ): """ Initializes the genetic algorithm with the defaults. The populations_desc param should follow certain rules in order to work in the way the genetic algorithm is intended: 1. The population size of each of the populations must be greater than or equal to 1. If not, there will be nothing to be evolved. 2. The replacement rate should be at least 1 (otherwise no individual will be replaced) and, at most, the population size (i.e. a total replacement also called generational scheme). 3. The spawning pool must be an instance of SpawningPool class (or any of their subclasses). 4. The fitness method must be an instance of FitnessMethod class (or any of its subclasses). :param stop_condition: The condition to be met in order to stop the genetic algorithm. :param populations: The populations to be evolved. :param catastrophe: The method to be used as catastrophe operation. :raises UnexpectedClassError: If any of the input variables doesn't follow the contract required (i.e. doesn't inherit from a predefined class). """ self.__stop_condition = check_is_instance_of( stop_condition, StopCondition ) self.populations = populations self.listeners = defaultdict(list) self.catastrophe = check_is_instance_of(catastrophe, Catastrophe) self.generation = 0 for population in self.populations: population.genetic_algorithm = self def run(self): """ Runs the simulation. The process is as follows: initialize populations and, while the stop condition is not met, do a new evolve step. This process relies in the abstract method "step". """ [f(self) for f in self.listeners[self.MSG_PRE_INITIALIZE]] self.__initialize() [f(self) for f in self.listeners[self.MSG_POST_INITIALIZE]] [f(self) for f in self.listeners[self.MSG_ALGORITHM_STARTED]] while not self.__stop_condition(self): [f(self) for f in self.listeners[self.MSG_STEP_STARTED]] for population in self.populations: population.evolve() self.catastrophe(population) self.generation += 1 [f(self) for f in self.listeners[self.MSG_STEP_FINISHED]] [f(self) for f in self.listeners[self.MSG_ALGORITHM_FINISHED]] def __initialize(self): """ Called when starting the genetic algorithm to initialize it. """ self.generation = 0 def best(self): """ Returns the best individuals obtained until now. They will be returned as a dictionary where the keys are the population names and the values the best individuals for those populations. :return: A dictionary with the best individual of each population. """ return {p.name: p[0] for p in self.populations} class SimpleGA(GeneticAlgorithm): """ Simple implementation of a GeneticAlgorithm This subclass abstracts all the behavior of the evolution over several populations. """ def __init__( self, stop_condition, size, replacement_rate, spawning_pool, fitness, selection, replacement, recombination, p_recombination=1.0, mutation=NoMutation(), p_mutation=0.0, ): """ Initializes this instance. :param stop_condition: The condition to be met in order to stop the genetic algorithm. :param size: The size this population should have. :param replacement_rate: The rate of individuals to be replaced in each step of the algorithm. Must be a float value in the (0, 1] interval. :param spawning_pool: The object that generates individuals. :param fitness: The method to evaluate individuals. :param selection: The method to select individuals of the population to recombine. :param replacement: The method that will add and remove individuals from the population given the set of old individuals (i.e. the ones on the population before the evolution step) and new individuals (i.e. the offspring). :param recombination: The method to recombine parents in order to generate an offspring with characteristics of the parents. If none, no recombination will be applied. :param p_recombination: The odds for recombination method to be performed over a set of selected individuals to generate progeny. If not performed, progeny will be the parents. Must be a value between 0 and 1 (both included). If not provided, defaults to 1.0. :param mutation: The method to mutate an individual. If none, no mutation over the individual will be applied. If not provided, no mutation is performed. :param p_mutation: The odds for mutation method to be performed over a progeny. It's applied once for each individual. If not performed the individuals will not be modified. Must be a value between 0 and 1 (both included). If not provided, it defaults to 0.0 (no mutation is performed). :raises WrongValueForIntervalError: If any of the bounded values fall out of their respective intervals. :raises NotAProbabilityError: If a value was expected to be a probability and it wasn't. :raises UnexpectedClassError: If any of the input variables doesn't follow the contract required (i.e. doesn't inherit from a predefined class). """ self.__population_name = 'SimpleGA' super().__init__( stop_condition=stop_condition, populations=[ Population( name=self.__population_name, size=size, replacement_rate=replacement_rate, spawning_pool=spawning_pool, fitness=fitness, selection=selection, recombination=recombination, mutation=mutation, replacement=replacement, p_recombination=p_recombination, p_mutation=p_mutation, ) ], catastrophe=NoCatastrophe(), ) def best(self): """ Returns the best individual obtained until now. """ return super().best()[self.__population_name] PK:Hi@$ڽ pynetics/replacements.pyimport abc class Replacement(metaclass=abc.ABCMeta): """ Replacement of individuals of the population. """ def __init__(self, maintain_population_size=True): """ Initializes this replacement method. :param maintain_population_size: If the population, once applied the replacement method, must have the same number of individuals. It defaults to True. """ self.maintain_population_size = maintain_population_size def __call__(self, population, offspring): """ Performs some checks before applying the replacement method. :param population: The population where make the replacement. :param offspring: The new population to use as replacement. :raises ValueError: If the number of individuals in population is lower than the number of individuals in the offspring. """ pre_length = len(population) self.perform(population, offspring) if self.maintain_population_size and pre_length != len(population): raise ValueError('Population length not maintained after replacing') @abc.abstractmethod def perform(self, population, offspring): """ It makes the replacement according to the subclass implementation. It is recommended for perform method to return the same :param population: The population where make the replacement. :param offspring: The new population to use as replacement. """ class LowElitism(Replacement): """ Low elitism replacement. The method will replace the less fit individuals by the ones specified in the offspring. This makes this operator elitist, but at least not much. Moreover, if offspring size equals to the population size then it's a full replacement (i.e. a generational scheme). """ def perform(self, population, offspring): """ Removes less fit individuals and then inserts the offspring. :param population: The population where make the replacement. :param offspring: The new population to use as replacement. """ del population[-len(offspring):] population.extend(offspring) class HighElitism(Replacement): """ Drops the less fit individuals among all (population plus offspring). The method will add all the individuals in the offspring to the population, removing afterwards those individuals less fit. This makes this operator highly elitist but if length os population and offspring are the same, the process will result in a full replacement, i.e. a generational scheme of replacement. """ def perform(self, population, offspring): """ Inserts the offspring in the population and removes the less fit. :param population: The population where make the replacement. :param offspring: The new population to use as replacement. """ population.extend(offspring) del population[-len(offspring):] PK:H~pynetics/selections.pyimport abc import operator import random class Selection(metaclass=abc.ABCMeta): """ Selection of the fittest individuals among the population. The selection method is defined as a class. However, it is enough to provide as a selection method a function that receives a village and a number of individuals, and returns a sample of individuals of that size from the given population. """ def __init__(self, rep=False): """ Initializes this selector. :param rep: If repetition of individuals is allowed. If true, there are chances of the same individual be selected again. Defaults to False. """ self.__rep = rep def __call__(self, population, n): """ Makes some checks to the configuration before delegating selection. After checking the parameters, the selection is performed by perform method. :param population: The population from which select the individuals. :param n: The number of individuals to return. :return: A list of individuals. :raises ValueError: If length of the population is smaller than the number of individuals to select and the repetition parameter is set to False (i.e. the same Individual cannot be selected twice or more times). """ if not self.rep and len(population) < n: raise ValueError() else: return self.perform(population, n) @abc.abstractmethod def perform(self, population, n): """ It makes the selection according to the subclass implementation. :param population: The population from which select the individuals. :param n: The number of individuals to return. :return: A list of n individuals. """ @property def rep(self): """ Returns if the same individual can be selected repeatedly. """ return self.__rep class BestIndividual(Selection): """ Selects the best individuals among the population. """ def perform(self, population, n): """ Gets the top n individuals out of all the population. If "rep" is activated, the returned individuals will be n times the best individual. If False, the returned individuals will be the top n individuals. :param population: The population from which select the individuals. :param n: The number of individuals to return. :return: A list of n individuals. """ return [population[0] for _ in range(n)] if self.rep else population[:n] class ProportionalToPosition(Selection): """ Selects individuals randomly proportionally to their position. """ def perform(self, population, n): """ Gets randomly the individuals, giving more probability to those in first positions of the population, i.e. those fittest. The probability to be selected is proportional to the position of the fitness of the individual among the population (i.e. those with better fitness have better positions, but a very high fitness doesn't implies more chances to be selected). If "rep" is activated, the returned individuals may be repeated. :param n: The number of individuals to return. :param population: The population from which select the individuals. :return: A list of n individuals. """ # TODO Implement raise NotImplementedError() class Tournament(Selection): """ Selects best individuals of a random sample of the whole population. """ def __init__(self, m, rep=False): """ Initializes this selector. :param m: The size of the random sample of individuals to pick prior to the selection of the fittest. :param rep: If repetition of individuals is allowed. If true, there are chances of the same individual be selected again. Defaults to False. """ super().__init__(rep) self.__m = m def perform(self, population, n): """ Gets the best individuals from a random sample of the population. To do it, a sample of individuals will be selected randomly and, after that, the best individual of the sample is then selected. This process (i.e. extract sample and the get best individual from sample) is done as many times as individuals to be selected. If "rep" is activated, the returned individuals may be repeated. :param n: The number of individuals to return. :param population: The population from which select the individuals. :return: A list of n individuals. """ individuals = [] while len(individuals) < n: sample = random.sample(population, self.__m) individual = max(sample, key=lambda i: population.fitness(i)) if not self.rep or individual not in individuals: individuals.append(individual) return individuals class Uniform(Selection): """ Selects individuals randomly from the population. """ def perform(self, population, n): """ Selects n individuals randomly from the population. The selection is done by following a uniform distribution along the entire population. :param population: The population from which select the individuals. :param n: The number of individuals to return. :return: A list of n individuals. """ if self.rep: return [random.choice(population) for _ in range(n)] random.sample(population, n) PK:HI6eP6P6pynetics/__init__.pyimport inspect import math import abc import random from pynetics.catastrophe import Catastrophe from pynetics.exceptions import WrongValueForInterval, NotAProbabilityError from pynetics.individuals import SpawningPool, Individual from pynetics.mutation import Mutation, NoMutation from pynetics.recombination import Recombination, NoRecombination from pynetics.replacements import Replacement from pynetics.selections import Selection from pynetics.stop import StopCondition from pynetics.utils import check_is_instance_of, take_chances __version__ = '0.1.2' class Population(list): """ Manages a population of individuals. A population is where individuals of the same kind evolve over an environment. A basic genetic algorithm consists in a single population, but more complex schemes involve two or more populations evolving concurrently. """ def __init__( self, name=None, size=None, replacement_rate=None, spawning_pool=None, fitness=None, selection=None, recombination=None, p_recombination=None, mutation=None, p_mutation=None, replacement=None, individuals=None, ): """ Initializes the population, filling it with individuals. When the population is initialized, the fitness of the individuals generated is also calculated, implying that init_perform of every individual is called. Because operators requires to know which individual is the fittest, others which is the less fit and others need to travel along the collection of individuals in some way or another (e.g. from fittest to less fit), the population is always sorted when an access is required. Thus, writing population[0] always returns the fittest individual, population[1] the next and so on, until population[-1] which is the less fit. :param name: The name of this population. :param size: The size this population should have. :param replacement_rate: The rate of individuals to be replaced in each step of the algorithm. Must be a float value in the (0, 1] interval. :param spawning_pool: The object that generates individuals. :param fitness: The method to evaluate individuals. :param selection: The method to select individuals of the population to recombine. :param recombination: The method to recombine parents in order to generate an offspring with characteristics of the parents. If none, no recombination will be applied. :param p_recombination: The odds for recombination method to be performed over a set of selected individuals to generate progeny. If not performed, progeny will be the parents. Must be a value between 0 and 1 (both included). :param mutation: The method to mutate an individual. If none, no mutation over the individual will be applied. :param p_mutation: The odds for mutation method to be performed over a progeny. It's applied once for each individual. If not performed the individuals will not be modified. Must be a value between 0 and 1 (both included). :param replacement: The method that will add and remove individuals from the population given the set of old individuals (i.e. the ones on the population before the evolution step) and new individuals (i.e. the offspring). :param individuals: The list of starting individuals. If none or if its length is lower than the population size, the rest of individuals will be generated randomly. If the length of initial individuals is greater than the population size, a random sample of the individuals is selected as members of population. :raises ValueError: If no name for this population is provided. :raises WrongValueForIntervalError: If any of the bounded values fall out of their respective intervals. :raises NotAProbabilityError: If a value was expected to be a probability and it wasn't. :raises UnexpectedClassError: If any of the instances provided wasn't of the required class. """ super().__init__() if not name: raise ValueError('A name for population is required') if size is None or size < 1: raise WrongValueForInterval('size', 0, '∞', size, inc_lower=False) if replacement_rate is None or not 0 < replacement_rate <= 1: raise WrongValueForInterval( 'replacement_rate', 0, 1, replacement_rate, inc_lower=False ) if p_recombination is None or not 0 <= p_recombination <= 1: raise NotAProbabilityError('p_recombination', p_recombination) if p_mutation is None or not 0 <= p_mutation <= 1: raise NotAProbabilityError('p_mutation', p_mutation) self.name = name self.size = size self.replacement_rate = replacement_rate self.spawning_pool = check_is_instance_of(spawning_pool, SpawningPool) self.fitness = check_is_instance_of(fitness, Fitness) self.selection = check_is_instance_of(selection, Selection) self.recombination = check_is_instance_of(recombination, Recombination) self.p_recombination = p_recombination self.mutation = check_is_instance_of(mutation, Mutation) self.p_mutation = p_mutation self.replacement = check_is_instance_of(replacement, Replacement) self.sorted = False self.genetic_algorithm = None # Precomputed values to speed up the things a bit self.offspring_size = int(math.ceil(size * replacement_rate)) self.selection_size = len( inspect.signature(recombination.perform).parameters ) # Population is initialized with the individuals, and they are sorted by # their initial fitness computation (method init_perform) individuals = individuals or [] self.extend(random.sample( individuals, min(self.size, len(individuals))) ) [self.append(self.spawn()) for _ in range(len(individuals), self.size)] self.sort(key=lambda i: self.fitness(i, init=True)) def spawn(self): """ Spawns a new individual. This individual will have a reference to the population which created it, but the population itself will not have the individual included in it until "append" method is called. :return: An individual of the class of the individuals created by the spawning pool defined in the initialization. """ individual = self.spawning_pool.create() individual.population = self return individual def sort(self, *args, **kwargs): """ Sorts the list of individuals by its fitness. The key may be overridden, but is not recommended. It's overridden at initialization time when performing the initial ordering, using init_perform instead perform. :param args: Positional parameters (inherited from list class). :param kwargs: Named parameters (inherited from list class). """ if not self.sorted: super().sort( key=kwargs.get('key', self.fitness), reverse=True ) self.sorted = True def __getitem__(self, index): """ Returns the individual located on this position. Treat this call as if population were sorted by fitness, from the fittest to the less fit. :param index: The index of the individual to recover. :return: The individual. """ self.sort() return super().__getitem__(index) def __setitem__(self, index, individual): """ Puts the named individual in the specified position. This call will cause a new sorting of the individuals the next time an access is required. This means that is preferable to make all the inserts in the population at once instead doing interleaved readings and inserts. :param index: The position where to insert the individual. :param individual: The individual to be inserted. """ self.sorted = False self.__setitem__(index, individual) individual.population = self def extend(self, individuals): """ Extends the population with a collection of individuals. This call will cause a new sorting of the individuals the next time an access is required. This means that is preferable to make all the inserts in the population at once instead doing interleaved readings and inserts. :param individuals: A collection of individuals to be inserted into the population. """ self.sorted = False for individual in individuals: individual.population = self super().extend(individuals) def append(self, individual): """ Ads a new element to the end of the list of the population. This call will cause a new sorting of the individuals the next time an access is required. This means that is preferable to make all the inserts in the population at once instead doing interleaved readings and inserts. :param individual: The individual to be inserted in the population """ self.sorted = False individual.population = self super().append(individual) def evolve(self): """ A step of evolution is made on this population. That means that a full cycle of select-recombine-mutate-replace is performed, potentially modifying the individuals this population contains. """ # First, we generate the offspring given population replacement rate. offspring = [] while len(offspring) < self.offspring_size: # Selection parents = self.selection(self, self.selection_size) # Recombination if take_chances(self.p_recombination): progeny = self.recombination(*parents) else: progeny = parents individuals_who_fit = min( len(progeny), self.offspring_size - len(offspring) ) progeny = random.sample(progeny, individuals_who_fit) # Mutation for individual in progeny: if take_chances(self.p_mutation): self.mutation(individual) # Add progeny to the offspring offspring.extend(progeny) # Once offspring is generated, a replace step is performed self.replacement(self, offspring) class Fitness(metaclass=abc.ABCMeta): """ Method to estimate how adapted is the individual to the environment. """ def __call__(self, individual, init=False): """ Calculates the fitness of the individual. This method does some checks and the delegates the computation of the fitness to the "perform" method. :param individual: The individual to which estimate the adaptation. :param init: If this call to fitness is at initialization time. It defaults to False. :return: A sortable object representing the adaptation of the individual to the environment. """ if individual is None: raise ValueError('The individual cannot be None') elif init: return self.init_perform(individual) else: return self.perform(individual) def init_perform(self, individual): """ Estimates how adapted is the individual at initialization time. This is useful in schemas where the fitness while initializing is computed in a different way than along the generations. Overriding this method can be tricky, specially in a co-evolutionary scheme. In this stage of the algorithm (initialization) the populations are not sorted, and it's position on its population cannot depend on the best of other individuals of other populations (circular dependency). Therefore, calling other_population[0] is not an option here. The scheme proposed by Mitchell A. et. al. in "A Cooperative Coevolutionary Approach to Function Optimization", the initialization may be performed by selecting a random individual among the other populations instead the best. For this purpose, a random() method in Population class is provided. The default behavior is to call method "perform" but can be overridden to any other behavior if needed. :param individual: The individual to which estimate the adaptation. :return: A sortable object representing the adaptation of the individual to the environment. """ return self.perform(individual) @abc.abstractmethod def perform(self, individual): """ Estimates how adapted is the individual. Must return something comparable (in order to be sorted with the results of the methods for other fitnesses). It's supposed that, the highest the fitness value is, the fittest the individual is in the environment. :param individual: The individual to which estimate the adaptation. :return: A sortable object representing the adaptation of the individual to the environment. """ PK:HPY  pynetics/stop.pyimport abc class StopCondition(metaclass=abc.ABCMeta): """ A condition to be met in order to stop the algorithm. Although the stop condition is defined as a class, it's enough to provide a function that is able to discern whether the time has come to stop (True or False) receiving as parameter the population. """ @abc.abstractmethod def __call__(self, genetic_algorithm): """ Checks if this stop condition is met. :param genetic_algorithm: The genetic algorithm where this stop condition belongs. :return: True if criteria is met, false otherwise. """ class StepsNumStopCondition(StopCondition): """ If the genetic algorithm has made enough iterations. """ def __init__(self, steps): """ Initializes this function with the number of iterations. :param steps: An integer value. """ self.steps = steps def __call__(self, genetic_algorithm): """ Checks if this stop criteria is met. It will look at the generation of the genetic algorithm. It's expected that. If its generation is greater or equal to the specified in initialization method, the criteria is met. :param genetic_algorithm: The genetic algorithm where this stop condition belongs. :return: True if criteria is met, false otherwise. """ return genetic_algorithm.generation >= self.steps class FitnessBound(StopCondition): """ If the genetic algorithm obtained a fine enough individual. """ def __init__(self, fitness_bound, all_populations=False): """ Initializes this function with the upper bound for the fitness. :param fitness_bound: An fitness value. :param all_populations: If True, the condition will be met only when all the populations contain at least one individual with a fitness higher than the bound. If False, only one individual among all the populations will suffice. """ self.fitness_bound = fitness_bound self.all_populations = all_populations def __call__(self, genetic_algorithm): """ Checks if this stop criteria is met. It will look at the generation of the genetic algorithm. It's expected that. If its generation is greater or equal to the specified in initialization method, the criteria is met. :param genetic_algorithm: The genetic algorithm where this stop condition belongs. :return: True if criteria is met, false otherwise. """ fitnesses = [p[0].fitness() for p in genetic_algorithm.populations] criteria = [fitness > self.fitness_bound for fitness in fitnesses] return all(criteria) if self.all_populations else any(criteria) PK:Hp pynetics/recombination.pyimport abc from .individuals import Individual class Recombination(metaclass=abc.ABCMeta): """ Defines the behaviour of a recombination operator. A recombination operator takes a set of individuals (i.e. parents) and generates a different set of individuals (i.e. offspring) normally with aspects derived from their parents. """ def __call__(self, *args): """ Applies the recombine method to a sequence of individuals. :param args: A list of one or more Individual instances to use as parents in the recombination. :returns: A sequence of individuals with characteristics of the parents. """ if not args: msg = 'At least one individual is required for recombination' raise ValueError(msg) elif not all([isinstance(i, Individual) for i in args]): msg = 'All parameters should be Individual instances' raise ValueError(msg) else: return self.perform(*args) @abc.abstractmethod def perform(self, *args): """ Implementation of the recombine method. The method will always receive a list of Individual instances, and the implementation must be aware of the individual types because given that not all implementations are the same, not all the crossover operations may work. :param args: A list of one or more Individual instances to use as parents in the recombination. :returns: A sequence of individuals with characteristics of the parents. """ class NoRecombination(Recombination): """ A crossover method where no method is applied to the individuals. """ def perform(self, *args): """ Return the same individuals passed as parameter. """ return args PK:HЖ pynetics/catastrophe.pyimport abc # TODO I'm not proud of this methods. I think they performance may be improved. from .utils import take_chances class Catastrophe(metaclass=abc.ABCMeta): """ Defines the behaviour of a genetic algorithm catastrophe operator. It's expected for this operator to keep track of the ga and know when to act since it will be called every step of the algorithm after replacement operation. """ def __call__(self, population): """ Tries to apply the catastrophic operator to the population. This method does some checks and the delegates the application of the catastrophic operator to the "perform" method. :param population: The population where apply the catastrophic method. """ if population is None: raise ValueError('The population cannot be None') else: return self.perform(population) @abc.abstractmethod def perform(self, population): """ Implementation of the catastrophe operation. :param population: the population which may suffer the catastrophe """ class NoCatastrophe(Catastrophe): """ A catastrophe method where nothing happens. """ def perform(self, population): """ It's a wonderful world and nothing happens. :param population: The poppulation where nothing happens. Ever. """ pass class ProbabilityBasedCatastrophe(Catastrophe, metaclass=abc.ABCMeta): """ Base class for some bundled probability based catastrophe methods. This method will have a probability to be triggered. Is expected this probability to be very little. """ def __init__(self, probability): """ Initializes this catastrophe method. :param probability: The probability fot the catastrophe to happen. """ self.__probability = probability def perform(self, population): if take_chances(self.__probability): self.perform_catastrophe() @abc.abstractmethod def perform_catastrophe(self, population): """ Returns a list of the individuals to remove from population. :param population: The population from where extract individuals. :return: The individuals to retain after the catastrophe application. """ class PackingByProbability(ProbabilityBasedCatastrophe): """ Replaces all repeated individuals maintaining only one copy of each. """ def perform_catastrophe(self, population): """ Replaces all repeated individuals by new ones. :param population: The population where apply the catastrophe. """ visited_individuals = [] for i in range(len(population)): if population[i] in visited_individuals: population[i] = population.spawn() visited_individuals.append(population[i]) class DoomsdayByProbability(ProbabilityBasedCatastrophe): """ Replaces all but the best individual. """ def perform_catastrophe(self, population): """ Replaces all the individuals but the best. :param population: The population where apply the catastrophe. """ for i in range(1, len(population)): population[i] = population.spawning_pool.create() PK:H@ppynetics/exceptions.pyclass GeneticAlgorithmError(Exception): # TODO TBD pass class UnexpectedClassError(GeneticAlgorithmError): """ Raised when an instance is not of the expected class. """ def __init__(self, expected_class): """ Initializes the exception. :param expected_class: The expected class for that instance. """ super().__init__('Expected class {}'.format(expected_class)) class WrongValueForInterval(ValueError): """ When a value does not belong to an interval. """ def __init__( self, var_name, lower, upper, value, inc_lower=True, inc_upper=True, ): """ Initializes the exception. :param var_name: The variable name which contains the wrong value. :param lower: The lower bound of the interval. :param upper: The upper bound of the interval. :param value: The value. :param inc_lower: If the lower bound is include. Defaults to True. :param inc_upper: If the upper bound is include. Defaults to True. """ self.lower = lower self.upper = upper self.var_name = var_name self.value = value self.inc_lower = inc_lower self.inc_upper = inc_upper msg = 'Expected {} ∈ {}{}, {}{} but got {}'.format( var_name, '[' if inc_lower else '(', self.lower, self.upper, ']' if inc_upper else ')', self.value, ) super().__init__(msg) class NotAProbabilityError(WrongValueForInterval): """ If a value is not a valid probability. """ def __init__(self, var_name, value): """ Initializes the instance. :param var_name: The variable name which contains the wrong value. :param value: The value. """ super().__init__(var_name, 0, 1, value, inc_lower=True, inc_upper=True) PK:H wwpynetics/mutation.pyimport abc from pynetics.utils import check_is_instance_of from pynetics.individuals import Individual class Mutation(metaclass=abc.ABCMeta): """ Defines the behaviour of a genetic algorithm mutation operator. """ def __call__(self, individual): """ Applies the crossover method to the list of individuals. :param individual: an individual to mutate. :returns: A new mutated individual. :raises UnexpectedClassError: If the individual is not an Individual instance. """ individual = check_is_instance_of(individual, Individual) return self.perform(individual) @abc.abstractmethod def perform(self, individual): """ Implementation of the mutation operation. The mutation implementation must be aware of the implementation type. Given that not all the implementations are the same, not all the mutation operations may work. :param individual: The individual to mutate. :returns: A new mutated individual. """ class NoMutation(Mutation): """ A method where no modification is performed to the individual. """ def perform(self, individual): """ Return the same individual passed as parameter. :param individual: The individual to mutate. :returns: The same, unmodified individual. """ return individual PK:H !!pynetics/individuals.pyimport abc class Individual: """ One of the possible solutions to a problem. In a genetic algorithm, an individual is a tentative solution of a problem, i.e. the environment where populations of individuals evolve. """ def __init__(self): """ Initializes the individual. """ self.population = None def fitness(self): """ Cumputes the fitness of this individual. It will use the fitness method defined on its population. :return: A fitness. """ return self.population.fitness(self) @abc.abstractmethod def phenotype(self): """ The expression of this particular individual in the environment. :return: An object representing this individual in the environment """ class SpawningPool(metaclass=abc.ABCMeta): """ Defines the methods for creating individuals required by population. """ @abc.abstractmethod def create(self): """ Creates a new individual randomly. :return: A new Individual object. """ PK:HBpynetics/utils.pyimport random from .exceptions import UnexpectedClassError def take_chances(probability=0.5): """ Given a probability, the method generates a random value to see if is lower or not than that probability. :param probability: The value of the probability to beat. Default is 0.5. :return: A value of True if the value geneated is bellow the probability specified, and false otherwise. """ return random.random() < probability # Validations def check_is_instance_of(value, cls): """ Checks if a value is instance of a given class. If the value is an instance of the class, he method will return the value as is. Otherwise, it will raise an error. :param value: The value to be checked. :param cls: The class to be checked on. :return: The value. :raises UnexpectedClassError: In case of the value is not an instance of the given class. """ if not isinstance(value, cls): raise UnexpectedClassError(cls) else: return value PK$:HC"AA(pynetics-0.1.2.dist-info/DESCRIPTION.rst========== Pyvolution ========== *********************************************** An evolutionary computation library for Python. *********************************************** Pyvolution is a library for experimenting with evolutionary computation. ************ Installation ************ Installing from pip:: pip install pynetics Installing from source:: pip install git+https://github.com/blazaid/pynetics Requirements ============ No requirements for now. ************* Documentation ************* WIP ******* Authors ******* `pynetics` was written by `Blazaid `_. Thanks ====== To you, for using the library, for helping me to make it faster and better, for learn with it and for releasing your code to make the knowledge and the science open for the rest of humanity. Warning ======= It has been developed with only python 3.X in mind, so it won't probably work on lower versions (i.e. no python 2.X support). Second warning ============== English included in both this document and the code can be devastating for the brain of an average human being. Even so we, the poor developers, are working hard to write as correctly as possible and learn along the way. The documentation will be updated as we improve our language proficency as well as we receive critical / suggestions for this. PK$:HX]&pynetics-0.1.2.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3.5", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Scientific/Engineering :: Artificial Intelligence"], "extensions": {"python.details": {"contacts": [{"email": "alberto.da@gmail.com", "name": "blazaid", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/blazaid/pynetics/"}}}, "generator": "bdist_wheel (0.26.0)", "license": "GNU General Public License v3", "metadata_version": "2.0", "name": "pynetics", "platform": "any", "summary": "An evolutionary computation library for Python", "test_requires": [{"requires": []}], "version": "0.1.2"}PK#:HE/ &pynetics-0.1.2.dist-info/top_level.txtpynetics PK$:H}\\pynetics-0.1.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PK$:Ht  !pynetics-0.1.2.dist-info/METADATAMetadata-Version: 2.0 Name: pynetics Version: 0.1.2 Summary: An evolutionary computation library for Python Home-page: http://github.com/blazaid/pynetics/ Author: blazaid Author-email: alberto.da@gmail.com License: GNU General Public License v3 Platform: any Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence ========== Pyvolution ========== *********************************************** An evolutionary computation library for Python. *********************************************** Pyvolution is a library for experimenting with evolutionary computation. ************ Installation ************ Installing from pip:: pip install pynetics Installing from source:: pip install git+https://github.com/blazaid/pynetics Requirements ============ No requirements for now. ************* Documentation ************* WIP ******* Authors ******* `pynetics` was written by `Blazaid `_. Thanks ====== To you, for using the library, for helping me to make it faster and better, for learn with it and for releasing your code to make the knowledge and the science open for the rest of humanity. Warning ======= It has been developed with only python 3.X in mind, so it won't probably work on lower versions (i.e. no python 2.X support). Second warning ============== English included in both this document and the code can be devastating for the brain of an average human being. Even so we, the poor developers, are working hard to write as correctly as possible and learn along the way. The documentation will be updated as we improve our language proficency as well as we receive critical / suggestions for this. PK$:H[[pynetics-0.1.2.dist-info/RECORDpynetics/__init__.py,sha256=HzsnTLIQraSh2gFo2ucs_k4dN2hB-A57TBKyNCRwV00,13904 pynetics/algorithms.py,sha256=rcaiNrhnkt3cSuiDvcM5w4ECfFGIQKoi2UAJE0TEb9Y,7600 pynetics/catastrophe.py,sha256=b5B-foEjYV74nqZBFk_BRIzdYm38CC29ADxkIi4DUM8,3273 pynetics/exceptions.py,sha256=9elHR6-GsR93CvNGNF3NhejVaKZuXdV6PETltmwQHwk,1980 pynetics/individuals.py,sha256=A595pnMes54rI2v93M3eZW2JjM7fiaqmQOqketAnt1k,1057 pynetics/mutation.py,sha256=_DE73XnOJCRdI0eeLfb1feBhnm_vkNUAv1Kyqht3N9Y,1399 pynetics/recombination.py,sha256=DSVPmvTD-Ud7PVkxtEI31gQQTkZMbK7wLUYpXQRUo_g,1821 pynetics/replacements.py,sha256=rhrouEE4z0FnyS9YCkq7cmEMrVLHALUbb2uYExItL88,3005 pynetics/selections.py,sha256=MquAjBu9ES2y1bXKOv5yDdTKSglNzV8z31QXemjOivg,5629 pynetics/stop.py,sha256=Bjec0msBXft8YscDx5oP5BbwvP5NgUbZCXTUQ8iH5o8,2820 pynetics/utils.py,sha256=xy87h1myS9uOATHecwEGjrKf5egUB55qIdRCtlrGmx4,1027 pynetics-0.1.2.dist-info/DESCRIPTION.rst,sha256=OiDtNPQyNiI4k30FST5cNxJ_1abuztcUyNKV_hUQpJc,1345 pynetics-0.1.2.dist-info/METADATA,sha256=PW7a_ps8uxy1oTYVYJYUqxj7iFEHSbv4Zqs0-xWE55k,2057 pynetics-0.1.2.dist-info/RECORD,, pynetics-0.1.2.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 pynetics-0.1.2.dist-info/metadata.json,sha256=Zj5aRVMyfPnfykimbaGAwjAJIupDNGGi9g1s1noHPRE,903 pynetics-0.1.2.dist-info/top_level.txt,sha256=ScUGo5OAPTWWt0mnvqwVNWK7GXhKyJ670Hvbn92gS6A,9 PK:H. Tpynetics/algorithms.pyPK:Hi@$ڽ pynetics/replacements.pyPK:H~)pynetics/selections.pyPK:HI6eP6P6@pynetics/__init__.pyPK:HPY  vpynetics/stop.pyPK:Hp pynetics/recombination.pyPK:HЖ pynetics/catastrophe.pyPK:H@ppynetics/exceptions.pyPK:H wwpynetics/mutation.pyPK:H !!pynetics/individuals.pyPK:HBpynetics/utils.pyPK$:HC"AA(/pynetics-0.1.2.dist-info/DESCRIPTION.rstPK$:HX]&pynetics-0.1.2.dist-info/metadata.jsonPK#:HE/ &pynetics-0.1.2.dist-info/top_level.txtPK$:H}\\εpynetics-0.1.2.dist-info/WHEELPK$:Ht  !fpynetics-0.1.2.dist-info/METADATAPK$:H[[pynetics-0.1.2.dist-info/RECORDPKF