diff --git a/pyevolve/GAllele.py b/pyevolve/GAllele.py index 87737eb..2c7a2d5 100644 --- a/pyevolve/GAllele.py +++ b/pyevolve/GAllele.py @@ -12,268 +12,274 @@ class that holds the allele types) and all the import Consts import Util + class GAlleles: - """ GAlleles Class - The set of alleles - - Example: - >>> alleles = GAlleles() - >>> choices = [1,2,3,4] - >>> lst = GAlleleList(choices) - >>> alleles.add(lst) - >>> alleles[0].getRandomAllele() in lst - True - - :param allele_list: the list of alleles - :param homogeneous: if is True, all the alleles will be use only the first added - - """ - - def __init__(self, allele_list = None, homogeneous=False): - """ The constructor of GAlleles class """ - self.allele_list = [] - if allele_list is not None: - self.allele_list.extend(allele_list) - self.homogeneous = homogeneous - - def __iadd__(self, allele): - """ To add more alleles using the += operator - - .. versionadded:: 0.6 - The __iadd__ method. - """ - self.add(allele) - return self - - def add(self, allele): - """ Appends one allele to the alleles list - - :param allele: allele to be added - - """ - self.allele_list.append(allele) - - def __getslice__(self, a, b): - """ Returns the slice part of alleles list """ - return self.allele_list[a:b] - - def __getitem__(self, index): - """ Returns the index allele of the alleles list """ - if self.homogeneous: return self.allele_list[0] - try: - val = self.allele_list[index] - except IndexError: - Util.raiseException( - """An error was occurred while finding allele for the %d position of chromosome. - You may consider use the 'homogeneous' parameter of the GAlleles class. - """ % (index,)) - return val - - def __setitem__(self, index, value): - """ Sets the index allele of the alleles list """ - if self.homogeneous: self.allele_list[0] = value - self.allele_list[index] = value - - def __iter__(self): - """ Return the list iterator """ - if self.homogeneous: - oneList = [self.allele_list[0]] - return iter(oneList) - return iter(self.allele_list) - - def __len__(self): - """ Returns the lenght of the alleles list """ - if self.homogeneous: return 1 - return len(self.allele_list) - - def __repr__(self): - """ Return a string representation of the allele """ - ret = "- GAlleles\n" - ret += "\tHomogeneous:\t %s\n" % (self.homogeneous,) - ret += "\tList size:\t %s\n" % (len(self),) - ret += "\tAlleles:\n\n" - if self.homogeneous: - ret += "Allele for 0 position:\n" - ret += self.allele_list[0].__repr__() - else: - for i in xrange(len(self)): - ret += "Allele for %d position:\n" % (i,) - ret += self.allele_list[i].__repr__() - return ret + """ GAlleles Class - The set of alleles + + Example: + >>> alleles = GAlleles() + >>> choices = [1,2,3,4] + >>> lst = GAlleleList(choices) + >>> alleles.add(lst) + >>> alleles[0].getRandomAllele() in lst + True + + :param allele_list: the list of alleles + :param homogeneous: if is True, all the alleles will be use only the first added + + """ + + def __init__(self, allele_list = None, homogeneous=False): + """ The constructor of GAlleles class """ + self.allele_list = [] + if allele_list is not None: + self.allele_list.extend(allele_list) + self.homogeneous = homogeneous + + def __iadd__(self, allele): + """ To add more alleles using the += operator + + .. versionadded:: 0.6 + The __iadd__ method. + """ + self.add(allele) + return self + + def add(self, allele): + """ Appends one allele to the alleles list + + :param allele: allele to be added + + """ + self.allele_list.append(allele) + + def __getslice__(self, a, b): + """ Returns the slice part of alleles list """ + return self.allele_list[a:b] + + def __getitem__(self, index): + """ Returns the index allele of the alleles list """ + if self.homogeneous: + return self.allele_list[0] + try: + val = self.allele_list[index] + except IndexError: + Util.raiseException( + """An error was occurred while finding allele for the %d position of chromosome. + You may consider use the 'homogeneous' parameter of the GAlleles class. + """ % index) + return val + + def __setitem__(self, index, value): + """ Sets the index allele of the alleles list """ + if self.homogeneous: + self.allele_list[0] = value + self.allele_list[index] = value + + def __iter__(self): + """ Return the list iterator """ + if self.homogeneous: + oneList = [self.allele_list[0]] + return iter(oneList) + return iter(self.allele_list) + + def __len__(self): + """ Returns the lenght of the alleles list """ + if self.homogeneous: + return 1 + return len(self.allele_list) + + def __repr__(self): + """ Return a string representation of the allele """ + ret = "- GAlleles\n" + ret += "\tHomogeneous:\t %s\n" % (self.homogeneous,) + ret += "\tList size:\t %s\n" % (len(self),) + ret += "\tAlleles:\n\n" + if self.homogeneous: + ret += "Allele for 0 position:\n" + ret += self.allele_list[0].__repr__() + else: + for i in xrange(len(self)): + ret += "Allele for %d position:\n" % (i,) + ret += self.allele_list[i].__repr__() + return ret class GAlleleList: - """ GAlleleList Class - The list allele type + """ GAlleleList Class - The list allele type + + Example: + >>> alleles = GAlleles() + >>> choices = [1,2,3,4] + >>> lst = GAlleleList(choices) + >>> alleles.add(lst) + >>> alleles[0].getRandomAllele() in lst + True + + """ - Example: - >>> alleles = GAlleles() - >>> choices = [1,2,3,4] - >>> lst = GAlleleList(choices) - >>> alleles.add(lst) - >>> alleles[0].getRandomAllele() in lst - True + def __init__(self, options=None): + """ The constructor of GAlleleList class """ + self.options = [] + if options is not None: + self.options.extend(options) - """ + def clear(self): + """ Removes all the allele options from the list """ + del self.options[:] - def __init__(self, options=None): - """ The constructor of GAlleleList class """ - self.options = [] - if options is not None: - self.options.extend(options) + def getRandomAllele(self): + """ Returns one random choice from the options list """ + return random.choice(self.options) - def clear(self): - """ Removes all the allele options from the list """ - del self.options[:] - - def getRandomAllele(self): - """ Returns one random choice from the options list """ - return random.choice(self.options) + def add(self, option): + """ Appends one option to the options list - def add(self, option): - """ Appends one option to the options list + :param option: option to be added in the list - :param option: option to be added in the list + """ + self.options.append(option) - """ - self.options.append(option) + def __getslice__(self, a, b): + """ Returns the slice part of options """ + return self.options[a:b] - def __getslice__(self, a, b): - """ Returns the slice part of options """ - return self.options[a:b] + def __getitem__(self, index): + """ Returns the index option from the options list """ + return self.options[index] - def __getitem__(self, index): - """ Returns the index option from the options list """ - return self.options[index] + def __setitem__(self, index, value): + """ Sets the index option of the list """ + self.options[index] = value - def __setitem__(self, index, value): - """ Sets the index option of the list """ - self.options[index] = value + def __iter__(self): + """ Return the list iterator """ + return iter(self.options) - def __iter__(self): - """ Return the list iterator """ - return iter(self.options) + def __len__(self): + """ Returns the lenght of the options list """ + return len(self.options) - def __len__(self): - """ Returns the lenght of the options list """ - return len(self.options) + def remove(self, option): + """ Removes the option from list - def remove(self, option): - """ Removes the option from list + :param option: remove the option from the list - :param option: remove the option from the list + """ + self.options.remove(option) - """ - self.options.remove(option) + def __repr__(self): + """ Return a string representation of the allele """ + ret = "- GAlleleList\n" + ret += "\tList size:\t %s\n" % (len(self),) + ret += "\tAllele Options:\t %s\n\n" % (self.options,) + return ret - def __repr__(self): - """ Return a string representation of the allele """ - ret = "- GAlleleList\n" - ret += "\tList size:\t %s\n" % (len(self),) - ret += "\tAllele Options:\t %s\n\n" % (self.options,) - return ret class GAlleleRange: - """ GAlleleRange Class - The range allele type - - Example: - >>> ranges = GAlleleRange(0,100) - >>> ranges.getRandomAllele() >= 0 and ranges.getRandomAllele() <= 100 - True - - :param begin: the begin of the range - :param end: the end of the range - :param real: if True, the range will be of real values - - """ - - def __init__(self, begin=Consts.CDefRangeMin, - end=Consts.CDefRangeMax, real=False): - """ The constructor of GAlleleRange class """ - self.beginEnd = [(begin, end)] - self.real = real - self.minimum = None - self.maximum = None - self.__processMinMax() - - def __processMinMax(self): - """ Process the mininum and maximum of the Allele """ - self.minimum = min([x for x,y in self.beginEnd]) - self.maximum = max([y for x,y in self.beginEnd]) - - def add(self, begin, end): - """ Add a new range - - :param begin: the begin of range - :param end: the end of the range - - """ - if begin > end: - Util.raiseException('Wrong value, the end of the range (%s) is greater than the begin (%s) !' % (end, begin), ValueError) - self.beginEnd.append((begin, end)) - self.__processMinMax() - - def __getitem__(self, index): - return self.beginEnd[index] - - def __setitem__(self, index, value): - if value[0] > value[1]: - Util.raiseException('Wrong value, the end of the range is greater than the begin ! %s' % value, ValueError) - self.beginEnd[index] = value - self.__processMinMax() - - def __iter__(self): - return iter(self.beginEnd) - - def getMaximum(self): - """ Return the maximum of all the ranges - - :rtype: the maximum value - """ - return self.maximum - - def getMinimum(self): - """ Return the minimum of all the ranges - - :rtype: the minimum value - """ - return self.minimum - - def clear(self): - """ Removes all ranges """ - del self.beginEnd[:] - self.minimum = None - self.maximum = None - - def getRandomAllele(self): - """ Returns one random choice between the range """ - rand_func = random.uniform if self.real else random.randint - - if len(self.beginEnd) <= 1: choice = 0 - else: choice = random.randint(0, len(self.beginEnd)-1) - return rand_func(self.beginEnd[choice][0], self.beginEnd[choice][1]) - - def setReal(self, flag=True): - """ Sets True if the range is real or False if is integer - - :param flag: True or False - - """ - self.real = flag - - def getReal(self): - """ Returns True if the range is real or False if it is integer """ - return self.real - - def __len__(self): - """ Returns the ranges in the allele """ - return len(self.beginEnd) - - def __repr__(self): - """ Return a string representation of the allele """ - ret = "- GAlleleRange\n" - ret += "\tReal:\t\t %s\n" % (self.real,) - ret += "\tRanges Count:\t %s\n" % (len(self),) - ret += "\tRange List:\n" - for beg, end in self.beginEnd: - ret += "\t\t\t Range from [%s] to [%s]\n" % (beg, end) - ret += "\n" - return ret + """ GAlleleRange Class - The range allele type + + Example: + >>> ranges = GAlleleRange(0,100) + >>> ranges.getRandomAllele() >= 0 and ranges.getRandomAllele() <= 100 + True + + :param begin: the begin of the range + :param end: the end of the range + :param real: if True, the range will be of real values + + """ + + def __init__(self, begin=Consts.CDefRangeMin, + end=Consts.CDefRangeMax, real=False): + """ The constructor of GAlleleRange class """ + self.beginEnd = [(begin, end)] + self.real = real + self.minimum = None + self.maximum = None + self.__processMinMax() + + def __processMinMax(self): + """ Process the mininum and maximum of the Allele """ + self.minimum = min([x for x,y in self.beginEnd]) + self.maximum = max([y for x,y in self.beginEnd]) + + def add(self, begin, end): + """ Add a new range + + :param begin: the begin of range + :param end: the end of the range + + """ + if begin > end: + Util.raiseException('Wrong value, the end of the range (%s) is greater than the begin (%s) !' % (end, begin), ValueError) + self.beginEnd.append((begin, end)) + self.__processMinMax() + + def __getitem__(self, index): + return self.beginEnd[index] + + def __setitem__(self, index, value): + if value[0] > value[1]: + Util.raiseException('Wrong value, the end of the range is greater than the begin ! %s' % value, ValueError) + self.beginEnd[index] = value + self.__processMinMax() + + def __iter__(self): + return iter(self.beginEnd) + + def getMaximum(self): + """ Return the maximum of all the ranges + + :rtype: the maximum value + """ + return self.maximum + + def getMinimum(self): + """ Return the minimum of all the ranges + + :rtype: the minimum value + """ + return self.minimum + + def clear(self): + """ Removes all ranges """ + del self.beginEnd[:] + self.minimum = None + self.maximum = None + + def getRandomAllele(self): + """ Returns one random choice between the range """ + rand_func = random.uniform if self.real else random.randint + + if len(self.beginEnd) <= 1: choice = 0 + else: + choice = random.randint(0, len(self.beginEnd)-1) + return rand_func(self.beginEnd[choice][0], self.beginEnd[choice][1]) + + def setReal(self, flag=True): + """ Sets True if the range is real or False if is integer + + :param flag: True or False + + """ + self.real = flag + + def getReal(self): + """ Returns True if the range is real or False if it is integer """ + return self.real + + def __len__(self): + """ Returns the ranges in the allele """ + return len(self.beginEnd) + + def __repr__(self): + """ Return a string representation of the allele """ + ret = "- GAlleleRange\n" + ret += "\tReal:\t\t %s\n" % (self.real,) + ret += "\tRanges Count:\t %s\n" % (len(self),) + ret += "\tRange List:\n" + for beg, end in self.beginEnd: + ret += "\t\t\t Range from [%s] to [%s]\n" % (beg, end) + ret += "\n" + return ret diff --git a/pyevolve/GPopulation.py b/pyevolve/GPopulation.py index a60c3ff..7942acb 100644 --- a/pyevolve/GPopulation.py +++ b/pyevolve/GPopulation.py @@ -39,457 +39,457 @@ import logging try: - from multiprocessing import cpu_count, Pool - CPU_COUNT = cpu_count() - MULTI_PROCESSING = True if CPU_COUNT > 1 else False - logging.debug("You have %d CPU cores, so the multiprocessing state is %s", CPU_COUNT, MULTI_PROCESSING) + from multiprocessing import cpu_count, Pool + CPU_COUNT = cpu_count() + MULTI_PROCESSING = True if CPU_COUNT > 1 else False + logging.debug("You have %d CPU cores, so the multiprocessing state is %s", CPU_COUNT, MULTI_PROCESSING) except ImportError: - MULTI_PROCESSING = False - logging.debug("You don't have multiprocessing support for your Python version !") + MULTI_PROCESSING = False + logging.debug("You don't have multiprocessing support for your Python version !") def key_raw_score(individual): - """ A key function to return raw score + """ A key function to return raw score - :param individual: the individual instance - :rtype: the individual raw score + :param individual: the individual instance + :rtype: the individual raw score - .. note:: this function is used by the max()/min() python functions + .. note:: this function is used by the max()/min() python functions + + """ + return individual.score - """ - return individual.score def key_fitness_score(individual): - """ A key function to return fitness score, used by max()/min() + """ A key function to return fitness score, used by max()/min() - :param individual: the individual instance - :rtype: the individual fitness score + :param individual: the individual instance + :rtype: the individual fitness score - .. note:: this function is used by the max()/min() python functions + .. note:: this function is used by the max()/min() python functions - """ - return individual.fitness + """ + return individual.fitness def multiprocessing_eval(ind): - """ Internal used by the multiprocessing """ - ind.evaluate() - return ind.score + """ Internal used by the multiprocessing """ + ind.evaluate() + return ind.score + def multiprocessing_eval_full(ind): - """ Internal used by the multiprocessing (full copy)""" - ind.evaluate() - return ind + """ Internal used by the multiprocessing (full copy)""" + ind.evaluate() + return ind class GPopulation: - """ GPopulation Class - The container for the population - - **Examples** - Get the population from the :class:`GSimpleGA.GSimpleGA` (GA Engine) instance - >>> pop = ga_engine.getPopulation() - - Get the best fitness individual - >>> bestIndividual = pop.bestFitness() - - Get the best raw individual - >>> bestIndividual = pop.bestRaw() - - Get the statistics from the :class:`Statistics.Statistics` instance - >>> stats = pop.getStatistics() - >>> print stats["rawMax"] - 10.4 - - Iterate, get/set individuals - >>> for ind in pop: - >>> print ind - (...) - - >>> for i in xrange(len(pop)): - >>> print pop[i] - (...) - - >>> pop[10] = newGenome - >>> pop[10].fitness - 12.5 - - :param genome: the :term:`Sample genome`, or a GPopulation object, when cloning. - - """ - - def __init__(self, genome): - """ The GPopulation Class creator """ - - if isinstance(genome, GPopulation): - self.oneSelfGenome = genome.oneSelfGenome - self.internalPop = [] - self.internalPopRaw = [] - self.popSize = genome.popSize - self.sortType = genome.sortType - self.sorted = False - self.minimax = genome.minimax - self.scaleMethod = genome.scaleMethod - self.allSlots = [self.scaleMethod] - - self.internalParams = genome.internalParams - self.multiProcessing = genome.multiProcessing - - self.statted = False - self.stats = Statistics() - return - - logging.debug("New population instance, %s class genomes.", genome.__class__.__name__) - self.oneSelfGenome = genome - self.internalPop = [] - self.internalPopRaw = [] - self.popSize = 0 - self.sortType = Consts.CDefPopSortType - self.sorted = False - self.minimax = Consts.CDefPopMinimax - self.scaleMethod = FunctionSlot("Scale Method") - self.scaleMethod.set(Consts.CDefPopScale) - self.allSlots = [self.scaleMethod] - - self.internalParams = {} - self.multiProcessing = (False, False) - - # Statistics - self.statted = False - self.stats = Statistics() - - def setMultiProcessing(self, flag=True, full_copy=False): - """ Sets the flag to enable/disable the use of python multiprocessing module. - Use this option when you have more than one core on your CPU and when your - evaluation function is very slow. - The parameter "full_copy" defines where the individual data should be copied back - after the evaluation or not. This parameter is useful when you change the - individual in the evaluation function. - - :param flag: True (default) or False - :param full_copy: True or False (default) - - .. warning:: Use this option only when your evaluation function is slow, se you - will get a good tradeoff between the process communication speed and the - parallel evaluation. - - .. versionadded:: 0.6 - The `setMultiProcessing` method. - - """ - self.multiProcessing = (flag, full_copy) - - def setMinimax(self, minimax): - """ Sets the population minimax + """ GPopulation Class - The container for the population + + **Examples** + Get the population from the :class:`GSimpleGA.GSimpleGA` (GA Engine) instance + >>> pop = ga_engine.getPopulation() - Example: - >>> pop.setMinimax(Consts.minimaxType["maximize"]) - - :param minimax: the minimax type - - """ - self.minimax = minimax - - def __repr__(self): - """ Returns the string representation of the population """ - ret = "- GPopulation\n" - ret += "\tPopulation Size:\t %d\n" % (self.popSize,) - ret += "\tSort Type:\t\t %s\n" % (Consts.sortType.keys()[Consts.sortType.values().index(self.sortType)].capitalize(),) - ret += "\tMinimax Type:\t\t %s\n" % (Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)].capitalize(),) - for slot in self.allSlots: - ret+= "\t" + slot.__repr__() - ret+="\n" - ret+= self.stats.__repr__() - return ret - - def __len__(self): - """ Return the length of population """ - return len(self.internalPop) - - def __getitem__(self, key): - """ Returns the specified individual from population """ - return self.internalPop[key] - - def __iter__(self): - """ Returns the iterator of the population """ - return iter(self.internalPop) - - def __setitem__(self, key, value): - """ Set an individual of population """ - self.internalPop[key] = value - self.clearFlags() - - def clearFlags(self): - """ Clear the sorted and statted internal flags """ - self.sorted = False - self.statted = False - - def getStatistics(self): - """ Return a Statistics class for statistics - - :rtype: the :class:`Statistics.Statistics` instance - - """ - self.statistics() - return self.stats - - def statistics(self): - """ Do statistical analysis of population and set 'statted' to True """ - if self.statted: return - logging.debug("Running statistical calculations") - raw_sum = 0 - fit_sum = 0 - - len_pop = len(self) - for ind in xrange(len_pop): - raw_sum += self[ind].score - #fit_sum += self[ind].fitness - - self.stats["rawMax"] = max(self, key=key_raw_score).score - self.stats["rawMin"] = min(self, key=key_raw_score).score - self.stats["rawAve"] = raw_sum / float(len_pop) - #self.stats["rawTot"] = raw_sum - #self.stats["fitTot"] = fit_sum - - tmpvar = 0.0 - for ind in xrange(len_pop): - s = self[ind].score - self.stats["rawAve"] - s*= s - tmpvar += s - - tmpvar/= float((len(self) - 1)) - try: - self.stats["rawDev"] = math_sqrt(tmpvar) - except: - self.stats["rawDev"] = 0.0 - - self.stats["rawVar"] = tmpvar - - self.statted = True - - def bestFitness(self, index=0): - """ Return the best scaled fitness individual of population - - :param index: the *index* best individual - :rtype: the individual - - """ - self.sort() - return self.internalPop[index] - - def worstFitness(self): - """ Return the worst scaled fitness individual of the population - - :rtype: the individual - - """ - self.sort() - return self.internalPop[-1] - - def bestRaw(self, index=0): - """ Return the best raw score individual of population - - :param index: the *index* best raw individual - :rtype: the individual - - .. versionadded:: 0.6 - The parameter `index`. - - """ - if self.sortType == Consts.sortType["raw"]: - return self.internalPop[index] - else: - self.sort() - return self.internalPopRaw[index] - - def worstRaw(self): - """ Return the worst raw score individual of population - - :rtype: the individual - - .. versionadded:: 0.6 - The parameter `index`. - - """ - if self.sortType == Consts.sortType["raw"]: - return self.internalPop[-1] - else: - self.sort() - return self.internalPopRaw[-1] - - def sort(self): - """ Sort the population """ - if self.sorted: return - rev = (self.minimax == Consts.minimaxType["maximize"]) - - if self.sortType == Consts.sortType["raw"]: - self.internalPop.sort(cmp=Util.cmp_individual_raw, reverse=rev) - else: - self.scale() - self.internalPop.sort(cmp=Util.cmp_individual_scaled, reverse=rev) - self.internalPopRaw = self.internalPop[:] - self.internalPopRaw.sort(cmp=Util.cmp_individual_raw, reverse=rev) - - self.sorted = True - - def setPopulationSize(self, size): - """ Set the population size - - :param size: the population size - - """ - self.popSize = size - - def setSortType(self, sort_type): - """ Sets the sort type - - Example: - >>> pop.setSortType(Consts.sortType["scaled"]) - - :param sort_type: the Sort Type - - """ - self.sortType = sort_type - - def create(self, **args): - """ Clone the example genome to fill the population """ - self.minimax = args["minimax"] - self.internalPop = [self.oneSelfGenome.clone() for i in xrange(self.popSize)] - self.clearFlags() - - def __findIndividual(self, individual, end): - for i in xrange(end): - if individual.compare(self.internalPop[i]) == 0: - return True - - def initialize(self, **args): - """ Initialize all individuals of population, - this calls the initialize() of individuals """ - logging.debug("Initializing the population") - - if self.oneSelfGenome.getParam("full_diversity", True) and hasattr(self.oneSelfGenome, "compare"): - for i in xrange(len(self.internalPop)): - curr = self.internalPop[i] - curr.initialize(**args) - while self.__findIndividual(curr, i): - curr.initialize(**args) - else: - for gen in self.internalPop: - gen.initialize(**args) - self.clearFlags() - - def evaluate(self, **args): - """ Evaluate all individuals in population, calls the evaluate() method of individuals - - :param args: this params are passed to the evaluation function - - """ - # We have multiprocessing - if self.multiProcessing[0] and MULTI_PROCESSING: - logging.debug("Evaluating the population using the multiprocessing method") - proc_pool = Pool() - - # Multiprocessing full_copy parameter - if self.multiProcessing[1]: - results = proc_pool.map(multiprocessing_eval_full, self.internalPop) - proc_pool.close() - proc_pool.join() - for i in xrange(len(self.internalPop)): - self.internalPop[i] = results[i] - else: - results = proc_pool.map(multiprocessing_eval, self.internalPop) - proc_pool.close() - proc_pool.join() - for individual, score in zip(self.internalPop, results): - individual.score = score - else: - for ind in self.internalPop: - ind.evaluate(**args) - - self.clearFlags() - - def scale(self, **args): - """ Scale the population using the scaling method - - :param args: this parameter is passed to the scale method - - """ - for it in self.scaleMethod.applyFunctions(self, **args): - pass - - fit_sum = 0 - for ind in xrange(len(self)): - fit_sum += self[ind].fitness - - self.stats["fitMax"] = max(self, key=key_fitness_score).fitness - self.stats["fitMin"] = min(self, key=key_fitness_score).fitness - self.stats["fitAve"] = fit_sum / float(len(self)) - - self.sorted = False - - def printStats(self): - """ Print statistics of the current population """ - message = "" - if self.sortType == Consts.sortType["scaled"]: - message = "Max/Min/Avg Fitness(Raw) [%(fitMax).2f(%(rawMax).2f)/%(fitMin).2f(%(rawMin).2f)/%(fitAve).2f(%(rawAve).2f)]" % self.stats - else: - message = "Max/Min/Avg Raw [%(rawMax).2f/%(rawMin).2f/%(rawAve).2f]" % self.stats - logging.info(message) - print message - return message - - def copy(self, pop): - """ Copy current population to 'pop' - - :param pop: the destination population - - .. warning:: this method do not copy the individuals, only the population logic - - """ - pop.popSize = self.popSize - pop.sortType = self.sortType - pop.minimax = self.minimax - pop.scaleMethod = self.scaleMethod - #pop.internalParams = self.internalParams.copy() - pop.internalParams = self.internalParams - pop.multiProcessing = self.multiProcessing - - def getParam(self, key, nvl=None): - """ Gets an internal parameter + Get the best fitness individual + >>> bestIndividual = pop.bestFitness() + + Get the best raw individual + >>> bestIndividual = pop.bestRaw() + + Get the statistics from the :class:`Statistics.Statistics` instance + >>> stats = pop.getStatistics() + >>> print stats["rawMax"] + 10.4 + + Iterate, get/set individuals + >>> for ind in pop: + >>> print ind + (...) + + >>> for i in xrange(len(pop)): + >>> print pop[i] + (...) + + >>> pop[10] = newGenome + >>> pop[10].fitness + 12.5 + + :param genome: the :term:`Sample genome`, or a GPopulation object, when cloning. + + """ + + def __init__(self, genome): + """ The GPopulation Class creator """ + + if isinstance(genome, GPopulation): + self.oneSelfGenome = genome.oneSelfGenome + self.internalPop = [] + self.internalPopRaw = [] + self.popSize = genome.popSize + self.sortType = genome.sortType + self.sorted = False + self.minimax = genome.minimax + self.scaleMethod = genome.scaleMethod + self.allSlots = [self.scaleMethod] + + self.internalParams = genome.internalParams + self.multiProcessing = genome.multiProcessing + + self.statted = False + self.stats = Statistics() + return + + logging.debug("New population instance, %s class genomes.", genome.__class__.__name__) + self.oneSelfGenome = genome + self.internalPop = [] + self.internalPopRaw = [] + self.popSize = 0 + self.sortType = Consts.CDefPopSortType + self.sorted = False + self.minimax = Consts.CDefPopMinimax + self.scaleMethod = FunctionSlot("Scale Method") + self.scaleMethod.set(Consts.CDefPopScale) + self.allSlots = [self.scaleMethod] + + self.internalParams = {} + self.multiProcessing = (False, False) + + # Statistics + self.statted = False + self.stats = Statistics() + + def setMultiProcessing(self, flag=True, full_copy=False): + """ Sets the flag to enable/disable the use of python multiprocessing module. + Use this option when you have more than one core on your CPU and when your + evaluation function is very slow. + The parameter "full_copy" defines where the individual data should be copied back + after the evaluation or not. This parameter is useful when you change the + individual in the evaluation function. + + :param flag: True (default) or False + :param full_copy: True or False (default) + + .. warning:: Use this option only when your evaluation function is slow, se you + will get a good tradeoff between the process communication speed and the + parallel evaluation. + + .. versionadded:: 0.6 + The `setMultiProcessing` method. + + """ + self.multiProcessing = (flag, full_copy) + + def setMinimax(self, minimax): + """ Sets the population minimax + + Example: + >>> pop.setMinimax(Consts.minimaxType["maximize"]) + + :param minimax: the minimax type + + """ + self.minimax = minimax + + def __repr__(self): + """ Returns the string representation of the population """ + sort_type = Consts.sortType.keys()[Consts.sortType.values().index(self.sortType)] + minimax_type = Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)] + ret = "- GPopulation\n" + ret += "\tPopulation Size:\t %d\n" % self.popSize + ret += "\tSort Type:\t\t %s\n" % sort_type.capitalize() + ret += "\tMinimax Type:\t\t %s\n" % minimax_type.capitalize() + for slot in self.allSlots: + ret += "\t" + slot.__repr__() + ret += "\n" + ret += self.stats.__repr__() + return ret + + def __len__(self): + """ Return the length of population """ + return len(self.internalPop) + + def __getitem__(self, key): + """ Returns the specified individual from population """ + return self.internalPop[key] + + def __iter__(self): + """ Returns the iterator of the population """ + return iter(self.internalPop) + + def __setitem__(self, key, value): + """ Set an individual of population """ + self.internalPop[key] = value + self.clearFlags() + + def clearFlags(self): + """ Clear the sorted and statted internal flags """ + self.sorted = False + self.statted = False + + def getStatistics(self): + """ Return a Statistics class for statistics + + :rtype: the :class:`Statistics.Statistics` instance + + """ + self.statistics() + return self.stats + + def statistics(self): + """ Do statistical analysis of population and set 'statted' to True """ + if self.statted: return + logging.debug("Running statistical calculations") + raw_sum = 0 + fit_sum = 0 + + len_pop = len(self) + for ind in xrange(len_pop): + raw_sum += self[ind].score + #fit_sum += self[ind].fitness + + self.stats["rawMax"] = max(self, key=key_raw_score).score + self.stats["rawMin"] = min(self, key=key_raw_score).score + self.stats["rawAve"] = raw_sum / float(len_pop) + #self.stats["rawTot"] = raw_sum + #self.stats["fitTot"] = fit_sum + + tmpvar = 0.0 + for ind in xrange(len_pop): + s = self[ind].score - self.stats["rawAve"] + s*= s + tmpvar += s + + tmpvar /= float((len(self) - 1)) + try: + self.stats["rawDev"] = math_sqrt(tmpvar) + except: + self.stats["rawDev"] = 0.0 + + self.stats["rawVar"] = tmpvar + + self.statted = True + + def bestFitness(self, index=0): + """ Return the best scaled fitness individual of population + + :param index: the *index* best individual + :rtype: the individual - Example: - >>> population.getParam("tournamentPool") - 5 + """ + self.sort() + return self.internalPop[index] - :param key: the key of param - :param nvl: if the key doesn't exist, the nvl will be returned + def worstFitness(self): + """ Return the worst scaled fitness individual of the population - """ - return self.internalParams.get(key, nvl) + :rtype: the individual + """ + self.sort() + return self.internalPop[-1] + + def bestRaw(self, index=0): + """ Return the best raw score individual of population + + :param index: the *index* best raw individual + :rtype: the individual + + .. versionadded:: 0.6 + The parameter `index`. - def setParams(self, **args): - """ Gets an internal parameter + """ + if self.sortType == Consts.sortType["raw"]: + return self.internalPop[index] + else: + self.sort() + return self.internalPopRaw[index] - Example: - >>> population.setParams(tournamentPool=5) + def worstRaw(self): + """ Return the worst raw score individual of population - :param args: parameters to set + :rtype: the individual - .. versionadded:: 0.6 - The `setParams` method. - """ - self.internalParams.update(args) + .. versionadded:: 0.6 + The parameter `index`. - def clear(self): - """ Remove all individuals from population """ - del self.internalPop[:] - del self.internalPopRaw[:] - self.clearFlags() - - def clone(self): - """ Return a brand-new cloned population """ - newpop = GPopulation(self.oneSelfGenome) - self.copy(newpop) - return newpop - + """ + if self.sortType == Consts.sortType["raw"]: + return self.internalPop[-1] + else: + self.sort() + return self.internalPopRaw[-1] + def sort(self): + """ Sort the population """ + if self.sorted: return + rev = (self.minimax == Consts.minimaxType["maximize"]) + + if self.sortType == Consts.sortType["raw"]: + self.internalPop.sort(cmp=Util.cmp_individual_raw, reverse=rev) + else: + self.scale() + self.internalPop.sort(cmp=Util.cmp_individual_scaled, reverse=rev) + self.internalPopRaw = self.internalPop[:] + self.internalPopRaw.sort(cmp=Util.cmp_individual_raw, reverse=rev) + + self.sorted = True + + def setPopulationSize(self, size): + """ Set the population size + + :param size: the population size + + """ + self.popSize = size + + def setSortType(self, sort_type): + """ Sets the sort type + + Example: + >>> pop.setSortType(Consts.sortType["scaled"]) + + :param sort_type: the Sort Type + + """ + self.sortType = sort_type + + def create(self, **args): + """ Clone the example genome to fill the population """ + self.minimax = args["minimax"] + self.internalPop = [self.oneSelfGenome.clone() for i in xrange(self.popSize)] + self.clearFlags() + + def __findIndividual(self, individual, end): + for i in xrange(end): + if individual.compare(self.internalPop[i]) == 0: + return True + + def initialize(self, **args): + """ Initialize all individuals of population, + this calls the initialize() of individuals """ + logging.debug("Initializing the population") + + if self.oneSelfGenome.getParam("full_diversity", True) and hasattr(self.oneSelfGenome, "compare"): + for i in xrange(len(self.internalPop)): + curr = self.internalPop[i] + curr.initialize(**args) + while self.__findIndividual(curr, i): + curr.initialize(**args) + else: + for gen in self.internalPop: + gen.initialize(**args) + self.clearFlags() + + def evaluate(self, **args): + """ Evaluate all individuals in population, calls the evaluate() method of individuals + + :param args: this params are passed to the evaluation function + + """ + # We have multiprocessing + if self.multiProcessing[0] and MULTI_PROCESSING: + logging.debug("Evaluating the population using the multiprocessing method") + proc_pool = Pool() + + # Multiprocessing full_copy parameter + if self.multiProcessing[1]: + results = proc_pool.map(multiprocessing_eval_full, self.internalPop) + proc_pool.close() + proc_pool.join() + for i in xrange(len(self.internalPop)): + self.internalPop[i] = results[i] + else: + results = proc_pool.map(multiprocessing_eval, self.internalPop) + proc_pool.close() + proc_pool.join() + for individual, score in zip(self.internalPop, results): + individual.score = score + else: + for ind in self.internalPop: + ind.evaluate(**args) + + self.clearFlags() + + def scale(self, **args): + """ Scale the population using the scaling method + + :param args: this parameter is passed to the scale method + + """ + for it in self.scaleMethod.applyFunctions(self, **args): + pass + + fit_sum = 0 + for ind in xrange(len(self)): + fit_sum += self[ind].fitness + + self.stats["fitMax"] = max(self, key=key_fitness_score).fitness + self.stats["fitMin"] = min(self, key=key_fitness_score).fitness + self.stats["fitAve"] = fit_sum / float(len(self)) + + self.sorted = False + + def printStats(self): + """ Print statistics of the current population """ + if self.sortType == Consts.sortType["scaled"]: + message = "Max/Min/Avg Fitness(Raw) [%(fitMax).2f(%(rawMax).2f)/%(fitMin).2f(%(rawMin).2f)/%(fitAve).2f(%(rawAve).2f)]" % self.stats + else: + message = "Max/Min/Avg Raw [%(rawMax).2f/%(rawMin).2f/%(rawAve).2f]" % self.stats + logging.info(message) + print message + return message + + def copy(self, pop): + """ Copy current population to 'pop' + + :param pop: the destination population + + .. warning:: this method do not copy the individuals, only the population logic + + """ + pop.popSize = self.popSize + pop.sortType = self.sortType + pop.minimax = self.minimax + pop.scaleMethod = self.scaleMethod + #pop.internalParams = self.internalParams.copy() + pop.internalParams = self.internalParams + pop.multiProcessing = self.multiProcessing + + def getParam(self, key, nvl=None): + """ Gets an internal parameter + + Example: + >>> population.getParam("tournamentPool") + 5 + + :param key: the key of param + :param nvl: if the key doesn't exist, the nvl will be returned + + """ + return self.internalParams.get(key, nvl) + + def setParams(self, **args): + """ Gets an internal parameter + + Example: + >>> population.setParams(tournamentPool=5) + + :param args: parameters to set + + .. versionadded:: 0.6 + The `setParams` method. + """ + self.internalParams.update(args) + + def clear(self): + """ Remove all individuals from population """ + del self.internalPop[:] + del self.internalPopRaw[:] + self.clearFlags() + + def clone(self): + """ Return a brand-new cloned population """ + newpop = GPopulation(self.oneSelfGenome) + self.copy(newpop) + return newpop diff --git a/pyevolve/GenomeBase.py b/pyevolve/GenomeBase.py index 088da7e..8514ba0 100644 --- a/pyevolve/GenomeBase.py +++ b/pyevolve/GenomeBase.py @@ -14,620 +14,625 @@ from FunctionSlot import FunctionSlot import Util + class GenomeBase: - """ GenomeBase Class - The base of all chromosome representation """ + """ GenomeBase Class - The base of all chromosome representation """ + + evaluator = None + """ This is the :term:`evaluation function` slot, you can add + a function with the *set* method: :: + + genome.evaluator.set(eval_func) + """ + + initializator = None + """ This is the initialization function of the genome, you + can change the default initializator using the function slot: :: + + genome.initializator.set(Initializators.G1DListInitializatorAllele) + + In this example, the initializator :func:`Initializators.G1DListInitializatorAllele` + will be used to create the initial population. + """ + + mutator = None + """ This is the mutator function slot, you can change the default + mutator using the slot *set* function: :: + + genome.mutator.set(Mutators.G1DListMutatorSwap) + + """ - evaluator = None - """ This is the :term:`evaluation function` slot, you can add - a function with the *set* method: :: + crossover = None + """ This is the reproduction function slot, the crossover. You + can change the default crossover method using: :: - genome.evaluator.set(eval_func) - """ + genome.crossover.set(Crossovers.G1DListCrossoverUniform) + """ - initializator = None - """ This is the initialization function of the genome, you - can change the default initializator using the function slot: :: + def __init__(self): + """Genome Constructor""" + self.evaluator = FunctionSlot("Evaluator") + self.initializator = FunctionSlot("Initializator") + self.mutator = FunctionSlot("Mutator") + self.crossover = FunctionSlot("Crossover") - genome.initializator.set(Initializators.G1DListInitializatorAllele) + self.internalParams = {} + self.score = 0.0 + self.fitness = 0.0 - In this example, the initializator :func:`Initializators.G1DListInitializatorAllele` - will be used to create the initial population. - """ + def getRawScore(self): + """ Get the Raw Score of the genome - mutator = None - """ This is the mutator function slot, you can change the default - mutator using the slot *set* function: :: + :rtype: genome raw score - genome.mutator.set(Mutators.G1DListMutatorSwap) + """ + return self.score - """ + def getFitnessScore(self): + """ Get the Fitness Score of the genome - crossover = None - """ This is the reproduction function slot, the crossover. You - can change the default crossover method using: :: + :rtype: genome fitness score - genome.crossover.set(Crossovers.G1DListCrossoverUniform) - """ + """ + return self.fitness + def __repr__(self): + """String representation of Genome""" + allSlots = self.allSlots = [ + self.evaluator, + self.initializator, + self.mutator, + self.crossover + ] - def __init__(self): - """Genome Constructor""" - self.evaluator = FunctionSlot("Evaluator") - self.initializator = FunctionSlot("Initializator") - self.mutator = FunctionSlot("Mutator") - self.crossover = FunctionSlot("Crossover") - - self.internalParams = {} - self.score = 0.0 - self.fitness = 0.0 + ret = "- GenomeBase\n" + ret+= "\tScore:\t\t\t %.6f\n" % (self.score,) + ret+= "\tFitness:\t\t %.6f\n\n" % (self.fitness,) + ret+= "\tParams:\t\t %s\n\n" % (self.internalParams,) - def getRawScore(self): - """ Get the Raw Score of the genome + for slot in allSlots: + ret += "\t" + slot.__repr__() + ret += "\n" - :rtype: genome raw score + return ret - """ - return self.score + def setParams(self, **args): + """ Set the internal params - def getFitnessScore(self): - """ Get the Fitness Score of the genome + Example: + >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1) - :rtype: genome fitness score + .. note:: All the individuals of the population shares this parameters and uses + the same instance of this dict. - """ - return self.fitness + :param args: this params will saved in every chromosome for genetic op. use - def __repr__(self): - """String representation of Genome""" - allSlots = self.allSlots = [ self.evaluator, self.initializator, - self.mutator, self.crossover ] + """ + self.internalParams.update(args) - ret = "- GenomeBase\n" - ret+= "\tScore:\t\t\t %.6f\n" % (self.score,) - ret+= "\tFitness:\t\t %.6f\n\n" % (self.fitness,) - ret+= "\tParams:\t\t %s\n\n" % (self.internalParams,) + def getParam(self, key, nvl=None): + """ Gets an internal parameter - for slot in allSlots: - ret+= "\t" + slot.__repr__() - ret+="\n" + Example: + >>> genome.getParam("rangemax") + 100 - return ret + .. note:: All the individuals of the population shares this parameters and uses + the same instance of this dict. - def setParams(self, **args): - """ Set the internal params + :param key: the key of param + :param nvl: if the key doesn't exist, the nvl will be returned - Example: - >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1) + """ + return self.internalParams.get(key, nvl) - .. note:: All the individuals of the population shares this parameters and uses - the same instance of this dict. + def resetStats(self): + """ Clear score and fitness of genome """ + self.score = 0.0 + self.fitness = 0.0 - :param args: this params will saved in every chromosome for genetic op. use + def evaluate(self, **args): + """ Called to evaluate genome - """ - self.internalParams.update(args) - - def getParam(self, key, nvl=None): - """ Gets an internal parameter + :param args: this parameters will be passes to the evaluator - Example: - >>> genome.getParam("rangemax") - 100 + """ + self.resetStats() + for it in self.evaluator.applyFunctions(self, **args): + self.score += it - .. note:: All the individuals of the population shares this parameters and uses - the same instance of this dict. + def initialize(self, **args): + """ Called to initialize genome - :param key: the key of param - :param nvl: if the key doesn't exist, the nvl will be returned + :param args: this parameters will be passed to the initializator - """ - return self.internalParams.get(key, nvl) - - def resetStats(self): - """ Clear score and fitness of genome """ - self.score = 0.0 - self.fitness = 0.0 - - def evaluate(self, **args): - """ Called to evaluate genome + """ + for it in self.initializator.applyFunctions(self, **args): + pass - :param args: this parameters will be passes to the evaluator + def mutate(self, **args): + """ Called to mutate the genome - """ - self.resetStats() - for it in self.evaluator.applyFunctions(self, **args): - self.score += it + :param args: this parameters will be passed to the mutator + :rtype: the number of mutations returned by mutation operator - def initialize(self, **args): - """ Called to initialize genome + """ + nmuts = 0 + for it in self.mutator.applyFunctions(self, **args): + nmuts += it + return nmuts - :param args: this parameters will be passed to the initializator + def copy(self, g): + """ Copy the current GenomeBase to 'g' - """ - for it in self.initializator.applyFunctions(self, **args): - pass + :param g: the destination genome - def mutate(self, **args): - """ Called to mutate the genome - - :param args: this parameters will be passed to the mutator - :rtype: the number of mutations returned by mutation operator - - """ - nmuts = 0 - for it in self.mutator.applyFunctions(self, **args): - nmuts+=it - return nmuts + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. - def copy(self, g): - """ Copy the current GenomeBase to 'g' + """ + g.score = self.score + g.fitness = self.fitness + g.evaluator = self.evaluator + g.initializator = self.initializator + g.mutator = self.mutator + g.crossover = self.crossover + #g.internalParams = self.internalParams.copy() + g.internalParams = self.internalParams - :param g: the destination genome + def clone(self): + """ Clone this GenomeBase + + :rtype: the clone genome + + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. + """ + newcopy = GenomeBase() + self.copy(newcopy) + return newcopy - .. note:: If you are planning to create a new chromosome representation, you - **must** implement this method on your class. - """ - g.score = self.score - g.fitness = self.fitness - g.evaluator = self.evaluator - g.initializator = self.initializator - g.mutator = self.mutator - g.crossover = self.crossover - #g.internalParams = self.internalParams.copy() - g.internalParams = self.internalParams - - def clone(self): - """ Clone this GenomeBase - - :rtype: the clone genome - - .. note:: If you are planning to create a new chromosome representation, you - **must** implement this method on your class. - """ - newcopy = GenomeBase() - self.copy(newcopy) - return newcopy - class G1DBase: - """ G1DBase Class - The base class for 1D chromosomes - - :param size: the 1D list size - - .. versionadded:: 0.6 - Added te *G1DBase* class - """ - - def __init__(self, size): - self.genomeSize = size - self.genomeList = [] - - def __iadd__(self, item): - """ To add more items using the += operator """ - self.genomeList.append(item) - return self - - def __eq__(self, other): - """ Compares one chromosome with another """ - cond1 = (self.genomeList == other.genomeList) - cond2 = (self.genomeSize == other.genomeSize) - return True if cond1 and cond2 else False - - def __contains__(self, value): - """ Used on: *value in genome* """ - return value in self.genomeList - - def __getslice__(self, a, b): - """ Return the sliced part of chromosome """ - return self.genomeList[a:b] - - def __setslice__(self, a, b, val): - """ Sets the slice part of chromosome """ - self.genomeList[a:b] = val - - def __getitem__(self, key): - """ Return the specified gene of List """ - return self.genomeList[key] - - def __setitem__(self, key, value): - """ Set the specified value for an gene of List """ - self.genomeList[key] = value - - def __iter__(self): - """ Iterator support to the list """ - return iter(self.genomeList) - - def __len__(self): - """ Return the size of the List """ - return len(self.genomeList) - - def getListSize(self): - """ Returns the list supposed size - - .. warning:: this is different from what the len(obj) returns - """ - return self.genomeSize - - def resumeString(self): - """ Returns a resumed string representation of the Genome """ - return str(self.genomeList) - - def append(self, value): - """ Appends an item to the end of the list - - Example: - >>> genome.append(44) - - :param value: value to be added - - """ - self.genomeList.append(value) - - def remove(self, value): - """ Removes an item from the list - - Example: - >>> genome.remove(44) - - :param value: value to be added - - """ - self.genomeList.remove(value) - - def clearList(self): - """ Remove all genes from Genome """ - del self.genomeList[:] - - def copy(self, g): - """ Copy genome to 'g' - - Example: - >>> genome_origin.copy(genome_destination) - - :param g: the destination instance - - """ - g.genomeSize = self.genomeSize - g.genomeList = self.genomeList[:] - - def getInternalList(self): - """ Returns the internal list of the genome - - ... note:: this method was created to solve performance issues - :rtype: the internal list - """ - return self.genomeList - - def setInternalList(self, lst): - """ Assigns a list to the internal list of the chromosome - - :param lst: the list to assign the internal list of the chromosome - """ - self.genomeList = lst + """ G1DBase Class - The base class for 1D chromosomes + + :param size: the 1D list size + + .. versionadded:: 0.6 + Added te *G1DBase* class + """ + + def __init__(self, size): + self.genomeSize = size + self.genomeList = [] + + def __iadd__(self, item): + """ To add more items using the += operator """ + self.genomeList.append(item) + return self + + def __eq__(self, other): + """ Compares one chromosome with another """ + cond1 = (self.genomeList == other.genomeList) + cond2 = (self.genomeSize == other.genomeSize) + return True if cond1 and cond2 else False + + def __contains__(self, value): + """ Used on: *value in genome* """ + return value in self.genomeList + + def __getslice__(self, a, b): + """ Return the sliced part of chromosome """ + return self.genomeList[a:b] + + def __setslice__(self, a, b, val): + """ Sets the slice part of chromosome """ + self.genomeList[a:b] = val + + def __getitem__(self, key): + """ Return the specified gene of List """ + return self.genomeList[key] + + def __setitem__(self, key, value): + """ Set the specified value for an gene of List """ + self.genomeList[key] = value + + def __iter__(self): + """ Iterator support to the list """ + return iter(self.genomeList) + + def __len__(self): + """ Return the size of the List """ + return len(self.genomeList) + + def getListSize(self): + """ Returns the list supposed size + + .. warning:: this is different from what the len(obj) returns + """ + return self.genomeSize + + def resumeString(self): + """ Returns a resumed string representation of the Genome """ + return str(self.genomeList) + + def append(self, value): + """ Appends an item to the end of the list + + Example: + >>> genome.append(44) + + :param value: value to be added + + """ + self.genomeList.append(value) + + def remove(self, value): + """ Removes an item from the list + + Example: + >>> genome.remove(44) + + :param value: value to be added + + """ + self.genomeList.remove(value) + + def clearList(self): + """ Remove all genes from Genome """ + del self.genomeList[:] + + def copy(self, g): + """ Copy genome to 'g' + + Example: + >>> genome_origin.copy(genome_destination) + + :param g: the destination instance + + """ + g.genomeSize = self.genomeSize + g.genomeList = self.genomeList[:] + + def getInternalList(self): + """ Returns the internal list of the genome + + ... note:: this method was created to solve performance issues + :rtype: the internal list + """ + return self.genomeList + + def setInternalList(self, lst): + """ Assigns a list to the internal list of the chromosome + + :param lst: the list to assign the internal list of the chromosome + """ + self.genomeList = lst + class GTreeNodeBase: - """ GTreeNodeBase Class - The base class for the node tree genomes - - :param parent: the parent node of the node - :param childs: the childs of the node, must be a list of nodes - - .. versionadded:: 0.6 - Added te *GTreeNodeBase* class - """ - - def __init__(self, parent, childs=None): - self.parent = parent - self.childs = [] - - if childs is not None: - if type(childs) != list: - Util.raiseException("Childs must be a list of nodes", TypeError) - typecheck_list = filter(lambda x: not isinstance(x, GTreeNodeBase), childs) - if len(typecheck_list) > 0: - Util.raiseException("Childs must be a list of nodes", TypeError) - self.childs += childs - - def isLeaf(self): - """ Return True if the node is a leaf - - :rtype: True or False - """ - return len(self.childs)==0 - - def getChild(self, index): - """ Returns the index-child of the node - - :rtype: child node - """ - return self.childs[index] - - def getChilds(self): - """ Return the childs of the node - - .. warning :: use .getChilds()[:] if you'll change the list itself, like using childs.reverse(), - otherwise the original genome child order will be changed. - - :rtype: a list of nodes - """ - return self.childs - - def addChild(self, child): - """ Adds a child to the node - - :param child: the node to be added - """ - if type(child) == list: - self.childs.extend(child) - else: - if not isinstance(child, GTreeNodeBase): - Util.raiseException("The child must be a node", TypeError) - self.childs.append(child) - - def replaceChild(self, older, newer): - """ Replaces a child of the node - - :param older: the child to be replaces - :param newer: the new child which replaces the older - """ - index = self.childs.index(older) - self.childs[index] = newer - - def setParent(self, parent): - """ Sets the parent of the node - - :param parent: the parent node - """ - #if not isinstance(parent, GTreeNodeBase): - # Util.raiseException("The parent must be a node", TypeError) - self.parent = parent - - def getParent(self): - """ Get the parent node of the node - - :rtype: the parent node - """ - return self.parent - - def __repr__(self): - parent = "None" if self.getParent() is None else "Present" - str_repr = "GTreeNodeBase [Childs=%d]" % len(self) - return str_repr - - def __len__(self): - return len(self.childs) - - def copy(self, g): - """ Copy the current contents GTreeNodeBase to 'g' - - :param g: the destination node - - .. note:: If you are planning to create a new chromosome representation, you - **must** implement this method on your class. - """ - g.parent = self.parent - g.childs = self.childs[:] - - def clone(self): - """ Clone this GenomeBase - - :rtype: the clone genome - - .. note:: If you are planning to create a new chromosome representation, you - **must** implement this method on your class. - """ - newcopy = GTreeNodeBase(None) - self.copy(newcopy) - return newcopy - + """ GTreeNodeBase Class - The base class for the node tree genomes -class GTreeBase: - """ GTreeBase Class - The base class for the tree genomes - - :param root_node: the root node of the tree - - .. versionadded:: 0.6 - Added te *GTreeBase* class - """ - - def __init__(self, root_node): - self.root_node = root_node - self.tree_height = None - self.nodes_list = None - - def processNodes(self, cloning=False): - """ Creates a *cache* on the tree, this method must be called - every time you change the shape of the tree. It updates the - internal nodes list and the internal nodes properties such as - depth and height. - """ - if self.root_node is None: return - self.nodes_list = self.getAllNodes() - self.nodes_leaf = filter(lambda n: n.isLeaf(), self.nodes_list) - self.nodes_branch = filter(lambda n: n.isLeaf()==False, self.nodes_list) - - if not cloning: - self.tree_height = self.getNodeHeight(self.getRoot()) - - def getRoot(self): - """ Return the tree root node - - :rtype: the tree root node - """ - return self.root_node - - def setRoot(self, root): - """ Sets the root of the tree - - :param root: the tree root node - """ - if not isinstance(root, GTreeNodeBase): - Util.raiseException("The root must be a node", TypeError) - self.root_node = root - - def getNodeDepth(self, node): - """ Returns the depth of a node - - :rtype: the depth of the node, the depth of root node is 0 - """ - if node==self.getRoot(): return 0 - else: return 1 + self.getNodeDepth(node.getParent()) - - def getNodeHeight(self, node): - """ Returns the height of a node - - .. note:: If the node has no childs, the height will be 0. - - :rtype: the height of the node - """ - height = 0 - if len(node) <= 0: - return 0 - for child in node.getChilds(): - h_inner = self.getNodeHeight(child)+1 - if h_inner > height: - height = h_inner - return height - - def getHeight(self): - """ Return the tree height - - :rtype: the tree height - """ - return self.tree_height - - def getNodesCount(self, start_node=None): - """ Return the number of the nodes on the tree - starting at the *start_node*, if *start_node* is None, - then the method will count all the tree nodes. - - :rtype: the number of nodes - """ - count = 1 - if start_node is None: - start_node = self.getRoot() - for i in start_node.getChilds(): - count += self.getNodesCount(i) - return count - - def getTraversalString(self, start_node=None, spc=0): - """ Returns a tree-formated string of the tree. This - method is used by the __repr__ method of the tree - - :rtype: a string representing the tree - """ - str_buff = "" - if start_node is None: - start_node = self.getRoot() - str_buff += "%s\n" % start_node - spaces = spc + 2 - for child_node in start_node.getChilds(): - str_buff += "%s%s\n" % (" " * spaces, child_node) - str_buff += self.getTraversalString(child_node, spaces) - return str_buff - - - def traversal(self, callback, start_node=None): - """ Traversal the tree, this method will call the - user-defined callback function for each node on the tree - - :param callback: a function - :param start_node: the start node to begin the traversal - """ - if not inspect.isfunction(callback): - Util.raiseException("The callback for the tree traversal must be a function", TypeError) - - if start_node is None: - start_node = self.getRoot() - callback(start_node) - for child_node in start_node.getChilds(): - callback(child_node) - self.traversal(callback, child_node) - - def getRandomNode(self, node_type=0): - """ Returns a random node from the Tree - - :param node_type: 0 = Any, 1 = Leaf, 2 = Branch - :rtype: random node - """ - lists = (self.nodes_list, self.nodes_leaf, self.nodes_branch) - cho = lists[node_type] - if len(cho) <= 0: - return None - return rand_choice(cho) - - def getAllNodes(self): - """ Return a new list with all nodes - - :rtype: the list with all nodes - """ - node_stack = [] - all_nodes = [] - tmp = None - - node_stack.append(self.getRoot()) - while len(node_stack) > 0: - tmp = node_stack.pop() - all_nodes.append(tmp) - childs = tmp.getChilds() - node_stack.extend(childs) - - return all_nodes - - def __repr__(self): - str_buff = "- GTree\n" - str_buff += "\tHeight:\t\t\t%d\n" % self.getHeight() - str_buff += "\tNodes:\t\t\t%d\n" % self.getNodesCount() - str_buff += "\n" + self.getTraversalString() - - return str_buff - - def __len__(self): - return len(self.nodes_list) - - def __getitem__(self, index): - return self.nodes_list[index] - - def __iter__(self): - return iter(self.nodes_list) - - def copy(self, g, node=None, node_parent=None): - """ Copy the current contents GTreeBase to 'g' - - :param g: the destination GTreeBase tree - - .. note:: If you are planning to create a new chromosome representation, you - **must** implement this method on your class. - """ - if node is None: - g.tree_height = self.tree_height - node = self.root_node - - if node is None: return None - - newnode = node.clone() - - if node_parent is None: - g.setRoot(newnode) - else: - newnode.setParent(node_parent) - node_parent.replaceChild(node, newnode) - - for ci in xrange(len(newnode)): - GTreeBase.copy(self, g, newnode.getChild(ci), newnode) - - return newnode - - def clone(self): - """ Clone this GenomeBase - - :rtype: the clone genome - - .. note:: If you are planning to create a new chromosome representation, you - **must** implement this method on your class. - """ - newcopy = GTreeBase(None) - self.copy(newcopy) - newcopy.processNodes() - return newcopy + :param parent: the parent node of the node + :param childs: the childs of the node, must be a list of nodes + + .. versionadded:: 0.6 + Added te *GTreeNodeBase* class + """ + + def __init__(self, parent, childs=None): + self.parent = parent + self.childs = [] + + if childs is not None: + if type(childs) != list: + Util.raiseException("Childs must be a list of nodes", TypeError) + typecheck_list = filter(lambda x: not isinstance(x, GTreeNodeBase), childs) + if len(typecheck_list) > 0: + Util.raiseException("Childs must be a list of nodes", TypeError) + self.childs += childs + + def isLeaf(self): + """ Return True if the node is a leaf + + :rtype: True or False + """ + return not len(self.childs) + + def getChild(self, index): + """ Returns the index-child of the node + + :rtype: child node + """ + return self.childs[index] + + def getChilds(self): + """ Return the childs of the node + + .. warning :: use .getChilds()[:] if you'll change the list itself, like using childs.reverse(), + otherwise the original genome child order will be changed. + :rtype: a list of nodes + """ + return self.childs + def addChild(self, child): + """ Adds a child to the node + + :param child: the node to be added + """ + if type(child) == list: + self.childs.extend(child) + else: + if not isinstance(child, GTreeNodeBase): + Util.raiseException("The child must be a node", TypeError) + self.childs.append(child) + + def replaceChild(self, older, newer): + """ Replaces a child of the node + + :param older: the child to be replaces + :param newer: the new child which replaces the older + """ + index = self.childs.index(older) + self.childs[index] = newer + + def setParent(self, parent): + """ Sets the parent of the node + + :param parent: the parent node + """ + #if not isinstance(parent, GTreeNodeBase): + # Util.raiseException("The parent must be a node", TypeError) + self.parent = parent + + def getParent(self): + """ Get the parent node of the node + + :rtype: the parent node + """ + return self.parent + + def __repr__(self): + str_repr = "GTreeNodeBase [Childs=%d]" % len(self) + return str_repr + + def __len__(self): + return len(self.childs) + + def copy(self, g): + """ Copy the current contents GTreeNodeBase to 'g' + + :param g: the destination node + + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. + """ + g.parent = self.parent + g.childs = self.childs[:] + + def clone(self): + """ Clone this GenomeBase + + :rtype: the clone genome + + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. + """ + newcopy = GTreeNodeBase(None) + self.copy(newcopy) + return newcopy + + +class GTreeBase: + """ GTreeBase Class - The base class for the tree genomes + + :param root_node: the root node of the tree + + .. versionadded:: 0.6 + Added te *GTreeBase* class + """ + + def __init__(self, root_node): + self.root_node = root_node + self.tree_height = None + self.nodes_list = None + + def processNodes(self, cloning=False): + """ Creates a *cache* on the tree, this method must be called + every time you change the shape of the tree. It updates the + internal nodes list and the internal nodes properties such as + depth and height. + """ + if self.root_node is None: + return + self.nodes_list = self.getAllNodes() + self.nodes_leaf = filter(lambda n: n.isLeaf(), self.nodes_list) + self.nodes_branch = filter(lambda n: not n.isLeaf(), self.nodes_list) + + if not cloning: + self.tree_height = self.getNodeHeight(self.getRoot()) + + def getRoot(self): + """ Return the tree root node + + :rtype: the tree root node + """ + return self.root_node + + def setRoot(self, root): + """ Sets the root of the tree + + :param root: the tree root node + """ + if not isinstance(root, GTreeNodeBase): + Util.raiseException("The root must be a node", TypeError) + self.root_node = root + + def getNodeDepth(self, node): + """ Returns the depth of a node + + :rtype: the depth of the node, the depth of root node is 0 + """ + if node == self.getRoot(): + return 0 + else: + return 1 + self.getNodeDepth(node.getParent()) + + def getNodeHeight(self, node): + """ Returns the height of a node + + .. note:: If the node has no childs, the height will be 0. + + :rtype: the height of the node + """ + height = 0 + if len(node) <= 0: + return 0 + for child in node.getChilds(): + h_inner = self.getNodeHeight(child)+1 + if h_inner > height: + height = h_inner + return height + + def getHeight(self): + """ Return the tree height + + :rtype: the tree height + """ + return self.tree_height + + def getNodesCount(self, start_node=None): + """ Return the number of the nodes on the tree + starting at the *start_node*, if *start_node* is None, + then the method will count all the tree nodes. + + :rtype: the number of nodes + """ + count = 1 + if start_node is None: + start_node = self.getRoot() + for i in start_node.getChilds(): + count += self.getNodesCount(i) + return count + + def getTraversalString(self, start_node=None, spc=0): + """ Returns a tree-formated string of the tree. This + method is used by the __repr__ method of the tree + + :rtype: a string representing the tree + """ + str_buff = "" + if start_node is None: + start_node = self.getRoot() + str_buff += "%s\n" % start_node + spaces = spc + 2 + for child_node in start_node.getChilds(): + str_buff += "%s%s\n" % (" " * spaces, child_node) + str_buff += self.getTraversalString(child_node, spaces) + return str_buff + + def traversal(self, callback, start_node=None): + """ Traversal the tree, this method will call the + user-defined callback function for each node on the tree + + :param callback: a function + :param start_node: the start node to begin the traversal + """ + if not inspect.isfunction(callback): + Util.raiseException("The callback for the tree traversal must be a function", TypeError) + + if start_node is None: + start_node = self.getRoot() + callback(start_node) + for child_node in start_node.getChilds(): + callback(child_node) + self.traversal(callback, child_node) + + def getRandomNode(self, node_type=0): + """ Returns a random node from the Tree + + :param node_type: 0 = Any, 1 = Leaf, 2 = Branch + :rtype: random node + """ + lists = (self.nodes_list, self.nodes_leaf, self.nodes_branch) + cho = lists[node_type] + if len(cho) <= 0: + return None + return rand_choice(cho) + + def getAllNodes(self): + """ Return a new list with all nodes + + :rtype: the list with all nodes + """ + node_stack = [] + all_nodes = [] + + node_stack.append(self.getRoot()) + while len(node_stack) > 0: + tmp = node_stack.pop() + all_nodes.append(tmp) + childs = tmp.getChilds() + node_stack.extend(childs) + + return all_nodes + + def __repr__(self): + str_buff = "- GTree\n" + str_buff += "\tHeight:\t\t\t%d\n" % self.getHeight() + str_buff += "\tNodes:\t\t\t%d\n" % self.getNodesCount() + str_buff += "\n" + self.getTraversalString() + + return str_buff + + def __len__(self): + return len(self.nodes_list) + + def __getitem__(self, index): + return self.nodes_list[index] + + def __iter__(self): + return iter(self.nodes_list) + + def copy(self, g, node=None, node_parent=None): + """ Copy the current contents GTreeBase to 'g' + + :param g: the destination GTreeBase tree + + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. + """ + if node is None: + g.tree_height = self.tree_height + node = self.root_node + + if node is None: + return None + + newnode = node.clone() + + if node_parent is None: + g.setRoot(newnode) + else: + newnode.setParent(node_parent) + node_parent.replaceChild(node, newnode) + + for ci in xrange(len(newnode)): + GTreeBase.copy(self, g, newnode.getChild(ci), newnode) + + return newnode + + def clone(self): + """ Clone this GenomeBase + + :rtype: the clone genome + + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. + """ + newcopy = GTreeBase(None) + self.copy(newcopy) + newcopy.processNodes() + return newcopy