diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e35e8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ + +\.tox/ + +Pyevolve\.egg-info/ + +*.pyc + +dist/ + +build/ + +\.idea/ + +\.coverage diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..79fb642 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "2.7" + - "3.5" + - "3.6" + - "pypy3.5" +install: + - pip install -r requirements_test.txt + - pip install tox-travis +script: + - tox \ No newline at end of file diff --git a/README b/README.md similarity index 58% rename from README rename to README.md index 5590741..3243121 100644 --- a/README +++ b/README.md @@ -1,3 +1,5 @@ This is the new official Pyevolve repository. The documentation (html rendered) is still hosted at sourceforge.net at http://pyevolve.sourceforge.net/0_6rc1/ + +[![Build Status](https://travis-ci.org/BubaVV/Pyevolve.svg?branch=master)](https://travis-ci.org/BubaVV/Pyevolve) diff --git a/build_dist.bat b/build_dist.bat index f969270..4b45192 100644 --- a/build_dist.bat +++ b/build_dist.bat @@ -3,6 +3,7 @@ python setup.py build python setup.py bdist_wininst --target-version="2.5" python setup.py bdist_wininst --target-version="2.6" +python setup.py bdist_wininst --target-version="3.5" python setup.py sdist --formats=gztar,zip python setup.py bdist --formats=egg diff --git a/build_dist.sh b/build_dist.sh index 256cc4e..d9bc84a 100644 --- a/build_dist.sh +++ b/build_dist.sh @@ -2,6 +2,7 @@ python setup.py build python setup.py bdist_wininst --target-version="2.5" python setup.py bdist_wininst --target-version="2.6" +python setup.py bdist_wininst --target-version="3.5" python setup.py sdist python setup.py bdist python setup.py bdist_egg diff --git a/docs/source/ext/pyevolve_tooltip.py b/docs/source/ext/pyevolve_tooltip.py index cc7198b..272e9ca 100644 --- a/docs/source/ext/pyevolve_tooltip.py +++ b/docs/source/ext/pyevolve_tooltip.py @@ -5,10 +5,10 @@ :license: PSF, see LICENSE for details. """ -from sphinx.util.compat import Directive from docutils import nodes import re + def tip_role(name, rawtext, text, lineno, inliner, options={}, content=[]): matches = re.match("\<(?P\w+)\> (?P.*)", text) matches_tuple = matches.group("tip"), matches.group("word") @@ -16,5 +16,6 @@ def tip_role(name, rawtext, text, lineno, inliner, options={}, content=[]): node = nodes.raw('', template, format='html') return [node], [] + def setup(app): - app.add_role('tip', tip_role) \ No newline at end of file + app.add_role('tip', tip_role) diff --git a/docs/source/sphinx06_code_patch.py b/docs/source/sphinx06_code_patch.py index bea6609..4b5eb5c 100644 --- a/docs/source/sphinx06_code_patch.py +++ b/docs/source/sphinx06_code_patch.py @@ -33,11 +33,12 @@ def highlightlang_directive(name, arguments, options, content, lineno, return [addnodes.highlightlang(lang=arguments[0].strip(), linenothreshold=linenothreshold)] + highlightlang_directive.content = 0 highlightlang_directive.arguments = (1, 0, 0) highlightlang_directive.options = {'linenothreshold': directives.unchanged} directives.register_directive('highlight', highlightlang_directive) -directives.register_directive('highlightlang', highlightlang_directive) # old name +directives.register_directive('highlightlang', highlightlang_directive) # old name # ------ code-block directive ------------------------------------------------------- @@ -50,6 +51,7 @@ def codeblock_directive(name, arguments, options, content, lineno, literal['linenos'] = 'linenos' in options return [literal] + codeblock_directive.content = 1 codeblock_directive.arguments = (1, 0, 0) codeblock_directive.options = {'linenos': directives.flag} @@ -59,7 +61,7 @@ def codeblock_directive(name, arguments, options, content, lineno, # ------ literalinclude directive --------------------------------------------------- -def literalinclude_directive(name, arguments, options, content, lineno, +def literalinclude_directive(name, arguments, options, content, lineno, # noqa content_offset, block_text, state, state_machine): """Like .. include:: :literal:, but only warns if the include file is not found.""" if not state.document.settings.file_insertion_enabled: @@ -99,13 +101,13 @@ def literalinclude_directive(name, arguments, options, content, lineno, 'Object named %r not found in include file %r' % (objectname, arguments[0]), line=lineno)] else: - lines = lines[tags[objectname][1] - 1 : tags[objectname][2] - 1] + lines = lines[tags[objectname][1] - 1: tags[objectname][2] - 1] linespec = options.get('lines') if linespec is not None: try: linelist = parselinenos(linespec, len(lines)) - except ValueError, err: + except ValueError as err: # TODO untested return [state.document.reporter.warning(str(err), line=lineno)] lines = [lines[i] for i in linelist] @@ -126,7 +128,7 @@ def literalinclude_directive(name, arguments, options, content, lineno, text = ''.join(lines) text = re.sub("\r\n", "\n", text) - + retnode = nodes.literal_block(text, text, source=fn) retnode.line = 1 if options.get('language', ''): @@ -136,6 +138,7 @@ def literalinclude_directive(name, arguments, options, content, lineno, state.document.settings.env.note_dependency(rel_fn) return [retnode] + literalinclude_directive.options = {'linenos': directives.flag, 'language': directives.unchanged_required, 'encoding': directives.encoding, diff --git a/examples/pyevolve_ex10_g1dbinstr.py b/examples/pyevolve_ex10_g1dbinstr.py index 4c7692c..0ec7814 100644 --- a/examples/pyevolve_ex10_g1dbinstr.py +++ b/examples/pyevolve_ex10_g1dbinstr.py @@ -3,6 +3,7 @@ from pyevolve import Selectors from pyevolve import Mutators + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(chromosome): @@ -15,6 +16,7 @@ def eval_func(chromosome): return score + def run_main(): # Genome instance genome = G1DBinaryString.G1DBinaryString(50) @@ -33,8 +35,8 @@ def run_main(): ga.evolve(freq_stats=20) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) + if __name__ == "__main__": run_main() - diff --git a/examples/pyevolve_ex11_allele.py b/examples/pyevolve_ex11_allele.py index 188df49..0f60c39 100644 --- a/examples/pyevolve_ex11_allele.py +++ b/examples/pyevolve_ex11_allele.py @@ -4,6 +4,7 @@ from pyevolve import Initializators from pyevolve import GAllele + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(chromosome): @@ -26,22 +27,23 @@ def eval_func(chromosome): return score + def run_main(): # Genome instance setOfAlleles = GAllele.GAlleles() # From 0 to 10 we can have only some # defined ranges of integers - for i in xrange(11): + for i in range(11): a = GAllele.GAlleleRange(0, i) setOfAlleles.add(a) # From 11 to 19 we can have a set # of elements - for i in xrange(11, 20): + for i in range(11, 20): # You can even add objects instead of strings or # primitive values - a = GAllele.GAlleleList(['a','b', 'xxx', 666, 0]) + a = GAllele.GAlleleList(['a', 'b', 'xxx', 666, 0]) setOfAlleles.add(a) genome = G1DList.G1DList(20) @@ -65,8 +67,8 @@ def run_main(): ga.evolve(freq_stats=5) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) if __name__ == "__main__": - run_main() \ No newline at end of file + run_main() diff --git a/examples/pyevolve_ex12_tsp.py b/examples/pyevolve_ex12_tsp.py index 8ebc45c..a572f96 100644 --- a/examples/pyevolve_ex12_tsp.py +++ b/examples/pyevolve_ex12_tsp.py @@ -1,83 +1,89 @@ -from pyevolve import G1DList, GAllele +from math import sqrt + +import os +import random + +from pyevolve import G1DList from pyevolve import GSimpleGA -from pyevolve import Mutators from pyevolve import Crossovers from pyevolve import Consts -import sys, random random.seed(1024) -from math import sqrt PIL_SUPPORT = None try: from PIL import Image, ImageDraw, ImageFont PIL_SUPPORT = True -except: +except ImportError: PIL_SUPPORT = False - -cm = [] +cm = [] coords = [] CITIES = 100 -WIDTH = 1024 -HEIGHT = 768 +WIDTH = 1024 +HEIGHT = 768 LAST_SCORE = -1 + def cartesian_matrix(coords): """ A distance matrix """ - matrix={} - for i,(x1,y1) in enumerate(coords): - for j,(x2,y2) in enumerate(coords): - dx, dy = x1-x2, y1-y2 - dist=sqrt(dx*dx + dy*dy) - matrix[i,j] = dist + matrix = {} + for i, (x1, y1) in enumerate(coords): + for j, (x2, y2) in enumerate(coords): + dx, dy = x1 - x2, y1 - y2 + dist = sqrt(dx * dx + dy * dy) + matrix[i, j] = dist return matrix + def tour_length(matrix, tour): """ Returns the total length of the tour """ total = 0 t = tour.getInternalList() for i in range(CITIES): - j = (i+1)%CITIES + j = (i + 1) % CITIES total += matrix[t[i], t[j]] return total + def write_tour_to_img(coords, tour, img_file): """ The function to plot the graph """ - padding=20 - coords=[(x+padding,y+padding) for (x,y) in coords] - maxx,maxy=0,0 - for x,y in coords: - maxx, maxy = max(x,maxx), max(y,maxy) - maxx+=padding - maxy+=padding - img=Image.new("RGB",(int(maxx),int(maxy)),color=(255,255,255)) - font=ImageFont.load_default() - d=ImageDraw.Draw(img); - num_cities=len(tour) + padding = 20 + coords = [(x + padding, y + padding) for (x, y) in coords] + maxx, maxy = 0, 0 + for x, y in coords: + maxx, maxy = max(x, maxx), max(y, maxy) + maxx += padding + maxy += padding + img = Image.new("RGB", (int(maxx), int(maxy)), color=(255, 255, 255)) + font = ImageFont.load_default() + d = ImageDraw.Draw(img) + num_cities = len(tour) for i in range(num_cities): - j=(i+1)%num_cities - city_i=tour[i] - city_j=tour[j] - x1,y1=coords[city_i] - x2,y2=coords[city_j] - d.line((int(x1),int(y1),int(x2),int(y2)),fill=(0,0,0)) - d.text((int(x1)+7,int(y1)-5),str(i),font=font,fill=(32,32,32)) - - for x,y in coords: - x,y=int(x),int(y) - d.ellipse((x-5,y-5,x+5,y+5),outline=(0,0,0),fill=(196,196,196)) + j = (i + 1) % num_cities + city_i = tour[i] + city_j = tour[j] + x1, y1 = coords[city_i] + x2, y2 = coords[city_j] + d.line((int(x1), int(y1), int(x2), int(y2)), fill=(0, 0, 0)) + d.text((int(x1) + 7, int(y1) - 5), str(i), font=font, fill=(32, 32, 32)) + + for x, y in coords: + x, y = int(x), int(y) + d.ellipse((x - 5, y - 5, x + 5, y + 5), outline=(0, 0, 0), fill=(196, 196, 196)) del d img.save(img_file, "PNG") - print "The plot was saved into the %s file." % (img_file,) + print("The plot was saved into the %s file." % (img_file,)) + def G1DListTSPInitializator(genome, **args): """ The initializator for the TSP """ - lst = [i for i in xrange(genome.getListSize())] + lst = [i for i in range(genome.getListSize())] random.shuffle(lst) genome.setInternalList(lst) + # This is to make a video of best individuals along the evolution # Use mencoder to create a video with the file list list.txt # mencoder mf://@list.txt -mf w=400:h=200:fps=3:type=png -ovc lavc @@ -85,19 +91,24 @@ def G1DListTSPInitializator(genome, **args): # def evolve_callback(ga_engine): global LAST_SCORE + try: + os.makedirs('tspimg') + except OSError: + pass if ga_engine.getCurrentGeneration() % 100 == 0: best = ga_engine.bestIndividual() if LAST_SCORE != best.getRawScore(): - write_tour_to_img( coords, best, "tspimg/tsp_result_%d.png" % ga_engine.getCurrentGeneration()) + write_tour_to_img(coords, best, "tspimg/tsp_result_%d.png" % ga_engine.getCurrentGeneration()) LAST_SCORE = best.getRawScore() return False + def main_run(): global cm, coords, WIDTH, HEIGHT coords = [(random.randint(0, WIDTH), random.randint(0, HEIGHT)) - for i in xrange(CITIES)] - cm = cartesian_matrix(coords) + for i in range(CITIES)] + cm = cartesian_matrix(coords) genome = G1DList.G1DList(len(coords)) genome.evaluator.set(lambda chromosome: tour_length(cm, chromosome)) @@ -113,10 +124,9 @@ def main_run(): ga.setPopulationSize(80) # This is to make a video - ga.stepCallback.set(evolve_callback) + if PIL_SUPPORT: + ga.stepCallback.set(evolve_callback) # 21666.49 - import psyco - psyco.full() ga.evolve(freq_stats=500) best = ga.bestIndividual() @@ -124,7 +134,8 @@ def main_run(): if PIL_SUPPORT: write_tour_to_img(coords, best, "tsp_result.png") else: - print "No PIL detected, cannot plot the graph !" + print("No PIL detected, cannot plot the graph !") + if __name__ == "__main__": main_run() diff --git a/examples/pyevolve_ex13_sphere.py b/examples/pyevolve_ex13_sphere.py index 475f608..2bcd8fb 100644 --- a/examples/pyevolve_ex13_sphere.py +++ b/examples/pyevolve_ex13_sphere.py @@ -2,6 +2,7 @@ from pyevolve import Mutators, Initializators from pyevolve import GSimpleGA, Consts + # This is the Sphere Function def sphere(xlist): total = 0 @@ -9,6 +10,7 @@ def sphere(xlist): total += i**2 return total + def run_main(): genome = G1DList.G1DList(140) genome.setParams(rangemin=-5.12, rangemax=5.13) @@ -22,9 +24,6 @@ def run_main(): ga.setMutationRate(0.01) ga.evolve(freq_stats=500) - best = ga.bestIndividual() if __name__ == "__main__": run_main() - - diff --git a/examples/pyevolve_ex14_ackley.py b/examples/pyevolve_ex14_ackley.py index 6ce2063..115bf1a 100644 --- a/examples/pyevolve_ex14_ackley.py +++ b/examples/pyevolve_ex14_ackley.py @@ -1,21 +1,22 @@ -from pyevolve import G1DList, GSimpleGA, Selectors -from pyevolve import Initializators, Mutators, Consts, DBAdapters +from pyevolve import G1DList, GSimpleGA +from pyevolve import Initializators, Mutators, Consts import math + # This is the Rastringin Function, a deception function def ackley(xlist): sum1 = 0 score = 0 n = len(xlist) - for i in xrange(n): - sum1 += xlist[i]*xlist[i] - t1 = math.exp(-0.2*(math.sqrt((1.0/5.0)*sum1))) + for i in range(n): + sum1 += xlist[i] * xlist[i] + t1 = math.exp(-0.2 * (math.sqrt((1.0 / 5.0) * sum1))) sum1 = 0 - for i in xrange(n): - sum1 += math.cos(2.0*math.pi*xlist[i]); - t2 = math.exp((1.0/5.0)*sum1); - score = 20 + math.exp(1) - 20 * t1 - t2; + for i in range(n): + sum1 += math.cos(2.0 * math.pi * xlist[i]) + t2 = math.exp((1.0 / 5.0) * sum1) + score = 20 + math.exp(1) - 20 * t1 - t2 return score @@ -23,7 +24,7 @@ def ackley(xlist): def run_main(): # Genome instance genome = G1DList.G1DList(5) - genome.setParams(rangemin=-8, rangemax=8, bestrawscore=0.00, rounddecimal=2) + genome.setParams(rangemin=-8, rangemax=8, bestrawscore=0.00, rounddecimal=2) genome.initializator.set(Initializators.G1DListInitializatorReal) genome.mutator.set(Mutators.G1DListMutatorRealGaussian) @@ -47,8 +48,9 @@ def run_main(): # Best individual best = ga.bestIndividual() - print "\nBest individual score: %.2f" % (best.getRawScore(),) - print best + print("\nBest individual score: %.2f" % (best.getRawScore(),)) + print(best) + if __name__ == "__main__": run_main() diff --git a/examples/pyevolve_ex15_rosenbrock.py b/examples/pyevolve_ex15_rosenbrock.py index 1db3564..726bcc6 100644 --- a/examples/pyevolve_ex15_rosenbrock.py +++ b/examples/pyevolve_ex15_rosenbrock.py @@ -1,13 +1,15 @@ -from pyevolve import G1DList, GSimpleGA, Selectors, Statistics -from pyevolve import Initializators, Mutators, Consts, DBAdapters +from pyevolve import G1DList, GSimpleGA, Selectors +from pyevolve import Initializators, Mutators, Consts + # This is the Rosenbrock Function def rosenbrock(xlist): sum_var = 0 - for x in xrange(1, len(xlist)): - sum_var += 100.0 * (xlist[x] - xlist[x-1]**2)**2 + (1 - xlist[x-1])**2 + for x in range(1, len(xlist)): + sum_var += 100.0 * (xlist[x] - xlist[x - 1]**2)**2 + (1 - xlist[x - 1])**2 return sum_var + def run_main(): # Genome instance genome = G1DList.G1DList(15) @@ -31,22 +33,9 @@ def run_main(): # Best individual best = ga.bestIndividual() - print "\nBest individual score: %.2f" % (best.score,) - print best + print("\nBest individual score: %.2f" % (best.score,)) + print(best) if __name__ == "__main__": run_main() - - - - - - - - - - - - - diff --git a/examples/pyevolve_ex16_g2dbinstr.py b/examples/pyevolve_ex16_g2dbinstr.py index 6aa0038..e904b6d 100644 --- a/examples/pyevolve_ex16_g2dbinstr.py +++ b/examples/pyevolve_ex16_g2dbinstr.py @@ -1,22 +1,23 @@ from pyevolve import G2DBinaryString from pyevolve import GSimpleGA -from pyevolve import Selectors from pyevolve import Crossovers from pyevolve import Mutators + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(chromosome): score = 0.0 # iterate over the chromosome - for i in xrange(chromosome.getHeight()): - for j in xrange(chromosome.getWidth()): + for i in range(chromosome.getHeight()): + for j in range(chromosome.getWidth()): # You can use the chromosome.getItem(i, j) - if chromosome[i][j]==0: + if chromosome[i][j] == 0: score += 0.1 return score + # Genome instance genome = G2DBinaryString.G2DBinaryString(8, 5) @@ -34,4 +35,4 @@ def eval_func(chromosome): ga.evolve(freq_stats=10) # Best individual -print ga.bestIndividual() +print(ga.bestIndividual()) diff --git a/examples/pyevolve_ex17_gtree.py b/examples/pyevolve_ex17_gtree.py index 691f432..2e5fdd1 100644 --- a/examples/pyevolve_ex17_gtree.py +++ b/examples/pyevolve_ex17_gtree.py @@ -1,9 +1,7 @@ from pyevolve import GSimpleGA from pyevolve import GTree from pyevolve import Crossovers -from pyevolve import Mutators -import time -import random + def eval_func(chromosome): score = 0.0 @@ -11,16 +9,17 @@ def eval_func(chromosome): # in the height of the Tree, the extra # code is commented. - #height = chromosome.getHeight() + # height = chromosome.getHeight() for node in chromosome: - score += (100 - node.getData())*0.1 + score += (100 - node.getData()) * 0.1 - #if height <= chromosome.getParam("max_depth"): + # if height <= chromosome.getParam("max_depth"): # score += (score*0.8) return score + def run_main(): genome = GTree.GTree() root = GTree.GTreeNode(2) @@ -36,9 +35,8 @@ def run_main(): ga.setMutationRate(0.05) ga.evolve(freq_stats=10) - print ga.bestIndividual() + print(ga.bestIndividual()) + if __name__ == "__main__": run_main() - - diff --git a/examples/pyevolve_ex18_gp.py b/examples/pyevolve_ex18_gp.py index 5792450..26ca8da 100644 --- a/examples/pyevolve_ex18_gp.py +++ b/examples/pyevolve_ex18_gp.py @@ -1,37 +1,49 @@ -from pyevolve import Util -from pyevolve import GTree -from pyevolve import GSimpleGA from pyevolve import Consts +from pyevolve import GSimpleGA +from pyevolve import GTree +from pyevolve import Util import math rmse_accum = Util.ErrorAccumulator() -def gp_add(a, b): return a+b -def gp_sub(a, b): return a-b -def gp_mul(a, b): return a*b -def gp_sqrt(a): return math.sqrt(abs(a)) + +def gp_add(a, b): + return a + b + + +def gp_sub(a, b): + return a - b + + +def gp_mul(a, b): + return a * b + + +def gp_sqrt(a): + return math.sqrt(abs(a)) + def eval_func(chromosome): global rmse_accum rmse_accum.reset() code_comp = chromosome.getCompiledCode() - for a in xrange(0, 5): - for b in xrange(0, 5): - evaluated = eval(code_comp) - target = math.sqrt((a*a)+(b*b)) - rmse_accum += (target, evaluated) + for a in range(0, 5): + for b in range(0, 5): + evaluated = eval(code_comp) + target = math.sqrt((a * a) + (b * b)) + rmse_accum += (target, evaluated) return rmse_accum.getRMSE() + def main_run(): genome = GTree.GTreeGP() genome.setParams(max_depth=4, method="ramped") genome.evaluator += eval_func ga = GSimpleGA.GSimpleGA(genome) - ga.setParams(gp_terminals = ['a', 'b'], - gp_function_prefix = "gp") + ga.setParams(gp_terminals=['a', 'b'], gp_function_prefix="gp") ga.setMinimax(Consts.minimaxType["minimize"]) ga.setGenerations(50) @@ -41,7 +53,8 @@ def main_run(): ga(freq_stats=10) best = ga.bestIndividual() - print best + print(best) + if __name__ == "__main__": main_run() diff --git a/examples/pyevolve_ex19_gp.py b/examples/pyevolve_ex19_gp.py index c5adb93..fefc4e2 100644 --- a/examples/pyevolve_ex19_gp.py +++ b/examples/pyevolve_ex19_gp.py @@ -1,63 +1,67 @@ from pyevolve import GSimpleGA from pyevolve import GTree from pyevolve import Consts -from pyevolve import Selectors from pyevolve import Mutators from math import sqrt -import pydot +import pydot_ng as pydot import random + def gp_add(a, b): - assert len(a)==len(b) - new_list = [x+y for x,y in zip(a,b)] + assert len(a) == len(b) + new_list = [x + y for x, y in zip(a, b)] return new_list -#def gp_sub(a, b): +# def gp_sub(a, b): # assert len(a)==len(b) # new_list = [x-y for x,y in zip(a,b)] # return new_list + def prot_div(a, b): - if b==0: + if b == 0: return b else: - return a/b + return a / b -#def gp_div(a,b): +# def gp_div(a,b): # assert len(a)==len(b) # new_list = [prot_div(x,float(y)) for x,y in zip(a,b)] # return new_list -def gp_mul(a,b): - assert len(a)==len(b) - new_list = [x*y for x,y in zip(a,b)] + +def gp_mul(a, b): + assert len(a) == len(b) + new_list = [x * y for x, y in zip(a, b)] return new_list + def random_lists(size): - list_a = [random.randint(1,20) for i in xrange(size)] - list_b = [random.randint(1,20) for i in xrange(size)] + list_a = [random.randint(1, 20) for i in range(size)] + list_b = [random.randint(1, 20) for i in range(size)] - return (list_a, list_b) + return list_a, list_b def eval_func(chromosome): sz = 20 - code_comp = chromosome.getCompiledCode() - square_accum = 0.0 + code_comp = chromosome.getCompiledCode() + square_accum = 0.0 - for j in xrange(sz): + for j in range(sz): a, b = random_lists(5) - target_list = gp_add(gp_mul(a,b),gp_mul(a,b)) - ret_list = eval(code_comp) - square_accum += (sum(target_list)-sum(ret_list))**2 + target_list = gp_add(gp_mul(a, b), gp_mul(a, b)) + ret_list = eval(code_comp) + square_accum += (sum(target_list) - sum(ret_list))**2 RMSE = sqrt(square_accum / float(sz)) - score = (1.0 / (RMSE+1.0)) + score = (1.0 / (RMSE + 1.0)) return score + def main_run(): genome = GTree.GTreeGP() - root = GTree.GTreeNodeGP('a', Consts.nodeType["TERMINAL"]) + root = GTree.GTreeNodeGP('a', Consts.nodeType["TERMINAL"]) genome.setRoot(root) genome.setParams(max_depth=2, method="ramped") @@ -65,8 +69,7 @@ def main_run(): genome.mutator.set(Mutators.GTreeGPMutatorSubtree) ga = GSimpleGA.GSimpleGA(genome) - ga.setParams(gp_terminals = ['a', 'b'], - gp_function_prefix = "gp") + ga.setParams(gp_terminals=['a', 'b'], gp_function_prefix="gp") ga.setMinimax(Consts.minimaxType["maximize"]) ga.setGenerations(500) @@ -75,20 +78,23 @@ def main_run(): ga.setPopulationSize(80) ga(freq_stats=1) - print ga.bestIndividual() + print(ga.bestIndividual()) graph = pydot.Dot() - ga.bestIndividual().writeDotGraph(graph) - graph.write_jpeg('tree.png', prog='dot') + try: + ga.bestIndividual().writeDotGraph(graph) + graph.write_jpeg('tree.png', prog='dot') + except pydot_ng.InvocationException: # TODO We need to detect pydot presense # noqa + print('Graphviz not installed') + if __name__ == "__main__": main_run() - #import hotshot, hotshot.stats - #prof = hotshot.Profile("ev.prof") - #prof.runcall(main_run) - #prof.close() - #stats = hotshot.stats.load("ev.prof") - #stats.strip_dirs() - #stats.sort_stats('time', 'calls') - #stats.print_stats(20) - + # import hotshot, hotshot.stats + # prof = hotshot.Profile("ev.prof") + # prof.runcall(main_run) + # prof.close() + # stats = hotshot.stats.load("ev.prof") + # stats.strip_dirs() + # stats.sort_stats('time', 'calls') + # stats.print_stats(20) diff --git a/examples/pyevolve_ex1_simple.py b/examples/pyevolve_ex1_simple.py index 845dd17..0f5e8c5 100644 --- a/examples/pyevolve_ex1_simple.py +++ b/examples/pyevolve_ex1_simple.py @@ -1,9 +1,9 @@ from pyevolve import G1DList from pyevolve import GSimpleGA from pyevolve import Selectors -from pyevolve import Statistics from pyevolve import DBAdapters + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(genome): @@ -12,11 +12,12 @@ def eval_func(genome): # iterate over the chromosome # The same as "score = len(filter(lambda x: x==0, genome))" for value in genome: - if value==0: + if value == 0: score += 1 return score + def run_main(): # Genome instance, 1D List of 50 elements genome = G1DList.G1DList(50) @@ -48,7 +49,8 @@ def run_main(): ga.evolve(freq_stats=20) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) + if __name__ == "__main__": run_main() diff --git a/examples/pyevolve_ex20_gp_dotwrite.py b/examples/pyevolve_ex20_gp_dotwrite.py index ae016ab..5e97778 100644 --- a/examples/pyevolve_ex20_gp_dotwrite.py +++ b/examples/pyevolve_ex20_gp_dotwrite.py @@ -1,23 +1,41 @@ -from pyevolve import * import math +from pyevolve import Consts +from pyevolve import GSimpleGA +from pyevolve import GTree +from pyevolve import Mutators +from pyevolve import Util + + rmse_accum = Util.ErrorAccumulator() -def gp_add(a, b): return a+b -def gp_sub(a, b): return a-b -def gp_mul(a, b): return a*b -def gp_sqrt(a): return math.sqrt(abs(a)) + +def gp_add(a, b): + return a + b + + +def gp_sub(a, b): + return a - b + + +def gp_mul(a, b): + return a * b + + +def gp_sqrt(a): + return math.sqrt(abs(a)) + def eval_func(chromosome): global rmse_accum rmse_accum.reset() code_comp = chromosome.getCompiledCode() - for a in xrange(0, 5): - for b in xrange(0, 5): - evaluated = eval(code_comp) - target = math.sqrt((a*a)+(b*b)) - rmse_accum += (target, evaluated) + for a in range(0, 5): + for b in range(0, 5): + evaluated = eval(code_comp) + target = math.sqrt((a * a) + (b * b)) + rmse_accum += (target, evaluated) return rmse_accum.getRMSE() @@ -35,8 +53,7 @@ def main_run(): ga = GSimpleGA.GSimpleGA(genome, seed=666) ga.stepCallback.set(step_callback) - ga.setParams(gp_terminals = ['a', 'b'], - gp_function_prefix = "gp") + ga.setParams(gp_terminals=['a', 'b'], gp_function_prefix="gp") ga.setMinimax(Consts.minimaxType["minimize"]) ga.setGenerations(2) @@ -47,9 +64,7 @@ def main_run(): ga(freq_stats=5) - #GTree.GTreeGP.writePopulationDotRaw(ga, "pop.dot", 0, 14) - - best = ga.bestIndividual() + # GTree.GTreeGP.writePopulationDotRaw(ga, "pop.dot", 0, 14) if __name__ == "__main__": diff --git a/examples/pyevolve_ex21_nqueens.py b/examples/pyevolve_ex21_nqueens.py index 827ba14..7252a34 100644 --- a/examples/pyevolve_ex21_nqueens.py +++ b/examples/pyevolve_ex21_nqueens.py @@ -7,23 +7,28 @@ # The "n" in n-queens BOARD_SIZE = 64 + # The n-queens fitness function def queens_eval(genome): collisions = 0 - for i in xrange(0, BOARD_SIZE): - if i not in genome: return 0 - for i in xrange(0, BOARD_SIZE): + for i in range(0, BOARD_SIZE): + if i not in genome: + return 0 + for i in range(0, BOARD_SIZE): col = False - for j in xrange(0, BOARD_SIZE): - if (i != j) and (abs(i-j) == abs(genome[j]-genome[i])): + for j in range(0, BOARD_SIZE): + if (i != j) and (abs(i - j) == abs(genome[j] - genome[i])): col = True - if col == True: collisions +=1 - return BOARD_SIZE-collisions + if col: + collisions += 1 + return BOARD_SIZE - collisions + -def queens_init(genome, **args): - genome.genomeList = range(0, BOARD_SIZE) +def queens_init(genome, **kwargs): + genome.genomeList = list(range(0, BOARD_SIZE)) shuffle(genome.genomeList) + def run_main(): genome = G1DList.G1DList(BOARD_SIZE) genome.setParams(bestrawscore=BOARD_SIZE, rounddecimal=2) @@ -41,8 +46,8 @@ def run_main(): ga.setMutationRate(0.02) ga.setCrossoverRate(1.0) - #sqlite_adapter = DBAdapters.DBSQLite(identify="queens") - #ga.setDBAdapter(sqlite_adapter) + # sqlite_adapter = DBAdapters.DBSQLite(identify="queens") + # ga.setDBAdapter(sqlite_adapter) vpython_adapter = DBAdapters.DBVPythonGraph(identify="queens", frequency=1) ga.setDBAdapter(vpython_adapter) @@ -50,9 +55,9 @@ def run_main(): ga.evolve(freq_stats=10) best = ga.bestIndividual() - print best - print "Best individual score: %.2f\n" % (best.getRawScore(),) + print(best) + print("Best individual score: %.2f\n" % (best.getRawScore(),)) + if __name__ == "__main__": run_main() - diff --git a/examples/pyevolve_ex22_monkey.py b/examples/pyevolve_ex22_monkey.py index 50bb990..d5fcf61 100644 --- a/examples/pyevolve_ex22_monkey.py +++ b/examples/pyevolve_ex22_monkey.py @@ -1,28 +1,28 @@ -#=============================================================================== +# =============================================================================== # Pyevolve version of the Infinite Monkey Theorem # See: http://en.wikipedia.org/wiki/Infinite_monkey_theorem # By Jelle Feringa -#=============================================================================== +# =============================================================================== from pyevolve import G1DList from pyevolve import GSimpleGA, Consts -from pyevolve import Selectors -from pyevolve import Initializators, Mutators, Crossovers -import math +from pyevolve import Initializators, Mutators sentence = """ 'Just living is not enough,' said the butterfly, 'one must have sunshine, freedom, and a little flower.' """ -numeric_sentence = map(ord, sentence) +numeric_sentence = list(map(ord, sentence)) + def evolve_callback(ga_engine): generation = ga_engine.getCurrentGeneration() - if generation%50==0: + if generation % 50 == 0: indiv = ga_engine.bestIndividual() - print ''.join(map(chr,indiv)) + print(''.join(map(chr, indiv))) return False + def run_main(): genome = G1DList.G1DList(len(sentence)) genome.setParams(rangemin=min(numeric_sentence), @@ -33,11 +33,11 @@ def run_main(): genome.initializator.set(Initializators.G1DListInitializatorInteger) genome.mutator.set(Mutators.G1DListMutatorIntegerGaussian) genome.evaluator.set(lambda genome: sum( - [abs(a-b) for a, b in zip(genome, numeric_sentence)] + [abs(a - b) for a, b in zip(genome, numeric_sentence)] )) ga = GSimpleGA.GSimpleGA(genome) - #ga.stepCallback.set(evolve_callback) + # ga.stepCallback.set(evolve_callback) ga.setMinimax(Consts.minimaxType["minimize"]) ga.terminationCriteria.set(GSimpleGA.RawScoreCriteria) ga.setPopulationSize(60) @@ -47,8 +47,9 @@ def run_main(): ga.evolve(freq_stats=100) best = ga.bestIndividual() - print "Best individual score: %.2f" % (best.score,) - print ''.join(map(chr, best)) + print("Best individual score: %.2f" % (best.score,)) + print(''.join(map(chr, best))) + if __name__ == "__main__": run_main() diff --git a/examples/pyevolve_ex2_realgauss.py b/examples/pyevolve_ex2_realgauss.py index 29bf991..7cfb3b6 100644 --- a/examples/pyevolve_ex2_realgauss.py +++ b/examples/pyevolve_ex2_realgauss.py @@ -3,15 +3,18 @@ from pyevolve import Selectors from pyevolve import Initializators, Mutators + # Find negative element def eval_func(genome): score = 0.0 for element in genome: - if element < 0: score += 0.1 + if element < 0: + score += 0.1 return score + def run_main(): # Genome instance genome = G1DList.G1DList(20) @@ -35,8 +38,8 @@ def run_main(): ga.evolve(freq_stats=10) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) + if __name__ == "__main__": run_main() - diff --git a/examples/pyevolve_ex3_schaffer.py b/examples/pyevolve_ex3_schaffer.py index 46fd99e..59be0c6 100644 --- a/examples/pyevolve_ex3_schaffer.py +++ b/examples/pyevolve_ex3_schaffer.py @@ -2,17 +2,19 @@ from pyevolve import Initializators, Mutators, Consts import math + # This is the Schaffer F6 Function -# This function has been conceived by Schaffer, it's a +# This function has been conceived by Schaffer, it's a # multimodal function and it's hard for GAs due to the # large number of local minima, the global minimum is # at x=0,y=0 and there are many local minima around it def schafferF6(genome): - t1 = math.sin(math.sqrt(genome[0]**2 + genome[1]**2)); - t2 = 1.0 + 0.001*(genome[0]**2 + genome[1]**2); - score = 0.5 + (t1*t1 - 0.5)/(t2*t2) + t1 = math.sin(math.sqrt(genome[0]**2 + genome[1]**2)) + t2 = 1.0 + 0.001 * (genome[0]**2 + genome[1]**2) + score = 0.5 + (t1 * t1 - 0.5) / (t2 * t2) return score + def run_main(): # Genome instance genome = G1DList.G1DList(2) @@ -39,8 +41,9 @@ def run_main(): # Best individual best = ga.bestIndividual() - print best - print "Best individual score: %.2f" % best.getRawScore() + print(best) + print("Best individual score: %.2f" % best.getRawScore()) + if __name__ == "__main__": run_main() diff --git a/examples/pyevolve_ex4_sigmatrunc.py b/examples/pyevolve_ex4_sigmatrunc.py index e753421..4e50104 100644 --- a/examples/pyevolve_ex4_sigmatrunc.py +++ b/examples/pyevolve_ex4_sigmatrunc.py @@ -6,12 +6,13 @@ from pyevolve import Consts import math + def eval_func(ind): - score = 0.0 var_x = ind[0] - var_z = var_x**2+2*var_x+1*math.cos(var_x) + var_z = var_x**2 + 2 * var_x + 1 * math.cos(var_x) return var_z + def run_main(): # Genome instance genome = G1DList.G1DList(1) @@ -43,7 +44,8 @@ def run_main(): ga.evolve(10) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) + if __name__ == "__main__": run_main() diff --git a/examples/pyevolve_ex5_callback.py b/examples/pyevolve_ex5_callback.py index d9b8e65..3a04ede 100644 --- a/examples/pyevolve_ex5_callback.py +++ b/examples/pyevolve_ex5_callback.py @@ -2,24 +2,28 @@ from pyevolve import GSimpleGA from pyevolve import Selectors + # The step callback function, this function # will be called every step (generation) of the GA evolution def evolve_callback(ga_engine): generation = ga_engine.getCurrentGeneration() if generation % 100 == 0: - print "Current generation: %d" % (generation,) - print ga_engine.getStatistics() + print("Current generation: %d" % (generation,)) + print(ga_engine.getStatistics()) return False + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(genome): score = 0.0 # iterate over the chromosome for value in genome: - if value==0: score += 0.1 + if value == 0: + score += 0.1 return score + def run_main(): # Genome instance genome = G1DList.G1DList(200) @@ -38,8 +42,8 @@ def run_main(): ga.evolve() # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) + if __name__ == "__main__": run_main() - diff --git a/examples/pyevolve_ex6_dbadapter.py b/examples/pyevolve_ex6_dbadapter.py index 1c155bf..c44204c 100644 --- a/examples/pyevolve_ex6_dbadapter.py +++ b/examples/pyevolve_ex6_dbadapter.py @@ -1,8 +1,9 @@ from pyevolve import G1DList from pyevolve import GSimpleGA -from pyevolve import Selectors -from pyevolve import DBAdapters -from pyevolve import Statistics +# from pyevolve import Selectors +# from pyevolve import DBAdapters +# from pyevolve import Statistics + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes @@ -11,10 +12,11 @@ def eval_func(chromosome): # iterate over the chromosome for value in chromosome: - if value==0: + if value == 0: score += 0.5 return score + # Genome instance genome = G1DList.G1DList(100) genome.setParams(rangemin=0, rangemax=10) @@ -28,12 +30,12 @@ def eval_func(chromosome): ga.setMutationRate(0.2) # Create DB Adapter and set as adapter -#sqlite_adapter = DBAdapters.DBSQLite(identify="ex6", resetDB=True) -#ga.setDBAdapter(sqlite_adapter) +#sqlite_adapter = DBAdapters.DBSQLite(identify="ex6", resetDB=True) # noqa +#ga.setDBAdapter(sqlite_adapter) # noqa # Using CSV Adapter -#csvfile_adapter = DBAdapters.DBFileCSV() -#ga.setDBAdapter(csvfile_adapter) +#csvfile_adapter = DBAdapters.DBFileCSV() # noqa +#ga.setDBAdapter(csvfile_adapter) # noqa # Using the URL Post Adapter # urlpost_adapter = DBAdapters.DBURLPost(url="http://whatismyip.oceanus.ro/server_variables.php", post=False) @@ -44,4 +46,4 @@ def eval_func(chromosome): ga.evolve(freq_stats=10) # Best individual -#print ga.bestIndividual() +#print ga.bestIndividual() # noqa diff --git a/examples/pyevolve_ex7_rastrigin.py b/examples/pyevolve_ex7_rastrigin.py index 46c8f94..efc26dc 100644 --- a/examples/pyevolve_ex7_rastrigin.py +++ b/examples/pyevolve_ex7_rastrigin.py @@ -1,17 +1,18 @@ from pyevolve import GSimpleGA from pyevolve import G1DList from pyevolve import Mutators, Initializators -from pyevolve import Selectors from pyevolve import Consts import math + # This is the Rastrigin Function, a deception function def rastrigin(genome): n = len(genome) total = 0 - for i in xrange(n): - total += genome[i]**2 - 10*math.cos(2*math.pi*genome[i]) - return (10*n) + total + for i in range(n): + total += genome[i]**2 - 10 * math.cos(2 * math.pi * genome[i]) + return (10 * n) + total + def run_main(): # Genome instance @@ -34,7 +35,8 @@ def run_main(): ga.evolve(freq_stats=50) best = ga.bestIndividual() - print best + print(best) + if __name__ == "__main__": run_main() diff --git a/examples/pyevolve_ex8_gauss_int.py b/examples/pyevolve_ex8_gauss_int.py index 793f55b..8c91a74 100644 --- a/examples/pyevolve_ex8_gauss_int.py +++ b/examples/pyevolve_ex8_gauss_int.py @@ -1,8 +1,9 @@ from pyevolve import G1DList from pyevolve import GSimpleGA -from pyevolve import Selectors +# from pyevolve import Selectors from pyevolve import Mutators + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(chromosome): @@ -10,7 +11,7 @@ def eval_func(chromosome): # iterate over the chromosome for value in chromosome: - if value==0: + if value == 0: score += 0.1 return score @@ -29,7 +30,7 @@ def run_main(): # Genetic Algorithm Instance ga = GSimpleGA.GSimpleGA(genome) - #ga.selector.set(Selectors.GRouletteWheel) + # ga.selector.set(Selectors.GRouletteWheel) ga.setGenerations(800) # Do the evolution, with stats dump @@ -37,7 +38,7 @@ def run_main(): ga.evolve(freq_stats=150) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) if __name__ == "__main__": diff --git a/examples/pyevolve_ex9_g2dlist.py b/examples/pyevolve_ex9_g2dlist.py index b1d2276..01d40b4 100644 --- a/examples/pyevolve_ex9_g2dlist.py +++ b/examples/pyevolve_ex9_g2dlist.py @@ -1,22 +1,23 @@ from pyevolve import G2DList from pyevolve import GSimpleGA -from pyevolve import Selectors from pyevolve import Crossovers from pyevolve import Mutators + # This function is the evaluation function, we want # to give high score to more zero'ed chromosomes def eval_func(chromosome): score = 0.0 # iterate over the chromosome - for i in xrange(chromosome.getHeight()): - for j in xrange(chromosome.getWidth()): + for i in range(chromosome.getHeight()): + for j in range(chromosome.getWidth()): # You can use the chromosome.getItem(i, j) too - if chromosome[i][j]==0: + if chromosome[i][j] == 0: score += 0.1 return score + def run_main(): # Genome instance genome = G2DList.G2DList(8, 5) @@ -36,7 +37,7 @@ def run_main(): ga.evolve(freq_stats=100) # Best individual - print ga.bestIndividual() + print(ga.bestIndividual()) if __name__ == "__main__": diff --git a/pyevolve/Consts.py b/pyevolve/Consts.py index f1442cd..8ad2c77 100644 --- a/pyevolve/Consts.py +++ b/pyevolve/Consts.py @@ -3,7 +3,9 @@ :mod:`Consts` -- constants module ============================================================================ -Pyevolve have defaults in all genetic operators, settings and etc, this is an issue to helps the user in the API use and minimize the source code needed to make simple things. In the module :mod:`Consts`, you will find those defaults settings. You are encouraged to see the constants, but not to change directly on the module, there are methods for this. +Pyevolve have defaults in all genetic operators, settings and etc, this is an issue to helps the user in the API use +and minimize the source code needed to make simple things. In the module :mod:`Consts`, you will find those defaults +settings. You are encouraged to see the constants, but not to change directly on the module, there are methods for this. General constants ---------------------------------------------------------------------------- @@ -135,7 +137,8 @@ .. attribute:: CDefG1DBinaryStringUniformProb - The default uniform probability used for some uniform genetic operators for the 1D Binary String (:class:`G1DBinaryString.G1DBinaryString`) chromosome. + The default uniform probability used for some uniform genetic operators for the + 1D Binary String (:class:`G1DBinaryString.G1DBinaryString`) chromosome. 2D Binary String Defaults (:class:`G2DBinaryString.G2DBinaryString`) @@ -155,7 +158,8 @@ .. attribute:: CDefG2DBinaryStringUniformProb - The default uniform probability used for some uniform genetic operators for the 2D Binary String (:class:`G2DBinaryString.G2DBinaryString`) chromosome. + The default uniform probability used for some uniform genetic operators for the + 2D Binary String (:class:`G2DBinaryString.G2DBinaryString`) chromosome. 1D List chromosome constants (:class:`G1DList.G1DList`) @@ -163,19 +167,23 @@ .. attribute:: CDefG1DListMutIntMU - Default *mu* value of the 1D List Gaussian Integer Mutator (:func:`Mutators.G1DListMutatorIntegerGaussian`), the *mu* represents the mean of the distribution. + Default *mu* value of the 1D List Gaussian Integer Mutator (:func:`Mutators.G1DListMutatorIntegerGaussian`), + the *mu* represents the mean of the distribution. .. attribute:: CDefG1DListMutIntSIGMA - Default *sigma* value of the 1D List Gaussian Integer Mutator (:func:`Mutators.G1DListMutatorIntegerGaussian`), the *sigma* represents the standard deviation of the distribution. + Default *sigma* value of the 1D List Gaussian Integer Mutator (:func:`Mutators.G1DListMutatorIntegerGaussian`), + the *sigma* represents the standard deviation of the distribution. .. attribute:: CDefG1DListMutRealMU - Default *mu* value of the 1D List Gaussian Real Mutator (:func:`Mutators.G1DListMutatorRealGaussian`), the *mu* represents the mean of the distribution. + Default *mu* value of the 1D List Gaussian Real Mutator (:func:`Mutators.G1DListMutatorRealGaussian`), + the *mu* represents the mean of the distribution. .. attribute:: CDefG1DListMutRealSIGMA - Default *sigma* value of the 1D List Gaussian Real Mutator (:func:`Mutators.G1DListMutatorRealGaussian`), the *sigma* represents the mean of the distribution. + Default *sigma* value of the 1D List Gaussian Real Mutator (:func:`Mutators.G1DListMutatorRealGaussian`), + the *sigma* represents the mean of the distribution. Tree chromosome constants (:class:`GTree.GTree`) @@ -199,19 +207,23 @@ .. attribute:: CDefG2DListMutRealMU - Default *mu* value of the 2D List Gaussian Real Mutator (:func:`Mutators.G2DListMutatorRealGaussian`), the *mu* represents the mean of the distribution. + Default *mu* value of the 2D List Gaussian Real Mutator (:func:`Mutators.G2DListMutatorRealGaussian`), + the *mu* represents the mean of the distribution. .. attribute:: CDefG2DListMutRealSIGMA - Default *sigma* value of the 2D List Gaussian Real Mutator (:func:`Mutators.G2DListMutatorRealGaussian`), the *sigma* represents the mean of the distribution. + Default *sigma* value of the 2D List Gaussian Real Mutator (:func:`Mutators.G2DListMutatorRealGaussian`), + the *sigma* represents the mean of the distribution. .. attribute:: CDefG2DListMutIntMU - Default *mu* value of the 2D List Gaussian Integer Mutator (:func:`Mutators.G2DListMutatorIntegerGaussian`), the *mu* represents the mean of the distribution. + Default *mu* value of the 2D List Gaussian Integer Mutator (:func:`Mutators.G2DListMutatorIntegerGaussian`), + the *mu* represents the mean of the distribution. .. attribute:: CDefG2DListMutIntSIGMA - Default *sigma* value of the 2D List Gaussian Integer Mutator (:func:`Mutators.G2DListMutatorIntegerGaussian`), the *sigma* represents the mean of the distribution. + Default *sigma* value of the 2D List Gaussian Integer Mutator (:func:`Mutators.G2DListMutatorIntegerGaussian`), + the *sigma* represents the mean of the distribution. .. attribute:: CDefG2DListMutator @@ -366,13 +378,14 @@ """ -import Scaling -import Selectors -import Initializators -import Mutators -import Crossovers import logging -from GTree import GTreeGP + +from . import Scaling +from . import Selectors +from . import Initializators +from . import Mutators +from . import Crossovers +from .GTree import GTreeGP # Required python version 2.5+ CDefPythonRequire = (2, 5) diff --git a/pyevolve/Crossovers.py b/pyevolve/Crossovers.py index 7584a00..01291e1 100644 --- a/pyevolve/Crossovers.py +++ b/pyevolve/Crossovers.py @@ -6,795 +6,811 @@ In this module we have the genetic operators of crossover (or recombination) for each chromosome representation. """ +from future.builtins import range from random import randint as rand_randint, choice as rand_choice from random import random as rand_random import math -import Util -import Consts +from . import Util -############################# -## 1D Binary String ## -############################# + +# 1D Binary String def G1DBinaryStringXSinglePoint(genome, **args): - """ The crossover of 1D Binary String, Single Point + """ The crossover of 1D Binary String, Single Point + + .. warning:: You can't use this crossover method for binary strings with length of 1. - .. warning:: You can't use this crossover method for binary strings with length of 1. + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + if len(gMom) == 1: + Util.raiseException( + "The Binary String have one element, can't use the Single Point Crossover method !", TypeError) - if len(gMom) == 1: - Util.raiseException("The Binary String have one element, can't use the Single Point Crossover method !", TypeError) + cut = rand_randint(1, len(gMom) - 1) - cut = rand_randint(1, len(gMom) - 1) + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + sister[cut:] = gDad[cut:] - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - sister[cut:] = gDad[cut:] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + brother[cut:] = gMom[cut:] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - brother[cut:] = gMom[cut:] + return (sister, brother) - return (sister, brother) def G1DBinaryStringXTwoPoint(genome, **args): - """ The 1D Binary String crossover, Two Point + """ The 1D Binary String crossover, Two Point + + .. warning:: You can't use this crossover method for binary strings with length of 1. - .. warning:: You can't use this crossover method for binary strings with length of 1. + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + if len(gMom) == 1: + Util.raiseException("The Binary String have one element, can't use the Two Point Crossover method !", TypeError) - if len(gMom) == 1: - Util.raiseException("The Binary String have one element, can't use the Two Point Crossover method !", TypeError) + cuts = [rand_randint(1, len(gMom) - 1), rand_randint(1, len(gMom) - 1)] - cuts = [rand_randint(1, len(gMom) - 1), rand_randint(1, len(gMom) - 1)] + if cuts[0] > cuts[1]: + Util.listSwapElement(cuts, 0, 1) - if cuts[0] > cuts[1]: - Util.listSwapElement(cuts, 0, 1) + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + sister[cuts[0]:cuts[1]] = gDad[cuts[0]:cuts[1]] - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - sister[cuts[0]:cuts[1]] = gDad[cuts[0]:cuts[1]] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + brother[cuts[0]:cuts[1]] = gMom[cuts[0]:cuts[1]] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - brother[cuts[0]:cuts[1]] = gMom[cuts[0]:cuts[1]] + return (sister, brother) - return (sister, brother) def G1DBinaryStringXUniform(genome, **args): - """ The G1DList Uniform Crossover """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + """ The G1DList Uniform Crossover """ + from . import Consts - sister = gMom.clone() - brother = gDad.clone() - sister.resetStats() - brother.resetStats() + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - for i in xrange(len(gMom)): - if Util.randomFlipCoin(Consts.CDefG1DBinaryStringUniformProb): - temp = sister[i] - sister[i] = brother[i] - brother[i] = temp + sister = gMom.clone() + brother = gDad.clone() + sister.resetStats() + brother.resetStats() - return (sister, brother) + for i in range(len(gMom)): + if Util.randomFlipCoin(Consts.CDefG1DBinaryStringUniformProb): + temp = sister[i] + sister[i] = brother[i] + brother[i] = temp -#################### -## 1D List ## -#################### + return (sister, brother) + + +# 1D List def G1DListCrossoverSinglePoint(genome, **args): - """ The crossover of G1DList, Single Point + """ The crossover of G1DList, Single Point + + .. warning:: You can't use this crossover method for lists with just one element. - .. warning:: You can't use this crossover method for lists with just one element. + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + if len(gMom) == 1: + Util.raiseException("The 1D List have one element, can't use the Single Point Crossover method !", TypeError) - if len(gMom) == 1: - Util.raiseException("The 1D List have one element, can't use the Single Point Crossover method !", TypeError) + cut = rand_randint(1, len(gMom) - 1) - cut = rand_randint(1, len(gMom) - 1) + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + sister[cut:] = gDad[cut:] - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - sister[cut:] = gDad[cut:] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + brother[cut:] = gMom[cut:] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - brother[cut:] = gMom[cut:] + return (sister, brother) - return (sister, brother) def G1DListCrossoverTwoPoint(genome, **args): - """ The G1DList crossover, Two Point + """ The G1DList crossover, Two Point - .. warning:: You can't use this crossover method for lists with just one element. + .. warning:: You can't use this crossover method for lists with just one element. - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - if len(gMom) == 1: - Util.raiseException("The 1D List have one element, can't use the Two Point Crossover method !", TypeError) + if len(gMom) == 1: + Util.raiseException("The 1D List have one element, can't use the Two Point Crossover method !", TypeError) - cuts = [rand_randint(1, len(gMom) - 1), rand_randint(1, len(gMom) - 1)] + cuts = [rand_randint(1, len(gMom) - 1), rand_randint(1, len(gMom) - 1)] - if cuts[0] > cuts[1]: - Util.listSwapElement(cuts, 0, 1) + if cuts[0] > cuts[1]: + Util.listSwapElement(cuts, 0, 1) - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - sister[cuts[0]:cuts[1]] = gDad[cuts[0]:cuts[1]] + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + sister[cuts[0]:cuts[1]] = gDad[cuts[0]:cuts[1]] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - brother[cuts[0]:cuts[1]] = gMom[cuts[0]:cuts[1]] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + brother[cuts[0]:cuts[1]] = gMom[cuts[0]:cuts[1]] + + return (sister, brother) - return (sister, brother) def G1DListCrossoverUniform(genome, **args): - """ The G1DList Uniform Crossover + """ The G1DList Uniform Crossover + + Each gene has a 50% chance of being swapped between mom and dad - Each gene has a 50% chance of being swapped between mom and dad + """ + from . import Consts + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + sister = gMom.clone() + brother = gDad.clone() + sister.resetStats() + brother.resetStats() - sister = gMom.clone() - brother = gDad.clone() - sister.resetStats() - brother.resetStats() + for i in range(len(gMom)): + if Util.randomFlipCoin(Consts.CDefG1DListCrossUniformProb): + temp = sister[i] + sister[i] = brother[i] + brother[i] = temp - for i in xrange(len(gMom)): - if Util.randomFlipCoin(Consts.CDefG1DListCrossUniformProb): - temp = sister[i] - sister[i] = brother[i] - brother[i] = temp + return (sister, brother) - return (sister, brother) def G1DListCrossoverOX(genome, **args): - """ The OX Crossover for G1DList (order crossover) """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] - listSize = len(gMom) + """ The OX Crossover for G1DList (order crossover) """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] + listSize = len(gMom) - c1, c2 = [rand_randint(1, len(gMom) - 1), rand_randint(1, len(gMom) - 1)] + c1, c2 = [rand_randint(1, len(gMom) - 1), rand_randint(1, len(gMom) - 1)] - while c1 == c2: - c2 = rand_randint(1, len(gMom) - 1) + while c1 == c2: + c2 = rand_randint(1, len(gMom) - 1) - if c1 > c2: - h = c1 - c1 = c2 - c2 = h + if c1 > c2: + h = c1 + c1 = c2 + c2 = h - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - P1 = [c for c in gMom[c2:] + gMom[:c2] if c not in gDad[c1:c2]] - sister.genomeList = P1[listSize - c2:] + gDad[c1:c2] + P1[:listSize - c2] + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + P1 = [c for c in gMom[c2:] + gMom[:c2] if c not in gDad[c1:c2]] + sister.genomeList = P1[listSize - c2:] + gDad[c1:c2] + P1[:listSize - c2] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - P2 = [c for c in gDad[c2:] + gDad[:c2] if c not in gMom[c1:c2]] - brother.genomeList = P2[listSize - c2:] + gMom[c1:c2] + P2[:listSize - c2] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + P2 = [c for c in gDad[c2:] + gDad[:c2] if c not in gMom[c1:c2]] + brother.genomeList = P2[listSize - c2:] + gMom[c1:c2] + P2[:listSize - c2] - assert listSize == len(sister) - assert listSize == len(brother) + assert listSize == len(sister) + assert listSize == len(brother) + + return (sister, brother) - return (sister, brother) def G1DListCrossoverEdge(genome, **args): - """ THe Edge Recombination crossover for G1DList (widely used for TSP problem) - - See more information in the `Edge Recombination Operator `_ - Wikipedia entry. - """ - gMom, sisterl = args["mom"], [] - gDad, brotherl = args["dad"], [] - - mom_edges, dad_edges, merge_edges = Util.G1DListGetEdgesComposite(gMom, gDad) - - for c, u in (sisterl, set(gMom)), (brotherl, set(gDad)): - curr = None - for i in xrange(len(gMom)): - curr = rand_choice(tuple(u)) if not curr else curr - c.append(curr) - u.remove(curr) - d = [v for v in merge_edges.get(curr, []) if v in u] - if d: - curr = rand_choice(d) - else: - s = [v for v in mom_edges.get(curr, []) if v in u] - s += [v for v in dad_edges.get(curr, []) if v in u] - curr = rand_choice(s) if s else None - - sister = gMom.clone() - brother = gDad.clone() - sister.resetStats() - brother.resetStats() - - sister.genomeList = sisterl - brother.genomeList = brotherl - - return (sister, brother) + """ The Edge Recombination crossover for G1DList (widely used for TSP problem) + + See more information in the `Edge Recombination Operator + `_ + Wikipedia entry. + """ + gMom, sisterl = args["mom"], [] + gDad, brotherl = args["dad"], [] + + mom_edges, dad_edges, merge_edges = Util.G1DListGetEdgesComposite(gMom, gDad) + + for c, u in (sisterl, set(gMom)), (brotherl, set(gDad)): + curr = None + for i in range(len(gMom)): + curr = rand_choice(tuple(u)) if not curr else curr + c.append(curr) + u.remove(curr) + d = [v for v in merge_edges.get(curr, []) if v in u] + if d: + curr = rand_choice(d) + else: + s = [v for v in mom_edges.get(curr, []) if v in u] + s += [v for v in dad_edges.get(curr, []) if v in u] + curr = rand_choice(s) if s else None + + sister = gMom.clone() + brother = gDad.clone() + sister.resetStats() + brother.resetStats() + + sister.genomeList = sisterl + brother.genomeList = brotherl + + return (sister, brother) + def G1DListCrossoverCutCrossfill(genome, **args): - """ The crossover of G1DList, Cut and crossfill, for permutations - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] - - if len(gMom) == 1: - Util.raiseException("The 1D List have one element, can't use the Single Point Crossover method !", TypeError) - - cut = rand_randint(1, len(gMom) - 1) - - if args["count"] >= 1: - sister = gMom.clone() - mother_part = gMom[0:cut] - sister.resetStats() - i = (len(sister) - cut) - x = 0 - for v in gDad: - if v in mother_part: - continue - if x >= i: - break - sister[cut + x] = v - x += 1 - - if args["count"] == 2: - brother = gDad.clone() - father_part = gDad[0:cut] - brother.resetStats() - i = (len(brother) - cut) - x = 0 - for v in gMom: - if v in father_part: - continue - if x >= i: - break - brother[cut + x] = v - x += 1 + """ The crossover of G1DList, Cut and crossfill, for permutations + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] + + if len(gMom) == 1: + Util.raiseException("The 1D List have one element, can't use the Single Point Crossover method !", TypeError) + + cut = rand_randint(1, len(gMom) - 1) + + if args["count"] >= 1: + sister = gMom.clone() + mother_part = gMom[0:cut] + sister.resetStats() + i = (len(sister) - cut) + x = 0 + for v in gDad: + if v in mother_part: + continue + if x >= i: + break + sister[cut + x] = v + x += 1 + + if args["count"] == 2: + brother = gDad.clone() + father_part = gDad[0:cut] + brother.resetStats() + i = (len(brother) - cut) + x = 0 + for v in gMom: + if v in father_part: + continue + if x >= i: + break + brother[cut + x] = v + x += 1 + + return (sister, brother) - return (sister, brother) def G1DListCrossoverRealSBX(genome, **args): - """ Experimental SBX Implementation - Follows the implementation in NSGA-II (Deb, et.al) + """ Experimental SBX Implementation - Follows the implementation in NSGA-II (Deb, et.al) - Some implementation `reference `_. - And another reference to the `Simulated Binary Crossover `_. + Some implementation `reference `_. + And another reference to the `Simulated Binary Crossover + `_. - .. warning:: This crossover method is Data Type Dependent, which means that - must be used for 1D genome of real values. - """ - EPS = Consts.CDefG1DListSBXEPS - # Crossover distribution index - eta_c = Consts.CDefG1DListSBXEtac + .. warning:: This crossover method is Data Type Dependent, which means that + must be used for 1D genome of real values. + """ + from . import Consts - gMom = args["mom"] - gDad = args["dad"] + EPS = Consts.CDefG1DListSBXEPS + # Crossover distribution index + eta_c = Consts.CDefG1DListSBXEtac - # Get the variable bounds ('gDad' could have been used; but I love Mom:-)) - lb = gMom.getParam("rangemin", Consts.CDefRangeMin) - ub = gMom.getParam("rangemax", Consts.CDefRangeMax) + gMom = args["mom"] + gDad = args["dad"] - sister = gMom.clone() - brother = gDad.clone() + # Get the variable bounds ('gDad' could have been used; but I love Mom:-)) + lb = gMom.getParam("rangemin", Consts.CDefRangeMin) + ub = gMom.getParam("rangemax", Consts.CDefRangeMax) - sister.resetStats() - brother.resetStats() + sister = gMom.clone() + brother = gDad.clone() - for i in range(0, len(gMom)): - if math.fabs(gMom[i] - gDad[i]) > EPS: - if gMom[i] > gDad[i]: - #swap - temp = gMom[i] - gMom[i] = gDad[i] - gDad[i] = temp + sister.resetStats() + brother.resetStats() - #random number betwn. 0 & 1 - u = rand_random() + for i in range(0, len(gMom)): + if math.fabs(gMom[i] - gDad[i]) > EPS: + if gMom[i] > gDad[i]: + # swap + temp = gMom[i] + gMom[i] = gDad[i] + gDad[i] = temp - beta = 1.0 + 2 * (gMom[i] - lb) / (1.0 * (gDad[i] - gMom[i])) - alpha = 2.0 - beta ** (-(eta_c + 1.0)) + # random number betwn. 0 & 1 + u = rand_random() - if u <= (1.0 / alpha): - beta_q = (u * alpha) ** (1.0 / ((eta_c + 1.0) * 1.0)) - else: - beta_q = (1.0 / (2.0 - u * alpha)) ** (1.0 / (1.0 * (eta_c + 1.0))) + beta = 1.0 + 2 * (gMom[i] - lb) / (1.0 * (gDad[i] - gMom[i])) + alpha = 2.0 - beta ** (-(eta_c + 1.0)) - brother[i] = 0.5 * ((gMom[i] + gDad[i]) - beta_q * (gDad[i] - gMom[i])) + if u <= (1.0 / alpha): + beta_q = (u * alpha) ** (1.0 / ((eta_c + 1.0) * 1.0)) + else: + beta_q = (1.0 / (2.0 - u * alpha)) ** (1.0 / (1.0 * (eta_c + 1.0))) - beta = 1.0 + 2.0 * (ub - gDad[i]) / (1.0 * (gDad[i] - gMom[i])) - alpha = 2.0 - beta ** (-(eta_c + 1.0)) + brother[i] = 0.5 * ((gMom[i] + gDad[i]) - beta_q * (gDad[i] - gMom[i])) - if u <= (1.0 / alpha): - beta_q = (u * alpha) ** (1.0 / ((eta_c + 1) * 1.0)) - else: - beta_q = (1.0 / (2.0 - u * alpha)) ** (1.0 / (1.0 * (eta_c + 1.0))) + beta = 1.0 + 2.0 * (ub - gDad[i]) / (1.0 * (gDad[i] - gMom[i])) + alpha = 2.0 - beta ** (-(eta_c + 1.0)) - sister[i] = 0.5 * ((gMom[i] + gDad[i]) + beta_q * (gDad[i] - gMom[i])) + if u <= (1.0 / alpha): + beta_q = (u * alpha) ** (1.0 / ((eta_c + 1) * 1.0)) + else: + beta_q = (1.0 / (2.0 - u * alpha)) ** (1.0 / (1.0 * (eta_c + 1.0))) - if brother[i] > ub: - brother[i] = ub - if brother[i] < lb: - brother[i] = lb + sister[i] = 0.5 * ((gMom[i] + gDad[i]) + beta_q * (gDad[i] - gMom[i])) - if sister[i] > ub: - sister[i] = ub - if sister[i] < lb: - sister[i] = lb + if brother[i] > ub: + brother[i] = ub + if brother[i] < lb: + brother[i] = lb - if rand_random() > 0.5: - # Swap - temp = sister[i] - sister[i] = brother[i] - brother[i] = temp - else: - sister[i] = gMom[i] - brother[i] = gDad[i] + if sister[i] > ub: + sister[i] = ub + if sister[i] < lb: + sister[i] = lb + + if rand_random() > 0.5: + # Swap + temp = sister[i] + sister[i] = brother[i] + brother[i] = temp + else: + sister[i] = gMom[i] + brother[i] = gDad[i] - return (sister, brother) + return (sister, brother) -#################### -## 2D List ## -#################### +# 2D List def G2DListCrossoverUniform(genome, **args): - """ The G2DList Uniform Crossover """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + """ The G2DList Uniform Crossover """ + from . import Consts + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - sister = gMom.clone() - brother = gDad.clone() - sister.resetStats() - brother.resetStats() + sister = gMom.clone() + brother = gDad.clone() + sister.resetStats() + brother.resetStats() - h, w = gMom.getSize() + h, w = gMom.getSize() - for i in xrange(h): - for j in xrange(w): - if Util.randomFlipCoin(Consts.CDefG2DListCrossUniformProb): - temp = sister.getItem(i, j) - sister.setItem(i, j, brother.getItem(i, j)) - brother.setItem(i, j, temp) + for i in range(h): + for j in range(w): + if Util.randomFlipCoin(Consts.CDefG2DListCrossUniformProb): + temp = sister.getItem(i, j) + sister.setItem(i, j, brother.getItem(i, j)) + brother.setItem(i, j, temp) - return (sister, brother) + return (sister, brother) def G2DListCrossoverSingleVPoint(genome, **args): - """ The crossover of G2DList, Single Vertical Point """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + """ The crossover of G2DList, Single Vertical Point """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - cut = rand_randint(1, gMom.getWidth() - 1) + cut = rand_randint(1, gMom.getWidth() - 1) - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - for i in xrange(sister.getHeight()): - sister[i][cut:] = gDad[i][cut:] + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + for i in range(sister.getHeight()): + sister[i][cut:] = gDad[i][cut:] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - for i in xrange(brother.getHeight()): - brother[i][cut:] = gMom[i][cut:] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + for i in range(brother.getHeight()): + brother[i][cut:] = gMom[i][cut:] - return (sister, brother) + return (sister, brother) -def G2DListCrossoverSingleHPoint(genome, **args): - """ The crossover of G2DList, Single Horizontal Point """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] - cut = rand_randint(1, gMom.getHeight() - 1) +def G2DListCrossoverSingleHPoint(genome, **args): + """ The crossover of G2DList, Single Horizontal Point """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - for i in xrange(cut, sister.getHeight()): - sister[i][:] = gDad[i][:] + cut = rand_randint(1, gMom.getHeight() - 1) - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - for i in xrange(brother.getHeight()): - brother[i][:] = gMom[i][:] + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + for i in range(cut, sister.getHeight()): + sister[i][:] = gDad[i][:] - return (sister, brother) + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + for i in range(brother.getHeight()): + brother[i][:] = gMom[i][:] + return (sister, brother) -############################# -## 2D Binary String ## -############################# +# 2D Binary String def G2DBinaryStringXUniform(genome, **args): - """ The G2DBinaryString Uniform Crossover + """ The G2DBinaryString Uniform Crossover + + .. versionadded:: 0.6 + The *G2DBinaryStringXUniform* function + """ + from . import Consts - .. versionadded:: 0.6 - The *G2DBinaryStringXUniform* function - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - sister = gMom.clone() - brother = gDad.clone() - sister.resetStats() - brother.resetStats() + sister = gMom.clone() + brother = gDad.clone() + sister.resetStats() + brother.resetStats() - h, w = gMom.getSize() + h, w = gMom.getSize() - for i in xrange(h): - for j in xrange(w): - if Util.randomFlipCoin(Consts.CDefG2DBinaryStringUniformProb): - temp = sister.getItem(i, j) - sister.setItem(i, j, brother.getItem(i, j)) - brother.setItem(i, j, temp) + for i in range(h): + for j in range(w): + if Util.randomFlipCoin(Consts.CDefG2DBinaryStringUniformProb): + temp = sister.getItem(i, j) + sister.setItem(i, j, brother.getItem(i, j)) + brother.setItem(i, j, temp) - return (sister, brother) + return (sister, brother) def G2DBinaryStringXSingleVPoint(genome, **args): - """ The crossover of G2DBinaryString, Single Vertical Point + """ The crossover of G2DBinaryString, Single Vertical Point + + .. versionadded:: 0.6 + The *G2DBinaryStringXSingleVPoint* function + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - .. versionadded:: 0.6 - The *G2DBinaryStringXSingleVPoint* function - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + cut = rand_randint(1, gMom.getWidth() - 1) - cut = rand_randint(1, gMom.getWidth() - 1) + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + for i in range(sister.getHeight()): + sister[i][cut:] = gDad[i][cut:] - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - for i in xrange(sister.getHeight()): - sister[i][cut:] = gDad[i][cut:] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + for i in range(brother.getHeight()): + brother[i][cut:] = gMom[i][cut:] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - for i in xrange(brother.getHeight()): - brother[i][cut:] = gMom[i][cut:] + return (sister, brother) - return (sister, brother) def G2DBinaryStringXSingleHPoint(genome, **args): - """ The crossover of G2DBinaryString, Single Horizontal Point + """ The crossover of G2DBinaryString, Single Horizontal Point - .. versionadded:: 0.6 - The *G2DBinaryStringXSingleHPoint* function + .. versionadded:: 0.6 + The *G2DBinaryStringXSingleHPoint* function - """ - sister = None - brother = None - gMom = args["mom"] - gDad = args["dad"] + """ + sister = None + brother = None + gMom = args["mom"] + gDad = args["dad"] - cut = rand_randint(1, gMom.getHeight() - 1) + cut = rand_randint(1, gMom.getHeight() - 1) - if args["count"] >= 1: - sister = gMom.clone() - sister.resetStats() - for i in xrange(cut, sister.getHeight()): - sister[i][:] = gDad[i][:] + if args["count"] >= 1: + sister = gMom.clone() + sister.resetStats() + for i in range(cut, sister.getHeight()): + sister[i][:] = gDad[i][:] - if args["count"] == 2: - brother = gDad.clone() - brother.resetStats() - for i in xrange(brother.getHeight()): - brother[i][:] = gMom[i][:] + if args["count"] == 2: + brother = gDad.clone() + brother.resetStats() + for i in range(brother.getHeight()): + brother[i][:] = gMom[i][:] - return (sister, brother) + return (sister, brother) -############################# -## Tree ## -############################# +# Tree def GTreeCrossoverSinglePoint(genome, **args): - """ The crossover for GTree, Single Point """ - sister = None - brother = None - gMom = args["mom"].clone() - gDad = args["dad"].clone() + """ The crossover for GTree, Single Point """ + sister = None + brother = None + gMom = args["mom"].clone() + gDad = args["dad"].clone() - gMom.resetStats() - gDad.resetStats() + gMom.resetStats() + gDad.resetStats() - node_mom_stack = [] - all_mom_nodes = [] - node_mom_tmp = None + node_mom_stack = [] + all_mom_nodes = [] + node_mom_tmp = None - node_dad_stack = [] - all_dad_nodes = [] - node_dad_tmp = None + node_dad_stack = [] + all_dad_nodes = [] + node_dad_tmp = None - node_mom_stack.append(gMom.getRoot()) - node_dad_stack.append(gDad.getRoot()) + node_mom_stack.append(gMom.getRoot()) + node_dad_stack.append(gDad.getRoot()) - while (len(node_mom_stack) > 0) and (len(node_dad_stack) > 0): - node_mom_tmp = node_mom_stack.pop() - node_dad_tmp = node_dad_stack.pop() + while (len(node_mom_stack) > 0) and (len(node_dad_stack) > 0): + node_mom_tmp = node_mom_stack.pop() + node_dad_tmp = node_dad_stack.pop() - if node_mom_tmp != gMom.getRoot(): - all_mom_nodes.append(node_mom_tmp) - all_dad_nodes.append(node_dad_tmp) + if node_mom_tmp != gMom.getRoot(): + all_mom_nodes.append(node_mom_tmp) + all_dad_nodes.append(node_dad_tmp) - node_mom_stack.extend(node_mom_tmp.getChilds()) - node_dad_stack.extend(node_dad_tmp.getChilds()) + node_mom_stack.extend(node_mom_tmp.getChilds()) + node_dad_stack.extend(node_dad_tmp.getChilds()) - if len(all_mom_nodes) == 0 or len(all_dad_nodes) == 0: - return (gMom, gDad) + if len(all_mom_nodes) == 0 or len(all_dad_nodes) == 0: + return (gMom, gDad) - if len(all_dad_nodes) == 1: - nodeDad = all_dad_nodes[0] - else: - nodeDad = rand_choice(all_dad_nodes) + if len(all_dad_nodes) == 1: + nodeDad = all_dad_nodes[0] + else: + nodeDad = rand_choice(all_dad_nodes) - if len(all_mom_nodes) == 1: - nodeMom = all_mom_nodes[0] - else: - nodeMom = rand_choice(all_mom_nodes) + if len(all_mom_nodes) == 1: + nodeMom = all_mom_nodes[0] + else: + nodeMom = rand_choice(all_mom_nodes) - nodeMom_parent = nodeMom.getParent() - nodeDad_parent = nodeDad.getParent() - - # Sister - if args["count"] >= 1: - sister = gMom - nodeDad.setParent(nodeMom_parent) - nodeMom_parent.replaceChild(nodeMom, nodeDad) - sister.processNodes() - - # Brother - if args["count"] == 2: - brother = gDad - nodeMom.setParent(nodeDad_parent) - nodeDad_parent.replaceChild(nodeDad, nodeMom) - brother.processNodes() - - return (sister, brother) - -def GTreeCrossoverSinglePointStrict(genome, **args): - """ The crossover of Tree, Strict Single Point + nodeMom_parent = nodeMom.getParent() + nodeDad_parent = nodeDad.getParent() - ..note:: This crossover method creates offspring with restriction of the - *max_depth* parameter. + # Sister + if args["count"] >= 1: + sister = gMom + nodeDad.setParent(nodeMom_parent) + nodeMom_parent.replaceChild(nodeMom, nodeDad) + sister.processNodes() - Accepts the *max_attempt* parameter, *max_depth* (required), and - the distr_leaft (>= 0.0 and <= 1.0), which represents the probability - of leaf selection when findin random nodes for crossover. + # Brother + if args["count"] == 2: + brother = gDad + nodeMom.setParent(nodeDad_parent) + nodeDad_parent.replaceChild(nodeDad, nodeMom) + brother.processNodes() - """ - sister = None - brother = None + return (sister, brother) - gMom = args["mom"].clone() - gDad = args["dad"].clone() - gMom.resetStats() - gDad.resetStats() +def GTreeCrossoverSinglePointStrict(genome, **args): + """ The crossover of Tree, Strict Single Point - max_depth = gMom.getParam("max_depth", None) - max_attempt = gMom.getParam("max_attempt", 10) - distr_leaf = gMom.getParam("distr_leaf", None) + ..note:: This crossover method creates offspring with restriction of the + *max_depth* parameter. - if max_depth is None: - Util.raiseException("You must specify the max_depth genome parameter !", ValueError) + Accepts the *max_attempt* parameter, *max_depth* (required), and + the distr_leaft (>= 0.0 and <= 1.0), which represents the probability + of leaf selection when findin random nodes for crossover. - if max_depth < 0: - Util.raiseException("The max_depth must be >= 1, if you want to use GTreeCrossoverSinglePointStrict crossover !", ValueError) + """ + sister = None + brother = None - momRandom = None - dadRandom = None + gMom = args["mom"].clone() + gDad = args["dad"].clone() - for i in xrange(max_attempt): + gMom.resetStats() + gDad.resetStats() - if distr_leaf is None: - dadRandom = gDad.getRandomNode() - momRandom = gMom.getRandomNode() - else: - if Util.randomFlipCoin(distr_leaf): - momRandom = gMom.getRandomNode(1) - else: - momRandom = gMom.getRandomNode(2) + max_depth = gMom.getParam("max_depth", None) + max_attempt = gMom.getParam("max_attempt", 10) + distr_leaf = gMom.getParam("distr_leaf", None) - if Util.randomFlipCoin(distr_leaf): - dadRandom = gDad.getRandomNode(1) - else: - dadRandom = gDad.getRandomNode(2) + if max_depth is None: + Util.raiseException("You must specify the max_depth genome parameter !", ValueError) - assert momRandom is not None - assert dadRandom is not None + if max_depth < 0: + Util.raiseException( + "The max_depth must be >= 1, if you want to use GTreeCrossoverSinglePointStrict crossover !", ValueError) - # Optimize here - mH = gMom.getNodeHeight(momRandom) - dH = gDad.getNodeHeight(dadRandom) + momRandom = None + dadRandom = None - mD = gMom.getNodeDepth(momRandom) - dD = gDad.getNodeDepth(dadRandom) + for i in range(max_attempt): - # The depth of the crossover is greater than the max_depth - if (dD + mH <= max_depth) and (mD + dH <= max_depth): - break + if distr_leaf is None: + dadRandom = gDad.getRandomNode() + momRandom = gMom.getRandomNode() + else: + if Util.randomFlipCoin(distr_leaf): + momRandom = gMom.getRandomNode(1) + else: + momRandom = gMom.getRandomNode(2) - if i == (max_attempt - 1): - assert gMom.getHeight() <= max_depth - return (gMom, gDad) - else: - nodeMom, nodeDad = momRandom, dadRandom + if Util.randomFlipCoin(distr_leaf): + dadRandom = gDad.getRandomNode(1) + else: + dadRandom = gDad.getRandomNode(2) - nodeMom_parent = nodeMom.getParent() - nodeDad_parent = nodeDad.getParent() + assert momRandom is not None + assert dadRandom is not None - # Sister - if args["count"] >= 1: - sister = gMom - nodeDad.setParent(nodeMom_parent) + # Optimize here + mH = gMom.getNodeHeight(momRandom) + dH = gDad.getNodeHeight(dadRandom) - if nodeMom_parent is None: - sister.setRoot(nodeDad) - else: - nodeMom_parent.replaceChild(nodeMom, nodeDad) - sister.processNodes() - assert sister.getHeight() <= max_depth + mD = gMom.getNodeDepth(momRandom) + dD = gDad.getNodeDepth(dadRandom) - # Brother - if args["count"] == 2: - brother = gDad - nodeMom.setParent(nodeDad_parent) + # The depth of the crossover is greater than the max_depth + if (dD + mH <= max_depth) and (mD + dH <= max_depth): + break - if nodeDad_parent is None: - brother.setRoot(nodeMom) - else: - nodeDad_parent.replaceChild(nodeDad, nodeMom) - brother.processNodes() - assert brother.getHeight() <= max_depth + if i == (max_attempt - 1): # TODO i can be undefined + assert gMom.getHeight() <= max_depth + return (gMom, gDad) + else: + nodeMom, nodeDad = momRandom, dadRandom - return (sister, brother) + nodeMom_parent = nodeMom.getParent() + nodeDad_parent = nodeDad.getParent() -############################################################################# -################# GTreeGP Crossovers ###################################### -############################################################################# + # Sister + if args["count"] >= 1: + sister = gMom + nodeDad.setParent(nodeMom_parent) -def GTreeGPCrossoverSinglePoint(genome, **args): - """ The crossover of the GTreeGP, Single Point for Genetic Programming + if nodeMom_parent is None: + sister.setRoot(nodeDad) + else: + nodeMom_parent.replaceChild(nodeMom, nodeDad) + sister.processNodes() + assert sister.getHeight() <= max_depth - ..note:: This crossover method creates offspring with restriction of the - *max_depth* parameter. + # Brother + if args["count"] == 2: + brother = gDad + nodeMom.setParent(nodeDad_parent) - Accepts the *max_attempt* parameter, *max_depth* (required). - """ - sister = None - brother = None + if nodeDad_parent is None: + brother.setRoot(nodeMom) + else: + nodeDad_parent.replaceChild(nodeDad, nodeMom) + brother.processNodes() + assert brother.getHeight() <= max_depth - gMom = args["mom"].clone() - gDad = args["dad"].clone() + return (sister, brother) - gMom.resetStats() - gDad.resetStats() - max_depth = gMom.getParam("max_depth", None) - max_attempt = gMom.getParam("max_attempt", 15) +# GTreeGP Crossovers - if max_depth is None: - Util.raiseException("You must specify the max_depth genome parameter !", ValueError) +def GTreeGPCrossoverSinglePoint(genome, **args): + """ The crossover of the GTreeGP, Single Point for Genetic Programming - if max_depth < 0: - Util.raiseException("The max_depth must be >= 1, if you want to use GTreeCrossoverSinglePointStrict crossover !", ValueError) + ..note:: This crossover method creates offspring with restriction of the + *max_depth* parameter. - momRandom = None - dadRandom = None + Accepts the *max_attempt* parameter, *max_depth* (required). + """ + from . import Consts - for i in xrange(max_attempt): + sister = None + brother = None - dadRandom = gDad.getRandomNode() + gMom = args["mom"].clone() + gDad = args["dad"].clone() - if dadRandom.getType() == Consts.nodeType["TERMINAL"]: - momRandom = gMom.getRandomNode(1) - elif dadRandom.getType() == Consts.nodeType["NONTERMINAL"]: - momRandom = gMom.getRandomNode(2) + gMom.resetStats() + gDad.resetStats() - mD = gMom.getNodeDepth(momRandom) - dD = gDad.getNodeDepth(dadRandom) + max_depth = gMom.getParam("max_depth", None) + max_attempt = gMom.getParam("max_attempt", 15) - # Two nodes are root - if mD == 0 and dD == 0: - continue + if max_depth is None: + Util.raiseException("You must specify the max_depth genome parameter !", ValueError) - mH = gMom.getNodeHeight(momRandom) - if dD + mH > max_depth: - continue + if max_depth < 0: + Util.raiseException( + "The max_depth must be >= 1, if you want to use GTreeCrossoverSinglePointStrict crossover !", ValueError) - dH = gDad.getNodeHeight(dadRandom) - if mD + dH > max_depth: - continue + momRandom = None + dadRandom = None - break + for i in range(max_attempt): - if i == (max_attempt - 1): - assert gMom.getHeight() <= max_depth - return (gMom, gDad) - else: - nodeMom, nodeDad = momRandom, dadRandom + dadRandom = gDad.getRandomNode() - nodeMom_parent = nodeMom.getParent() - nodeDad_parent = nodeDad.getParent() + if dadRandom.getType() == Consts.nodeType["TERMINAL"]: + momRandom = gMom.getRandomNode(1) + elif dadRandom.getType() == Consts.nodeType["NONTERMINAL"]: + momRandom = gMom.getRandomNode(2) - # Sister - if args["count"] >= 1: - sister = gMom - nodeDad.setParent(nodeMom_parent) + mD = gMom.getNodeDepth(momRandom) + dD = gDad.getNodeDepth(dadRandom) - if nodeMom_parent is None: - sister.setRoot(nodeDad) - else: - nodeMom_parent.replaceChild(nodeMom, nodeDad) - sister.processNodes() - assert sister.getHeight() <= max_depth + # Two nodes are root + if mD == 0 and dD == 0: + continue - # Brother - if args["count"] == 2: - brother = gDad - nodeMom.setParent(nodeDad_parent) + mH = gMom.getNodeHeight(momRandom) + if dD + mH > max_depth: + continue - if nodeDad_parent is None: - brother.setRoot(nodeMom) - else: - nodeDad_parent.replaceChild(nodeDad, nodeMom) - brother.processNodes() - assert brother.getHeight() <= max_depth + dH = gDad.getNodeHeight(dadRandom) + if mD + dH > max_depth: + continue - return (sister, brother) + break + + if i == (max_attempt - 1): + assert gMom.getHeight() <= max_depth + return (gMom, gDad) + else: + nodeMom, nodeDad = momRandom, dadRandom + + nodeMom_parent = nodeMom.getParent() + nodeDad_parent = nodeDad.getParent() + + # Sister + if args["count"] >= 1: + sister = gMom + nodeDad.setParent(nodeMom_parent) + + if nodeMom_parent is None: + sister.setRoot(nodeDad) + else: + nodeMom_parent.replaceChild(nodeMom, nodeDad) + sister.processNodes() + assert sister.getHeight() <= max_depth + + # Brother + if args["count"] == 2: + brother = gDad + nodeMom.setParent(nodeDad_parent) + + if nodeDad_parent is None: + brother.setRoot(nodeMom) + else: + nodeDad_parent.replaceChild(nodeDad, nodeMom) + brother.processNodes() + assert brother.getHeight() <= max_depth + + return (sister, brother) diff --git a/pyevolve/DBAdapters.py b/pyevolve/DBAdapters.py index 63a4a35..ea6ae1a 100644 --- a/pyevolve/DBAdapters.py +++ b/pyevolve/DBAdapters.py @@ -17,774 +17,781 @@ """ +from future.builtins import range + from pyevolve import __version__ -import Consts -import Util +from . import Consts +from . import Util +from . import Statistics import logging -import types import datetime -import Statistics class DBBaseAdapter(object): - """ DBBaseAdapter Class - The base class for all DB Adapters + """ DBBaseAdapter Class - The base class for all DB Adapters + + If you want to create your own DB Adapter, you must subclass this + class. - If you want to create your own DB Adapter, you must subclass this - class. + :param frequency: the the generational dump frequency - :param frequency: the the generational dump frequency + .. versionadded:: 0.6 + Added the :class:`DBBaseAdapter` class. + """ + def __init__(self, frequency, identify): + """ The class constructor """ + self.statsGenFreq = frequency - .. versionadded:: 0.6 - Added the :class:`DBBaseAdapter` class. - """ - def __init__(self, frequency, identify): - """ The class constructor """ - self.statsGenFreq = frequency + if identify is None: + self.identify = datetime.datetime.strftime(datetime.datetime.now(), "%d/%m/%y-%H:%M") + else: + self.identify = identify - if identify is None: - self.identify = datetime.datetime.strftime(datetime.datetime.now(), "%d/%m/%y-%H:%M") - else: - self.identify = identify + def setIdentify(self, identify): + """ Sets the identify of the statistics - def setIdentify(self, identify): - """ Sets the identify of the statistics + :param identify: the id string + """ + if identify is None: + self.identify = datetime.datetime.strftime(datetime.datetime.now(), "%d/%m/%y-%H:%M") + else: + self.identify = identify - :param identify: the id string - """ - if identify is None: - self.identify = datetime.datetime.strftime(datetime.datetime.now(), "%d/%m/%y-%H:%M") - else: - self.identify = identify + def getIdentify(self): + """ Return the statistics identify - def getIdentify(self): - """ Return the statistics identify + :rtype: identify string + """ + return self.identify - :rtype: identify string - """ - return self.identify + def getStatsGenFreq(self): + """ Returns the frequency of statistical dump - def getStatsGenFreq(self): - """ Returns the frequency of statistical dump + :rtype: the generation interval of statistical dump + """ + return self.statsGenFreq - :rtype: the generation interval of statistical dump - """ - return self.statsGenFreq + def setStatsGenFreq(self, statsGenFreq): + """ Set the frequency of statistical dump - def setStatsGenFreq(self, statsGenFreq): - """ Set the frequency of statistical dump + :param statsGenFreq: the generation interval of statistical dump + """ + self.statsGenFreq = statsGenFreq - :param statsGenFreq: the generation interval of statistical dump - """ - self.statsGenFreq = statsGenFreq + def open(self, ga_engine): + """ This method is called one time to do the initialization of + the DB Adapter - def open(self, ga_engine): - """ This method is called one time to do the initialization of - the DB Adapter + :param ga_engine: the GA Engine + """ + pass - :param ga_engine: the GA Engine - """ - pass + def commitAndClose(self): + """ This method is called at the end of the evolution, to closes the + DB Adapter and commit the changes """ + pass - def commitAndClose(self): - """ This method is called at the end of the evolution, to closes the - DB Adapter and commit the changes """ - pass + def insert(self, ga_engine): + """ Insert the stats - def insert(self, ga_engine): - """ Insert the stats + :param ga_engine: the GA Engine + """ + Util.raiseException("This method is not implemented on the ABC", NotImplementedError) - :param ga_engine: the GA Engine - """ - Util.raiseException("This method is not implemented on the ABC", NotImplementedError) class DBFileCSV(DBBaseAdapter): - """ DBFileCSV Class - Adapter to dump statistics in CSV format + """ DBFileCSV Class - Adapter to dump statistics in CSV format - Inheritance diagram for :class:`DBAdapters.DBFileCSV`: + Inheritance diagram for :class:`DBAdapters.DBFileCSV`: - .. inheritance-diagram:: DBAdapters.DBFileCSV + .. inheritance-diagram:: DBAdapters.DBFileCSV - Example: - >>> adapter = DBFileCSV(filename="file.csv", identify="run_01", - frequency = 1, reset = True) + Example: + >>> adapter = DBFileCSV(filename="file.csv", identify="run_01", + frequency = 1, reset = True) - :param filename: the CSV filename - :param identify: the identify of the run - :param frequency: the generational dump frequency - :param reset: if True, the file old data will be overwrite with the new + :param filename: the CSV filename + :param identify: the identify of the run + :param frequency: the generational dump frequency + :param reset: if True, the file old data will be overwrite with the new - .. versionadded:: 0.6 - Removed the stub methods and subclassed the :class:`DBBaseAdapter` class. + .. versionadded:: 0.6 + Removed the stub methods and subclassed the :class:`DBBaseAdapter` class. - """ - def __init__(self, filename=Consts.CDefCSVFileName, identify=None, - frequency=Consts.CDefCSVFileStatsGenFreq, reset=True): - """ The creator of DBFileCSV Class """ + """ + def __init__(self, filename=Consts.CDefCSVFileName, identify=None, + frequency=Consts.CDefCSVFileStatsGenFreq, reset=True): + """ The creator of DBFileCSV Class """ - super(DBFileCSV, self).__init__(frequency, identify) + super(DBFileCSV, self).__init__(frequency, identify) - self.csvmod = None + self.csvmod = None - self.filename = filename - self.csvWriter = None - self.fHandle = None - self.reset = reset + self.filename = filename + self.csvWriter = None + self.fHandle = None + self.reset = reset - def __repr__(self): - """ The string representation of adapter """ - ret = "DBFileCSV DB Adapter [File='%s', identify='%s']" % (self.filename, self.getIdentify()) - return ret + def __repr__(self): + """ The string representation of adapter """ + ret = "DBFileCSV DB Adapter [File='%s', identify='%s']" % (self.filename, self.getIdentify()) + return ret - def open(self, ga_engine): - """ Open the CSV file or creates a new file + def open(self, ga_engine): + """ Open the CSV file or creates a new file - :param ga_engine: the GA Engine + :param ga_engine: the GA Engine - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - if self.csvmod is None: - logging.debug("Loading the csv module...") - self.csvmod = Util.importSpecial("csv") + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + if self.csvmod is None: + logging.debug("Loading the csv module...") + self.csvmod = Util.importSpecial("csv") - logging.debug("Opening the CSV file to dump statistics [%s]", self.filename) - open_mode = 'w' if self.reset else 'a' - self.fHandle = open(self.filename, open_mode) - self.csvWriter = self.csvmod.writer(self.fHandle, delimiter=';') + logging.debug("Opening the CSV file to dump statistics [%s]", self.filename) + open_mode = 'w' if self.reset else 'a' + self.fHandle = open(self.filename, open_mode) + self.csvWriter = self.csvmod.writer(self.fHandle, delimiter=';') - def close(self): - """ Closes the CSV file handle """ - logging.debug("Closing the CSV file [%s]", self.filename) - if self.fHandle: - self.fHandle.close() + def close(self): + """ Closes the CSV file handle """ + logging.debug("Closing the CSV file [%s]", self.filename) + if self.fHandle: + self.fHandle.close() - def commitAndClose(self): - """ Commits and closes """ - self.close() + def commitAndClose(self): + """ Commits and closes """ + self.close() - def insert(self, ga_engine): - """ Inserts the stats into the CSV file + def insert(self, ga_engine): + """ Inserts the stats into the CSV file - :param ga_engine: the GA Engine + :param ga_engine: the GA Engine + + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + stats = ga_engine.getStatistics() + generation = ga_engine.getCurrentGeneration() + line = [self.getIdentify(), generation] + line.extend(stats.asTuple()) + self.csvWriter.writerow(line) - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - stats = ga_engine.getStatistics() - generation = ga_engine.getCurrentGeneration() - line = [self.getIdentify(), generation] - line.extend(stats.asTuple()) - self.csvWriter.writerow(line) class DBURLPost(DBBaseAdapter): - """ DBURLPost Class - Adapter to call an URL with statistics + """ DBURLPost Class - Adapter to call an URL with statistics + + Inheritance diagram for :class:`DBAdapters.DBURLPost`: - Inheritance diagram for :class:`DBAdapters.DBURLPost`: + .. inheritance-diagram:: DBAdapters.DBURLPost - .. inheritance-diagram:: DBAdapters.DBURLPost + Example: + >>> dbadapter = DBURLPost(url="http://localhost/post.py", identify="test") - Example: - >>> dbadapter = DBURLPost(url="http://localhost/post.py", identify="test") + The parameters that will be sent is all the statistics described in the :class:`Statistics.Statistics` + class, and the parameters: - The parameters that will be sent is all the statistics described in the :class:`Statistics.Statistics` - class, and the parameters: + **generation** + The generation of the statistics - **generation** - The generation of the statistics + **identify** + The id specified by user - **identify** - The id specified by user + .. note:: see the :class:`Statistics.Statistics` documentation. - .. note:: see the :class:`Statistics.Statistics` documentation. + :param url: the URL to be used + :param identify: the identify of the run + :param frequency: the generational dump frequency + :param post: if True, the POST method will be used, otherwise GET will be used. - :param url: the URL to be used - :param identify: the identify of the run - :param frequency: the generational dump frequency - :param post: if True, the POST method will be used, otherwise GET will be used. + .. versionadded:: 0.6 + Removed the stub methods and subclassed the :class:`DBBaseAdapter` class. + """ - .. versionadded:: 0.6 - Removed the stub methods and subclassed the :class:`DBBaseAdapter` class. - """ + def __init__(self, url, identify=None, + frequency=Consts.CDefURLPostStatsGenFreq, post=True): + """ The creator of the DBURLPost Class. """ - def __init__(self, url, identify=None, - frequency=Consts.CDefURLPostStatsGenFreq, post=True): - """ The creator of the DBURLPost Class. """ + super(DBURLPost, self).__init__(frequency, identify) + self.urllibmod = None - super(DBURLPost, self).__init__(frequency, identify) - self.urllibmod = None + self.url = url + self.post = post - self.url = url - self.post = post + def __repr__(self): + """ The string representation of adapter """ + ret = "DBURLPost DB Adapter [URL='%s', identify='%s']" % (self.url, self.getIdentify()) + return ret - def __repr__(self): - """ The string representation of adapter """ - ret = "DBURLPost DB Adapter [URL='%s', identify='%s']" % (self.url, self.getIdentify()) - return ret + def open(self, ga_engine): + """ Load the modules needed - def open(self, ga_engine): - """ Load the modules needed + :param ga_engine: the GA Engine - :param ga_engine: the GA Engine + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + if self.urllibmod is None: + logging.debug("Loading urllib module...") + self.urllibmod = Util.importSpecial("urllib") - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - if self.urllibmod is None: - logging.debug("Loading urllib module...") - self.urllibmod = Util.importSpecial("urllib") + def insert(self, ga_engine): + """ Sends the data to the URL using POST or GET - def insert(self, ga_engine): - """ Sends the data to the URL using POST or GET + :param ga_engine: the GA Engine - :param ga_engine: the GA Engine + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + logging.debug("Sending http request to %s.", self.url) + stats = ga_engine.getStatistics() + response = None + params = stats.internalDict.copy() + params["generation"] = ga_engine.getCurrentGeneration() + params["identify"] = self.getIdentify() + if self.post: # POST + response = self.urllibmod.urlopen(self.url, self.urllibmod.urlencode(params)) + else: # GET + response = self.urllibmod.urlopen(self.url + "?%s" % (self.urllibmod.urlencode(params))) + if response: + response.close() - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - logging.debug("Sending http request to %s.", self.url) - stats = ga_engine.getStatistics() - response = None - params = stats.internalDict.copy() - params["generation"] = ga_engine.getCurrentGeneration() - params["identify"] = self.getIdentify() - if self.post: # POST - response = self.urllibmod.urlopen(self.url, self.urllibmod.urlencode(params)) - else: # GET - response = self.urllibmod.urlopen(self.url + "?%s" % (self.urllibmod.urlencode(params))) - if response: - response.close() class DBSQLite(DBBaseAdapter): - """ DBSQLite Class - Adapter to dump data in SQLite3 database format + """ DBSQLite Class - Adapter to dump data in SQLite3 database format - Inheritance diagram for :class:`DBAdapters.DBSQLite`: + Inheritance diagram for :class:`DBAdapters.DBSQLite`: - .. inheritance-diagram:: DBAdapters.DBSQLite + .. inheritance-diagram:: DBAdapters.DBSQLite - Example: - >>> dbadapter = DBSQLite(identify="test") + Example: + >>> dbadapter = DBSQLite(identify="test") - When you run some GA for the first time, you need to create the database, for this, you - must use the *resetDB* parameter: + When you run some GA for the first time, you need to create the database, for this, you + must use the *resetDB* parameter: - >>> dbadapter = DBSQLite(identify="test", resetDB=True) + >>> dbadapter = DBSQLite(identify="test", resetDB=True) - This parameter will erase all the database tables and will create the new ones. - The *resetDB* parameter is different from the *resetIdentify* parameter, the *resetIdentify* - only erases the rows with the same "identify" name. + This parameter will erase all the database tables and will create the new ones. + The *resetDB* parameter is different from the *resetIdentify* parameter, the *resetIdentify* + only erases the rows with the same "identify" name. + + :param dbname: the database filename + :param identify: the identify if the run + :param resetDB: if True, the database structure will be recreated + :param resetIdentify: if True, the identify with the same name will be overwrite with new data + :param frequency: the generational dump frequency + :param commit_freq: the commit frequency + """ + + def __init__(self, dbname=Consts.CDefSQLiteDBName, identify=None, resetDB=False, + resetIdentify=True, frequency=Consts.CDefSQLiteStatsGenFreq, + commit_freq=Consts.CDefSQLiteStatsCommitFreq): + """ The creator of the DBSQLite Class """ + + super(DBSQLite, self).__init__(frequency, identify) + + self.sqlite3mod = None + self.connection = None + self.resetDB = resetDB + self.resetIdentify = resetIdentify + self.dbName = dbname + self.typeDict = {float: "real"} + self.cursorPool = None + self.commitFreq = commit_freq + + def __repr__(self): + """ The string representation of adapter """ + ret = "DBSQLite DB Adapter [File='%s', identify='%s']" % (self.dbName, self.getIdentify()) + return ret + + def open(self, ga_engine): + """ Open the database connection + + :param ga_engine: the GA Engine + + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + if self.sqlite3mod is None: + logging.debug("Loading sqlite3 module...") + self.sqlite3mod = Util.importSpecial("sqlite3") + + logging.debug("Opening database, dbname=%s", self.dbName) + self.connection = self.sqlite3mod.connect(self.dbName) + + temp_stats = Statistics.Statistics() + + if self.resetDB: + self.resetStructure(Statistics.Statistics()) + + self.createStructure(temp_stats) + + if self.resetIdentify: + self.resetTableIdentify() + + def commitAndClose(self): + """ Commit changes on database and closes connection """ + self.commit() + self.close() + + def close(self): + """ Close the database connection """ + logging.debug("Closing database.") + if self.cursorPool: + self.cursorPool.close() + self.cursorPool = None + self.connection.close() + + def commit(self): + """ Commit changes to database """ + logging.debug("Commiting changes to database.") + self.connection.commit() + + def getCursor(self): + """ Return a cursor from the pool + + :rtype: the cursor + + """ + if not self.cursorPool: + logging.debug("Creating new cursor for database...") + self.cursorPool = self.connection.cursor() + return self.cursorPool + else: + return self.cursorPool + + def createStructure(self, stats): + """ Create table using the Statistics class structure + + :param stats: the statistics object + + """ + c = self.getCursor() + pstmt = "create table if not exists %s(identify text, generation integer, " % (Consts.CDefSQLiteDBTable) + for k, v in list(stats.items()): + pstmt += "%s %s, " % (k, self.typeDict[type(v)]) + pstmt = pstmt[:-2] + ")" + logging.debug("Creating table %s: %s.", Consts.CDefSQLiteDBTable, pstmt) + c.execute(pstmt) + + pstmt = """create table if not exists %s(identify text, generation integer, + individual integer, fitness real, raw real)""" % (Consts.CDefSQLiteDBTablePop) + logging.debug("Creating table %s: %s.", Consts.CDefSQLiteDBTablePop, pstmt) + c.execute(pstmt) + self.commit() + + def resetTableIdentify(self): + """ Delete all records on the table with the same Identify """ + c = self.getCursor() + stmt = "delete from %s where identify = ?" % (Consts.CDefSQLiteDBTable) + stmt2 = "delete from %s where identify = ?" % (Consts.CDefSQLiteDBTablePop) + + logging.debug("Erasing data from the tables with the identify = %s", self.getIdentify()) + try: + c.execute(stmt, (self.getIdentify(),)) + c.execute(stmt2, (self.getIdentify(),)) + except self.sqlite3mod.OperationalError as expt: + if str(expt).find("no such table") >= 0: + print("\n ## The DB Adapter can't find the tables ! Consider enable the parameter resetDB ! ##\n") + + self.commit() + + def resetStructure(self, stats): + """ Deletes de current structure and calls createStructure + + :param stats: the statistics object + + """ + logging.debug("Reseting structure, droping table and creating new empty table.") + c = self.getCursor() + c.execute("drop table if exists %s" % (Consts.CDefSQLiteDBTable,)) + c.execute("drop table if exists %s" % (Consts.CDefSQLiteDBTablePop,)) + self.commit() + self.createStructure(stats) + + def insert(self, ga_engine): + """ Inserts the statistics data to database + + :param ga_engine: the GA Engine + + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + stats = ga_engine.getStatistics() + population = ga_engine.getPopulation() + generation = ga_engine.getCurrentGeneration() + + c = self.getCursor() + pstmt = "insert into %s values (?, ?, " % (Consts.CDefSQLiteDBTable) + for i in range(len(stats)): + pstmt += "?, " + pstmt = pstmt[:-2] + ")" + c.execute(pstmt, (self.getIdentify(), generation) + stats.asTuple()) + + pstmt = "insert into %s values(?, ?, ?, ?, ?)" % (Consts.CDefSQLiteDBTablePop,) + tups = [] + for i in range(len(population)): + ind = population[i] + tups.append((self.getIdentify(), generation, i, ind.fitness, ind.score)) + + c.executemany(pstmt, tups) + if (generation % self.commitFreq == 0): + self.commit() - :param dbname: the database filename - :param identify: the identify if the run - :param resetDB: if True, the database structure will be recreated - :param resetIdentify: if True, the identify with the same name will be overwrite with new data - :param frequency: the generational dump frequency - :param commit_freq: the commit frequency - """ - - def __init__(self, dbname=Consts.CDefSQLiteDBName, identify=None, resetDB=False, - resetIdentify=True, frequency=Consts.CDefSQLiteStatsGenFreq, - commit_freq=Consts.CDefSQLiteStatsCommitFreq): - """ The creator of the DBSQLite Class """ - - super(DBSQLite, self).__init__(frequency, identify) - - self.sqlite3mod = None - self.connection = None - self.resetDB = resetDB - self.resetIdentify = resetIdentify - self.dbName = dbname - self.typeDict = {types.FloatType: "real"} - self.cursorPool = None - self.commitFreq = commit_freq - - def __repr__(self): - """ The string representation of adapter """ - ret = "DBSQLite DB Adapter [File='%s', identify='%s']" % (self.dbName, self.getIdentify()) - return ret - - def open(self, ga_engine): - """ Open the database connection - - :param ga_engine: the GA Engine - - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - if self.sqlite3mod is None: - logging.debug("Loading sqlite3 module...") - self.sqlite3mod = Util.importSpecial("sqlite3") - - logging.debug("Opening database, dbname=%s", self.dbName) - self.connection = self.sqlite3mod.connect(self.dbName) - - temp_stats = Statistics.Statistics() - - if self.resetDB: - self.resetStructure(Statistics.Statistics()) - - self.createStructure(temp_stats) - - if self.resetIdentify: - self.resetTableIdentify() - - def commitAndClose(self): - """ Commit changes on database and closes connection """ - self.commit() - self.close() - - def close(self): - """ Close the database connection """ - logging.debug("Closing database.") - if self.cursorPool: - self.cursorPool.close() - self.cursorPool = None - self.connection.close() - - def commit(self): - """ Commit changes to database """ - logging.debug("Commiting changes to database.") - self.connection.commit() - - def getCursor(self): - """ Return a cursor from the pool - - :rtype: the cursor - - """ - if not self.cursorPool: - logging.debug("Creating new cursor for database...") - self.cursorPool = self.connection.cursor() - return self.cursorPool - else: - return self.cursorPool - - def createStructure(self, stats): - """ Create table using the Statistics class structure - - :param stats: the statistics object - - """ - c = self.getCursor() - pstmt = "create table if not exists %s(identify text, generation integer, " % (Consts.CDefSQLiteDBTable) - for k, v in stats.items(): - pstmt += "%s %s, " % (k, self.typeDict[type(v)]) - pstmt = pstmt[:-2] + ")" - logging.debug("Creating table %s: %s.", Consts.CDefSQLiteDBTable, pstmt) - c.execute(pstmt) - - pstmt = """create table if not exists %s(identify text, generation integer, - individual integer, fitness real, raw real)""" % (Consts.CDefSQLiteDBTablePop) - logging.debug("Creating table %s: %s.", Consts.CDefSQLiteDBTablePop, pstmt) - c.execute(pstmt) - self.commit() - - def resetTableIdentify(self): - """ Delete all records on the table with the same Identify """ - c = self.getCursor() - stmt = "delete from %s where identify = ?" % (Consts.CDefSQLiteDBTable) - stmt2 = "delete from %s where identify = ?" % (Consts.CDefSQLiteDBTablePop) - - logging.debug("Erasing data from the tables with the identify = %s", self.getIdentify()) - try: - c.execute(stmt, (self.getIdentify(),)) - c.execute(stmt2, (self.getIdentify(),)) - except self.sqlite3mod.OperationalError, expt: - if str(expt).find("no such table") >= 0: - print "\n ## The DB Adapter can't find the tables ! Consider enable the parameter resetDB ! ##\n" - - self.commit() - - def resetStructure(self, stats): - """ Deletes de current structure and calls createStructure - - :param stats: the statistics object - - """ - logging.debug("Reseting structure, droping table and creating new empty table.") - c = self.getCursor() - c.execute("drop table if exists %s" % (Consts.CDefSQLiteDBTable,)) - c.execute("drop table if exists %s" % (Consts.CDefSQLiteDBTablePop,)) - self.commit() - self.createStructure(stats) - - def insert(self, ga_engine): - """ Inserts the statistics data to database - - :param ga_engine: the GA Engine - - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - stats = ga_engine.getStatistics() - population = ga_engine.getPopulation() - generation = ga_engine.getCurrentGeneration() - - c = self.getCursor() - pstmt = "insert into %s values (?, ?, " % (Consts.CDefSQLiteDBTable) - for i in xrange(len(stats)): - pstmt += "?, " - pstmt = pstmt[:-2] + ")" - c.execute(pstmt, (self.getIdentify(), generation) + stats.asTuple()) - - pstmt = "insert into %s values(?, ?, ?, ?, ?)" % (Consts.CDefSQLiteDBTablePop,) - tups = [] - for i in xrange(len(population)): - ind = population[i] - tups.append((self.getIdentify(), generation, i, ind.fitness, ind.score)) - - c.executemany(pstmt, tups) - if (generation % self.commitFreq == 0): - self.commit() class DBXMLRPC(DBBaseAdapter): - """ DBXMLRPC Class - Adapter to dump statistics to a XML Remote Procedure Call + """ DBXMLRPC Class - Adapter to dump statistics to a XML Remote Procedure Call + + Inheritance diagram for :class:`DBAdapters.DBXMLRPC`: - Inheritance diagram for :class:`DBAdapters.DBXMLRPC`: + .. inheritance-diagram:: DBAdapters.DBXMLRPC - .. inheritance-diagram:: DBAdapters.DBXMLRPC + Example: + >>> adapter = DBXMLRPC(url="http://localhost:8000/", identify="run_01", + frequency = 1) - Example: - >>> adapter = DBXMLRPC(url="http://localhost:8000/", identify="run_01", - frequency = 1) + :param url: the URL of the XML RPC + :param identify: the identify of the run + :param frequency: the generational dump frequency - :param url: the URL of the XML RPC - :param identify: the identify of the run - :param frequency: the generational dump frequency + .. note:: The XML RPC Server must implement the *insert* method, wich receives + a python dictionary as argument. - .. note:: The XML RPC Server must implement the *insert* method, wich receives - a python dictionary as argument. + Example of an server in Python: :: - Example of an server in Python: :: + import xmlrpclib + from SimpleXMLRPCServer import SimpleXMLRPCServer - import xmlrpclib - from SimpleXMLRPCServer import SimpleXMLRPCServer + def insert(l): + print "Received statistics: %s" % l - def insert(l): - print "Received statistics: %s" % l + server = SimpleXMLRPCServer(("localhost", 8000), allow_none=True) + print "Listening on port 8000..." + server.register_function(insert, "insert") + server.serve_forever() - server = SimpleXMLRPCServer(("localhost", 8000), allow_none=True) - print "Listening on port 8000..." - server.register_function(insert, "insert") - server.serve_forever() + .. versionadded:: 0.6 + The :class:`DBXMLRPC` class. - .. versionadded:: 0.6 - The :class:`DBXMLRPC` class. + """ + def __init__(self, url, identify=None, frequency=Consts.CDefXMLRPCStatsGenFreq): + """ The creator of DBXMLRPC Class """ - """ - def __init__(self, url, identify=None, frequency=Consts.CDefXMLRPCStatsGenFreq): - """ The creator of DBXMLRPC Class """ + super(DBXMLRPC, self).__init__(frequency, identify) + self.xmlrpclibmod = None - super(DBXMLRPC, self).__init__(frequency, identify) - self.xmlrpclibmod = None + self.url = url + self.proxy = None - self.url = url - self.proxy = None + def __repr__(self): + """ The string representation of adapter """ + ret = "DBXMLRPC DB Adapter [URL='%s', identify='%s']" % (self.url, self.getIdentify()) + return ret - def __repr__(self): - """ The string representation of adapter """ - ret = "DBXMLRPC DB Adapter [URL='%s', identify='%s']" % (self.url, self.getIdentify()) - return ret + def open(self, ga_engine): + """ Open the XML RPC Server proxy - def open(self, ga_engine): - """ Open the XML RPC Server proxy + :param ga_engine: the GA Engine - :param ga_engine: the GA Engine + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + if self.xmlrpclibmod is None: + logging.debug("Loding the xmlrpclib module...") + self.xmlrpclibmod = Util.importSpecial("xmlrpclib") - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - if self.xmlrpclibmod is None: - logging.debug("Loding the xmlrpclib module...") - self.xmlrpclibmod = Util.importSpecial("xmlrpclib") + logging.debug("Opening the XML RPC Server Proxy on %s", self.url) + self.proxy = self.xmlrpclibmod.ServerProxy(self.url, allow_none=True) - logging.debug("Opening the XML RPC Server Proxy on %s", self.url) - self.proxy = self.xmlrpclibmod.ServerProxy(self.url, allow_none=True) + def insert(self, ga_engine): + """ Calls the XML RPC procedure - def insert(self, ga_engine): - """ Calls the XML RPC procedure + :param ga_engine: the GA Engine - :param ga_engine: the GA Engine + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + stats = ga_engine.getStatistics() + generation = ga_engine.getCurrentGeneration() + di = stats.internalDict.copy() + di.update({"identify": self.getIdentify(), "generation": generation}) + self.proxy.insert(di) - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - stats = ga_engine.getStatistics() - generation = ga_engine.getCurrentGeneration() - di = stats.internalDict.copy() - di.update({"identify": self.getIdentify(), "generation": generation}) - self.proxy.insert(di) class DBVPythonGraph(DBBaseAdapter): - """ The DBVPythonGraph Class - A DB Adapter for real-time visualization using VPython - - Inheritance diagram for :class:`DBAdapters.DBVPythonGraph`: - - .. inheritance-diagram:: DBAdapters.DBVPythonGraph - - .. note:: to use this DB Adapter, you **must** install VPython first. - - Example: - >>> adapter = DBAdapters.DBVPythonGraph(identify="run_01", frequency = 1) - >>> ga_engine.setDBAdapter(adapter) - - :param identify: the identify of the run - :param genmax: use the generations as max value for x-axis, default False - :param frequency: the generational dump frequency - - .. versionadded:: 0.6 - The *DBVPythonGraph* class. - """ - - def __init__(self, identify=None, frequency=20, genmax=False): - super(DBVPythonGraph, self).__init__(frequency, identify) - self.genmax = genmax - self.vtkGraph = None - self.curveMin = None - self.curveMax = None - self.curveDev = None - self.curveAvg = None - - def makeDisplay(self, title_sec, x, y, ga_engine): - """ Used internally to create a new display for VPython. - - :param title_sec: the title of the window - :param x: the x position of the window - :param y: the y position of the window - :param ga_engine: the GA Engine - - :rtype: the window (the return of gdisplay call) - """ - title = "Pyevolve v.%s - %s - id [%s]" % (__version__, title_sec, self.identify) - if self.genmax: - disp = self.vtkGraph.gdisplay(title=title, xtitle='Generation', ytitle=title_sec, - xmax=ga_engine.getGenerations(), xmin=0., width=500, - height=250, x=x, y=y) - else: - disp = self.vtkGraph.gdisplay(title=title, xtitle='Generation', ytitle=title_sec, - xmin=0., width=500, height=250, x=x, y=y) - return disp - - def open(self, ga_engine): - """ Imports the VPython module and creates the four graph windows - - :param ga_engine: the GA Engine - """ - logging.debug("Loading visual.graph (VPython) module...") - if self.vtkGraph is None: - self.vtkGraph = Util.importSpecial("visual.graph").graph - - display_rawmin = self.makeDisplay("Raw Score (min)", 0, 0, ga_engine) - display_rawmax = self.makeDisplay("Raw Score (max)", 0, 250, ga_engine) - display_rawdev = self.makeDisplay("Raw Score (std. dev.)", 500, 0, ga_engine) - display_rawavg = self.makeDisplay("Raw Score (avg)", 500, 250, ga_engine) - - self.curveMin = self.vtkGraph.gcurve(color=self.vtkGraph.color.red, gdisplay=display_rawmin) - self.curveMax = self.vtkGraph.gcurve(color=self.vtkGraph.color.green, gdisplay=display_rawmax) - self.curveDev = self.vtkGraph.gcurve(color=self.vtkGraph.color.blue, gdisplay=display_rawdev) - self.curveAvg = self.vtkGraph.gcurve(color=self.vtkGraph.color.orange, gdisplay=display_rawavg) - - def insert(self, ga_engine): - """ Plot the current statistics to the graphs - - :param ga_engine: the GA Engine - """ - stats = ga_engine.getStatistics() - generation = ga_engine.getCurrentGeneration() - - self.curveMin.plot(pos=(generation, stats["rawMin"])) - self.curveMax.plot(pos=(generation, stats["rawMax"])) - self.curveDev.plot(pos=(generation, stats["rawDev"])) - self.curveAvg.plot(pos=(generation, stats["rawAve"])) + """ The DBVPythonGraph Class - A DB Adapter for real-time visualization using VPython + + Inheritance diagram for :class:`DBAdapters.DBVPythonGraph`: + + .. inheritance-diagram:: DBAdapters.DBVPythonGraph + + .. note:: to use this DB Adapter, you **must** install VPython first. + + Example: + >>> adapter = DBAdapters.DBVPythonGraph(identify="run_01", frequency = 1) + >>> ga_engine.setDBAdapter(adapter) + + :param identify: the identify of the run + :param genmax: use the generations as max value for x-axis, default False + :param frequency: the generational dump frequency + + .. versionadded:: 0.6 + The *DBVPythonGraph* class. + """ + + def __init__(self, identify=None, frequency=20, genmax=False): + super(DBVPythonGraph, self).__init__(frequency, identify) + self.genmax = genmax + self.vtkGraph = None + self.curveMin = None + self.curveMax = None + self.curveDev = None + self.curveAvg = None + + def makeDisplay(self, title_sec, x, y, ga_engine): + """ Used internally to create a new display for VPython. + + :param title_sec: the title of the window + :param x: the x position of the window + :param y: the y position of the window + :param ga_engine: the GA Engine + + :rtype: the window (the return of gdisplay call) + """ + title = "Pyevolve v.%s - %s - id [%s]" % (__version__, title_sec, self.identify) + if self.genmax: + disp = self.vtkGraph.gdisplay(title=title, xtitle='Generation', ytitle=title_sec, + xmax=ga_engine.getGenerations(), xmin=0., width=500, + height=250, x=x, y=y) + else: + disp = self.vtkGraph.gdisplay(title=title, xtitle='Generation', ytitle=title_sec, + xmin=0., width=500, height=250, x=x, y=y) + return disp + + def open(self, ga_engine): + """ Imports the VPython module and creates the four graph windows + + :param ga_engine: the GA Engine + """ + logging.debug("Loading visual.graph (VPython) module...") + if self.vtkGraph is None: + self.vtkGraph = Util.importSpecial("visual.graph").graph + + display_rawmin = self.makeDisplay("Raw Score (min)", 0, 0, ga_engine) + display_rawmax = self.makeDisplay("Raw Score (max)", 0, 250, ga_engine) + display_rawdev = self.makeDisplay("Raw Score (std. dev.)", 500, 0, ga_engine) + display_rawavg = self.makeDisplay("Raw Score (avg)", 500, 250, ga_engine) + + self.curveMin = self.vtkGraph.gcurve(color=self.vtkGraph.color.red, gdisplay=display_rawmin) + self.curveMax = self.vtkGraph.gcurve(color=self.vtkGraph.color.green, gdisplay=display_rawmax) + self.curveDev = self.vtkGraph.gcurve(color=self.vtkGraph.color.blue, gdisplay=display_rawdev) + self.curveAvg = self.vtkGraph.gcurve(color=self.vtkGraph.color.orange, gdisplay=display_rawavg) + + def insert(self, ga_engine): + """ Plot the current statistics to the graphs + + :param ga_engine: the GA Engine + """ + stats = ga_engine.getStatistics() + generation = ga_engine.getCurrentGeneration() + + self.curveMin.plot(pos=(generation, stats["rawMin"])) + self.curveMax.plot(pos=(generation, stats["rawMax"])) + self.curveDev.plot(pos=(generation, stats["rawDev"])) + self.curveAvg.plot(pos=(generation, stats["rawAve"])) + class DBMySQLAdapter(DBBaseAdapter): - """ DBMySQLAdapter Class - Adapter to dump data in MySql database server - - Inheritance diagram for :class:`DBAdapters.DBMySQLAdapter`: - - .. inheritance-diagram:: DBAdapters.DBMySQLAdapter - - Example: - >>> dbadapter = DBMySQLAdapter("pyevolve_username", "password", identify="run1") - - or - - >>> dbadapter = DBMySQLAdapter(user="username", passwd="password", - ... host="mysqlserver.com.br", port=3306, db="pyevolve_db") - - When you run some GA for the first time, you need to create the database, for this, you - must use the *resetDB* parameter as True. - - This parameter will erase all the database tables and will create the new ones. - The *resetDB* parameter is different from the *resetIdentify* parameter, the *resetIdentify* - only erases the rows with the same "identify" name, and *resetDB* will drop and recreate - the tables. - - :param user: mysql username (must have permission to create, drop, insert, etc.. on tables - :param passwd: the user password on MySQL server - :param host: the hostname, default is "localhost" - :param port: the port, default is 3306 - :param db: the database name, default is "pyevolve" - :param identify: the identify if the run - :param resetDB: if True, the database structure will be recreated - :param resetIdentify: if True, the identify with the same name will be overwrite with new data - :param frequency: the generational dump frequency - :param commit_freq: the commit frequency - """ - - def __init__(self, user, passwd, host=Consts.CDefMySQLDBHost, port=Consts.CDefMySQLDBPort, - db=Consts.CDefMySQLDBName, identify=None, resetDB=False, resetIdentify=True, - frequency=Consts.CDefMySQLStatsGenFreq, commit_freq=Consts.CDefMySQLStatsCommitFreq): - """ The creator of the DBSQLite Class """ - - super(DBMySQLAdapter, self).__init__(frequency, identify) - - self.mysqldbmod = None - self.connection = None - self.resetDB = resetDB - self.resetIdentify = resetIdentify - self.db = db - self.host = host - self.port = port - self.user = user - self.passwd = passwd - self.typeDict = {types.FloatType: "DOUBLE(14,6)"} - self.cursorPool = None - self.commitFreq = commit_freq - - def __repr__(self): - """ The string representation of adapter """ - ret = "DBMySQLAdapter DB Adapter [identify='%s', host='%s', username='%s', db='%s']" % (self.getIdentify(), - self.host, self.user, self.db) - return ret - - def open(self, ga_engine): - """ Open the database connection - - :param ga_engine: the GA Engine - - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - if self.mysqldbmod is None: - logging.debug("Loading MySQLdb module...") - self.mysqldbmod = Util.importSpecial("MySQLdb") - - logging.debug("Opening database, host=%s", self.host) - self.connection = self.mysqldbmod.connect(host=self.host, user=self.user, - passwd=self.passwd, db=self.db, - port=self.port) - temp_stats = Statistics.Statistics() - self.createStructure(temp_stats) - - if self.resetDB: - self.resetStructure(Statistics.Statistics()) - - if self.resetIdentify: - self.resetTableIdentify() - - def commitAndClose(self): - """ Commit changes on database and closes connection """ - self.commit() - self.close() - - def close(self): - """ Close the database connection """ - logging.debug("Closing database.") - if self.cursorPool: - self.cursorPool.close() - self.cursorPool = None - self.connection.close() - - def commit(self): - """ Commit changes to database """ - logging.debug("Commiting changes to database.") - self.connection.commit() - - def getCursor(self): - """ Return a cursor from the pool - - :rtype: the cursor - - """ - if not self.cursorPool: - logging.debug("Creating new cursor for database...") - self.cursorPool = self.connection.cursor() - return self.cursorPool - else: - return self.cursorPool - - def createStructure(self, stats): - """ Create table using the Statistics class structure - - :param stats: the statistics object - - """ - c = self.getCursor() - pstmt = "create table if not exists %s(identify VARCHAR(80), generation INTEGER, " % (Consts.CDefMySQLDBTable) - for k, v in stats.items(): - pstmt += "%s %s, " % (k, self.typeDict[type(v)]) - pstmt = pstmt[:-2] + ")" - logging.debug("Creating table %s: %s.", Consts.CDefSQLiteDBTable, pstmt) - c.execute(pstmt) - - pstmt = """create table if not exists %s(identify VARCHAR(80), generation INTEGER, - individual INTEGER, fitness DOUBLE(14,6), raw DOUBLE(14,6))""" % (Consts.CDefMySQLDBTablePop) - logging.debug("Creating table %s: %s.", Consts.CDefMySQLDBTablePop, pstmt) - c.execute(pstmt) - self.commit() - - def resetTableIdentify(self): - """ Delete all records on the table with the same Identify """ - c = self.getCursor() - stmt = "delete from %s where identify = '%s'" % (Consts.CDefMySQLDBTable, self.getIdentify()) - stmt2 = "delete from %s where identify = '%s'" % (Consts.CDefMySQLDBTablePop, self.getIdentify()) - - logging.debug("Erasing data from the tables with the identify = %s", self.getIdentify()) - c.execute(stmt) - c.execute(stmt2) - - self.commit() - - def resetStructure(self, stats): - """ Deletes de current structure and calls createStructure - - :param stats: the statistics object - - """ - logging.debug("Reseting structure, droping table and creating new empty table.") - c = self.getCursor() - c.execute("drop table if exists %s" % (Consts.CDefMySQLDBTable,)) - c.execute("drop table if exists %s" % (Consts.CDefMySQLDBTablePop,)) - self.commit() - self.createStructure(stats) - - def insert(self, ga_engine): - """ Inserts the statistics data to database - - :param ga_engine: the GA Engine - - .. versionchanged:: 0.6 - The method now receives the *ga_engine* parameter. - """ - stats = ga_engine.getStatistics() - population = ga_engine.getPopulation() - generation = ga_engine.getCurrentGeneration() - - c = self.getCursor() - pstmt = "insert into " + Consts.CDefMySQLDBTable + " values (%s, %s, " - for i in xrange(len(stats)): - pstmt += "%s, " - pstmt = pstmt[:-2] + ")" - c.execute(pstmt, (self.getIdentify(), generation) + stats.asTuple()) - - pstmt = "insert into " + Consts.CDefMySQLDBTablePop + " values(%s, %s, %s, %s, %s)" - - tups = [] - for i in xrange(len(population)): - ind = population[i] - tups.append((self.getIdentify(), generation, i, ind.fitness, ind.score)) - - c.executemany(pstmt, tups) - if (generation % self.commitFreq == 0): - self.commit() + """ DBMySQLAdapter Class - Adapter to dump data in MySql database server + + Inheritance diagram for :class:`DBAdapters.DBMySQLAdapter`: + + .. inheritance-diagram:: DBAdapters.DBMySQLAdapter + + Example: + >>> dbadapter = DBMySQLAdapter("pyevolve_username", "password", identify="run1") + + or + + >>> dbadapter = DBMySQLAdapter(user="username", passwd="password", + ... host="mysqlserver.com.br", port=3306, db="pyevolve_db") + + When you run some GA for the first time, you need to create the database, for this, you + must use the *resetDB* parameter as True. + + This parameter will erase all the database tables and will create the new ones. + The *resetDB* parameter is different from the *resetIdentify* parameter, the *resetIdentify* + only erases the rows with the same "identify" name, and *resetDB* will drop and recreate + the tables. + + :param user: mysql username (must have permission to create, drop, insert, etc.. on tables + :param passwd: the user password on MySQL server + :param host: the hostname, default is "localhost" + :param port: the port, default is 3306 + :param db: the database name, default is "pyevolve" + :param identify: the identify if the run + :param resetDB: if True, the database structure will be recreated + :param resetIdentify: if True, the identify with the same name will be overwrite with new data + :param frequency: the generational dump frequency + :param commit_freq: the commit frequency + """ + + def __init__(self, user, passwd, host=Consts.CDefMySQLDBHost, port=Consts.CDefMySQLDBPort, + db=Consts.CDefMySQLDBName, identify=None, resetDB=False, resetIdentify=True, + frequency=Consts.CDefMySQLStatsGenFreq, commit_freq=Consts.CDefMySQLStatsCommitFreq): + """ The creator of the DBSQLite Class """ + + super(DBMySQLAdapter, self).__init__(frequency, identify) + + self.mysqldbmod = None + self.connection = None + self.resetDB = resetDB + self.resetIdentify = resetIdentify + self.db = db + self.host = host + self.port = port + self.user = user + self.passwd = passwd + self.typeDict = {float: "DOUBLE(14,6)"} + self.cursorPool = None + self.commitFreq = commit_freq + + def __repr__(self): + """ The string representation of adapter """ + ret = "DBMySQLAdapter DB Adapter [identify='%s', host='%s', username='%s', db='%s']" % \ + (self.getIdentify(), self.host, self.user, self.db) + return ret + + def open(self, ga_engine): + """ Open the database connection + + :param ga_engine: the GA Engine + + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + if self.mysqldbmod is None: + logging.debug("Loading MySQLdb module...") + self.mysqldbmod = Util.importSpecial("MySQLdb") + + logging.debug("Opening database, host=%s", self.host) + self.connection = self.mysqldbmod.connect(host=self.host, user=self.user, + passwd=self.passwd, db=self.db, + port=self.port) + temp_stats = Statistics.Statistics() + self.createStructure(temp_stats) + + if self.resetDB: + self.resetStructure(Statistics.Statistics()) + + if self.resetIdentify: + self.resetTableIdentify() + + def commitAndClose(self): + """ Commit changes on database and closes connection """ + self.commit() + self.close() + + def close(self): + """ Close the database connection """ + logging.debug("Closing database.") + if self.cursorPool: + self.cursorPool.close() + self.cursorPool = None + self.connection.close() + + def commit(self): + """ Commit changes to database """ + logging.debug("Commiting changes to database.") + self.connection.commit() + + def getCursor(self): + """ Return a cursor from the pool + + :rtype: the cursor + + """ + if not self.cursorPool: + logging.debug("Creating new cursor for database...") + self.cursorPool = self.connection.cursor() + return self.cursorPool + else: + return self.cursorPool + + def createStructure(self, stats): + """ Create table using the Statistics class structure + + :param stats: the statistics object + + """ + c = self.getCursor() + pstmt = "create table if not exists %s(identify VARCHAR(80), generation INTEGER, " % (Consts.CDefMySQLDBTable) + for k, v in list(stats.items()): + pstmt += "%s %s, " % (k, self.typeDict[type(v)]) + pstmt = pstmt[:-2] + ")" + logging.debug("Creating table %s: %s.", Consts.CDefSQLiteDBTable, pstmt) + c.execute(pstmt) + + pstmt = """create table if not exists %s(identify VARCHAR(80), generation INTEGER, + individual INTEGER, fitness DOUBLE(14,6), raw DOUBLE(14,6))""" % (Consts.CDefMySQLDBTablePop) + logging.debug("Creating table %s: %s.", Consts.CDefMySQLDBTablePop, pstmt) + c.execute(pstmt) + self.commit() + + def resetTableIdentify(self): + """ Delete all records on the table with the same Identify """ + c = self.getCursor() + stmt = "delete from %s where identify = '%s'" % (Consts.CDefMySQLDBTable, self.getIdentify()) + stmt2 = "delete from %s where identify = '%s'" % (Consts.CDefMySQLDBTablePop, self.getIdentify()) + + logging.debug("Erasing data from the tables with the identify = %s", self.getIdentify()) + c.execute(stmt) + c.execute(stmt2) + + self.commit() + + def resetStructure(self, stats): + """ Deletes de current structure and calls createStructure + + :param stats: the statistics object + + """ + logging.debug("Reseting structure, droping table and creating new empty table.") + c = self.getCursor() + c.execute("drop table if exists %s" % (Consts.CDefMySQLDBTable,)) + c.execute("drop table if exists %s" % (Consts.CDefMySQLDBTablePop,)) + self.commit() + self.createStructure(stats) + + def insert(self, ga_engine): + """ Inserts the statistics data to database + + :param ga_engine: the GA Engine + + .. versionchanged:: 0.6 + The method now receives the *ga_engine* parameter. + """ + stats = ga_engine.getStatistics() + population = ga_engine.getPopulation() + generation = ga_engine.getCurrentGeneration() + + c = self.getCursor() + pstmt = "insert into " + Consts.CDefMySQLDBTable + " values (%s, %s, " + for i in range(len(stats)): + pstmt += "%s, " + pstmt = pstmt[:-2] + ")" + c.execute(pstmt, (self.getIdentify(), generation) + stats.asTuple()) + + pstmt = "insert into " + Consts.CDefMySQLDBTablePop + " values(%s, %s, %s, %s, %s)" + + tups = [] + for i in range(len(population)): + ind = population[i] + tups.append((self.getIdentify(), generation, i, ind.fitness, ind.score)) + + c.executemany(pstmt, tups) + if (generation % self.commitFreq == 0): + self.commit() diff --git a/pyevolve/FunctionSlot.py b/pyevolve/FunctionSlot.py index e5145e0..ea449b0 100644 --- a/pyevolve/FunctionSlot.py +++ b/pyevolve/FunctionSlot.py @@ -12,191 +12,192 @@ """ from random import uniform as rand_uniform -from types import BooleanType -import Util +from . import Util +import collections + class FunctionSlot(object): - """ FunctionSlot Class - The function slot - - Example: - >>> genome.evaluator.set(eval_func) - >>> genome.evaluator[0] - - >>> genome.evaluator - Slot [Evaluation Function] (Count: 1) - Name: eval_func - >>> genome.evaluator.clear() - >>> genome.evaluator - Slot [Evaluation Function] (Count: 0) - No function - - You can add weight to functions when using the `rand_apply` paramter: - >>> genome.evaluator.set(eval_main, 0.9) - >>> genome.evaluator.add(eval_sec, 0.3) - >>> genome.evaluator.setRandomApply() - - In the above example, the function *eval_main* will be called with 90% of - probability and the *eval_sec* will be called with 30% of probability. - - There are another way to add functions too: - >>> genome.evaluator += eval_func - - :param name: the slot name - :param rand_apply: if True, just one of the functions in the slot - will be applied, this function is randomly picked based - on the weight of the function added. - - """ - - def __init__(self, name="Anonymous Function", rand_apply=False): - """ The creator of the FunctionSlot Class """ - self.funcList = [] - self.funcWeights = [] - self.slotName = name - self.rand_apply = rand_apply - - def __typeCheck(self, func): - """ Used internally to check if a function passed to the - function slot is callable. Otherwise raises a TypeError exception. - - :param func: the function object - """ - if not callable(func): - Util.raiseException("The function must be a method or function", TypeError) - - def __iadd__(self, func): - """ To add more functions using the += operator - - .. versionadded:: 0.6 - The __iadd__ method. - """ - self.__typeCheck(func) - self.funcList.append(func) - return self - - def __getitem__(self, index): - """ Used to retrieve some slot function index """ - return self.funcList[index] - - def __setitem__(self, index, value): - """ Used to set the index slot function """ - self.__typeCheck(value) - self.funcList[index] = value - - def __iter__(self): - """ Return the function list iterator """ - return iter(self.funcList) - - def __len__(self): - """ Return the number of functions on the slot - - .. versionadded:: 0.6 - The *__len__* method - """ - return len(self.funcList) - - def setRandomApply(self, flag=True): - """ Sets the random function application, in this mode, the - function will randomly choose one slot to apply - - :param flag: True or False - - """ - if type(flag) != BooleanType: - Util.raiseException("Random option must be True or False", TypeError) - - self.rand_apply = flag - - def clear(self): - """ Used to clear the functions in the slot """ - if len(self.funcList) > 0: - del self.funcList[:] - del self.funcWeights[:] - - def add(self, func, weight=0.5): - """ Used to add a function to the slot - - :param func: the function to be added in the slot - :param weight: used when you enable the *random apply*, it's the weight - of the function for the random selection - - .. versionadded:: 0.6 - The `weight` parameter. - - """ - self.__typeCheck(func) - self.funcList.append(func) - self.funcWeights.append(weight) - - def isEmpty(self): - """ Return true if the function slot is empy """ - return (len(self.funcList) == 0) - - def set(self, func, weight=0.5): - """ Used to clear all functions in the slot and add one - - :param func: the function to be added in the slot - :param weight: used when you enable the *random apply*, it's the weight - of the function for the random selection - - .. versionadded:: 0.6 - The `weight` parameter. - - .. note:: the method *set* of the function slot remove all previous - functions added to the slot. - """ - self.clear() - self.__typeCheck(func) - self.add(func, weight) - - def apply(self, index, obj, **args): - """ Apply the index function - - :param index: the index of the function - :param obj: this object is passes as parameter to the function - :param args: this args dictionary is passed to the function - - """ - if len(self.funcList) <= 0: - raise Exception("No function defined: " + self.slotName) - return self.funcList[index](obj, **args) - - def applyFunctions(self, obj=None, **args): - """ Generator to apply all function slots in obj - - :param obj: this object is passes as parameter to the function - :param args: this args dictionary is passed to the function - - """ - if len(self.funcList) <= 0: - Util.raiseException("No function defined: " + self.slotName) - - if not self.rand_apply: - for f in self.funcList: - yield f(obj, **args) - else: - v = rand_uniform(0, 1) - fobj = None - for func, weight in zip(self.funcList, self.funcWeights): - fobj = func - if v < weight: - break - v = v - weight + """ FunctionSlot Class - The function slot + + Example: + >>> genome.evaluator.set(eval_func) + >>> genome.evaluator[0] + + >>> genome.evaluator + Slot [Evaluation Function] (Count: 1) + Name: eval_func + >>> genome.evaluator.clear() + >>> genome.evaluator + Slot [Evaluation Function] (Count: 0) + No function + + You can add weight to functions when using the `rand_apply` paramter: + >>> genome.evaluator.set(eval_main, 0.9) + >>> genome.evaluator.add(eval_sec, 0.3) + >>> genome.evaluator.setRandomApply() + + In the above example, the function *eval_main* will be called with 90% of + probability and the *eval_sec* will be called with 30% of probability. + + There are another way to add functions too: + >>> genome.evaluator += eval_func + + :param name: the slot name + :param rand_apply: if True, just one of the functions in the slot + will be applied, this function is randomly picked based + on the weight of the function added. + + """ + + def __init__(self, name="Anonymous Function", rand_apply=False): + """ The creator of the FunctionSlot Class """ + self.funcList = [] + self.funcWeights = [] + self.slotName = name + self.rand_apply = rand_apply + + def __typeCheck(self, func): + """ Used internally to check if a function passed to the + function slot is callable. Otherwise raises a TypeError exception. + + :param func: the function object + """ + if not isinstance(func, collections.Callable): + Util.raiseException("The function must be a method or function", TypeError) + + def __iadd__(self, func): + """ To add more functions using the += operator + + .. versionadded:: 0.6 + The __iadd__ method. + """ + self.__typeCheck(func) + self.funcList.append(func) + return self + + def __getitem__(self, index): + """ Used to retrieve some slot function index """ + return self.funcList[index] + + def __setitem__(self, index, value): + """ Used to set the index slot function """ + self.__typeCheck(value) + self.funcList[index] = value + + def __iter__(self): + """ Return the function list iterator """ + return iter(self.funcList) + + def __len__(self): + """ Return the number of functions on the slot + + .. versionadded:: 0.6 + The *__len__* method + """ + return len(self.funcList) + + def setRandomApply(self, flag=True): + """ Sets the random function application, in this mode, the + function will randomly choose one slot to apply + + :param flag: True or False + + """ + if not isinstance(flag, bool): + Util.raiseException("Random option must be True or False", TypeError) + + self.rand_apply = flag + + def clear(self): + """ Used to clear the functions in the slot """ + if len(self.funcList) > 0: + del self.funcList[:] + del self.funcWeights[:] + + def add(self, func, weight=0.5): + """ Used to add a function to the slot + + :param func: the function to be added in the slot + :param weight: used when you enable the *random apply*, it's the weight + of the function for the random selection + + .. versionadded:: 0.6 + The `weight` parameter. + + """ + self.__typeCheck(func) + self.funcList.append(func) + self.funcWeights.append(weight) + + def isEmpty(self): + """ Return true if the function slot is empy """ + return (len(self.funcList) == 0) + + def set(self, func, weight=0.5): + """ Used to clear all functions in the slot and add one + + :param func: the function to be added in the slot + :param weight: used when you enable the *random apply*, it's the weight + of the function for the random selection + + .. versionadded:: 0.6 + The `weight` parameter. + + .. note:: the method *set* of the function slot remove all previous + functions added to the slot. + """ + self.clear() + self.__typeCheck(func) + self.add(func, weight) + + def apply(self, index, obj, **args): + """ Apply the index function + + :param index: the index of the function + :param obj: this object is passes as parameter to the function + :param args: this args dictionary is passed to the function + + """ + if len(self.funcList) <= 0: + raise Exception("No function defined: " + self.slotName) + return self.funcList[index](obj, **args) + + def applyFunctions(self, obj=None, **args): + """ Generator to apply all function slots in obj + + :param obj: this object is passes as parameter to the function + :param args: this args dictionary is passed to the function + + """ + if len(self.funcList) <= 0: + Util.raiseException("No function defined: " + self.slotName) + + if not self.rand_apply: + for f in self.funcList: + yield f(obj, **args) + else: + v = rand_uniform(0, 1) + fobj = None + for func, weight in zip(self.funcList, self.funcWeights): + fobj = func + if v < weight: + break + v = v - weight - yield fobj(obj, **args) + yield fobj(obj, **args) - def __repr__(self): - """ String representation of FunctionSlot """ - strRet = "Slot [%s] (Count: %d)\n" % (self.slotName, len(self.funcList)) + def __repr__(self): + """ String representation of FunctionSlot """ + strRet = "Slot [%s] (Count: %d)\n" % (self.slotName, len(self.funcList)) - if len(self.funcList) <= 0: - strRet += "\t\tNo function\n" - return strRet + if len(self.funcList) <= 0: + strRet += "\t\tNo function\n" + return strRet - for f, w in zip(self.funcList, self.funcWeights): - strRet += "\t\tName: %s - Weight: %.2f\n" % (f.func_name, w) - if f.func_doc: - strRet += "\t\tDoc: " + f.func_doc + "\n" + for f, w in zip(self.funcList, self.funcWeights): + strRet += "\t\tName: %s - Weight: %.2f\n" % (f.__name__, w) + if f.__doc__: + strRet += "\t\tDoc: " + f.__doc__ + "\n" - return strRet + return strRet diff --git a/pyevolve/G1DBinaryString.py b/pyevolve/G1DBinaryString.py index a1fb683..daa08a9 100644 --- a/pyevolve/G1DBinaryString.py +++ b/pyevolve/G1DBinaryString.py @@ -34,136 +34,141 @@ """ -from GenomeBase import GenomeBase, G1DBase -import Consts -import Util +from .GenomeBase import GenomeBase, G1DBase +from . import Consts +from . import Util -class G1DBinaryString(G1DBase): - """ G1DBinaryString Class - The 1D Binary String chromosome - - Inheritance diagram for :class:`G1DBinaryString.G1DBinaryString`: - - .. inheritance-diagram:: G1DBinaryString.G1DBinaryString - - This chromosome class extends the :class:`GenomeBase.G1DBase` class. - Example: - >>> genome = G1DBinaryString.G1DBinaryString(5) - - :param length: the 1D Binary String size - - """ - __slots__ = ["stringLength"] +class G1DBinaryString(G1DBase): + """ G1DBinaryString Class - The 1D Binary String chromosome - def __init__(self, length=10): - """ The initializator of G1DList representation """ - super(G1DBinaryString, self).__init__(length) - self.genomeList = [] - self.stringLength = length - self.initializator.set(Consts.CDefG1DBinaryStringInit) - self.mutator.set(Consts.CDefG1DBinaryStringMutator) - self.crossover.set(Consts.CDefG1DBinaryStringCrossover) + Inheritance diagram for :class:`G1DBinaryString.G1DBinaryString`: + + .. inheritance-diagram:: G1DBinaryString.G1DBinaryString + + This chromosome class extends the :class:`GenomeBase.G1DBase` class. + + Example: + >>> genome = G1DBinaryString.G1DBinaryString(5) - def __setitem__(self, key, value): - """ Set the specified value for an gene of List - - >>> g = G1DBinaryString(5) - >>> for i in xrange(len(g)): - ... g.append(1) - >>> g[4] = 0 - >>> g[4] - 0 - - """ - if value not in (0, 1): - Util.raiseException("The value must be zero (0) or one (1), used (%s)" % value, ValueError) - G1DBase.__setitem__(self, key, value) - - def __repr__(self): - """ Return a string representation of Genome """ - ret = GenomeBase.__repr__(self) - ret += "- G1DBinaryString\n" - ret += "\tString length:\t %s\n" % (self.getListSize(),) - ret += "\tString:\t\t %s\n\n" % (self.getBinary(),) - return ret - - def getDecimal(self): - """ Converts the binary string to decimal representation - - Example: - >>> g = G1DBinaryString(5) - >>> for i in xrange(len(g)): - ... g.append(0) - >>> g[3] = 1 - >>> g.getDecimal() - 2 - - :rtype: decimal value - - """ - return int(self.getBinary(), 2) - - def getBinary(self): - """ Returns the binary string representation - - Example: - >>> g = G1DBinaryString(2) - >>> g.append(0) - >>> g.append(1) - >>> g.getBinary() - '01' - - :rtype: the binary string - - """ - return "".join(map(str, self)) - - def append(self, value): - """ Appends an item to the list - - Example: - >>> g = G1DBinaryString(2) - >>> g.append(0) - - :param value: value to be added, 0 or 1 - - """ - if value not in [0, 1]: - Util.raiseException("The value must be 0 or 1", ValueError) - G1DBase.append(self, value) - - def copy(self, g): - """ Copy genome to 'g' - - Example: - >>> g1 = G1DBinaryString(2) - >>> g1.append(0) - >>> g1.append(1) - >>> g2 = G1DBinaryString(2) - >>> g1.copy(g2) - >>> g2[1] - 1 - - :param g: the destination genome - - """ - GenomeBase.copy(self, g) - G1DBase.copy(self, g) - - def clone(self): - """ Return a new instace copy of the genome - - Example: - >>> g = G1DBinaryString(5) - >>> for i in xrange(len(g)): - ... g.append(1) - >>> clone = g.clone() - >>> clone[0] - 1 - - :rtype: the G1DBinaryString instance clone - - """ - newcopy = G1DBinaryString(self.getListSize()) - self.copy(newcopy) - return newcopy + :param length: the 1D Binary String size + + """ + __slots__ = ["stringLength"] + + def __init__(self, length=10): + """ The initializator of G1DList representation """ + super(G1DBinaryString, self).__init__(length) + self.genomeList = [] + self.stringLength = length + self.initializator.set(Consts.CDefG1DBinaryStringInit) + self.mutator.set(Consts.CDefG1DBinaryStringMutator) + self.crossover.set(Consts.CDefG1DBinaryStringCrossover) + + def __setitem__(self, key, value): + """ Set the specified value for an gene of List + + >>> g = G1DBinaryString(5) + >>> for i in xrange(len(g)): + ... g.append(1) + >>> g[4] = 0 + >>> g[4] + 0 + + """ + if isinstance(value, int) and value not in (0, 1): # TODO add type check + Util.raiseException("The value must be zero (0) or one (1), used (%s)" % value, ValueError) + elif isinstance(value, list) and not set(value) <= set([0, 1]): # TODO use {0, 1} notation or list compr. + # if slice notation is used we check all passed values + vals = set(value) - set([0, 1]) # TODO check what to do with slice key + Util.raiseException("The value must be zero (0) or one (1), used (%s)" % vals, ValueError) + G1DBase.__setitem__(self, key, value) + + def __repr__(self): + """ Return a string representation of Genome """ + ret = GenomeBase.__repr__(self) + ret += "- G1DBinaryString\n" + ret += "\tString length:\t %s\n" % (self.getListSize(),) + ret += "\tString:\t\t %s\n\n" % (self.getBinary(),) + return ret + + def getDecimal(self): + """ Converts the binary string to decimal representation + + Example: + >>> g = G1DBinaryString(5) + >>> for i in xrange(len(g)): + ... g.append(0) + >>> g[3] = 1 + >>> g.getDecimal() + 2 + + :rtype: decimal value + + """ + return int(self.getBinary(), 2) + + def getBinary(self): + """ Returns the binary string representation + + Example: + >>> g = G1DBinaryString(2) + >>> g.append(0) + >>> g.append(1) + >>> g.getBinary() + '01' + + :rtype: the binary string + + """ + return "".join(map(str, self)) + + def append(self, value): + """ Appends an item to the list + + Example: + >>> g = G1DBinaryString(2) + >>> g.append(0) + + :param value: value to be added, 0 or 1 + + """ + if value not in [0, 1]: + Util.raiseException("The value must be 0 or 1", ValueError) + G1DBase.append(self, value) + + def copy(self, g): + """ Copy genome to 'g' + + Example: + >>> g1 = G1DBinaryString(2) + >>> g1.append(0) + >>> g1.append(1) + >>> g2 = G1DBinaryString(2) + >>> g1.copy(g2) + >>> g2[1] + 1 + + :param g: the destination genome + + """ + GenomeBase.copy(self, g) + G1DBase.copy(self, g) + + def clone(self): + """ Return a new instace copy of the genome + + Example: + >>> g = G1DBinaryString(5) + >>> for i in xrange(len(g)): + ... g.append(1) + >>> clone = g.clone() + >>> clone[0] + 1 + + :rtype: the G1DBinaryString instance clone + + """ + newcopy = G1DBinaryString(self.getListSize()) + self.copy(newcopy) + return newcopy diff --git a/pyevolve/G1DList.py b/pyevolve/G1DList.py index 62e487b..cff2a54 100644 --- a/pyevolve/G1DList.py +++ b/pyevolve/G1DList.py @@ -34,8 +34,10 @@ ------------------------------------------------------------- """ -from GenomeBase import GenomeBase, G1DBase -import Consts +from future.builtins import range + +from .GenomeBase import GenomeBase, G1DBase +from . import Consts class G1DList(G1DBase): @@ -113,24 +115,24 @@ def __init__(self, size=10, cloning=False): self.mutator.set(Consts.CDefG1DListMutator) self.crossover.set(Consts.CDefG1DListCrossover) - def __mul__(self, other): + def __mul__(self, other): # TODO: __rmul__ is needed """ Multiply every element of G1DList by "other" """ newObj = self.clone() - for i in xrange(len(newObj)): + for i in range(len(newObj)): newObj[i] *= other return newObj - def __add__(self, other): + def __add__(self, other): # TODO: __radd__ is needed """ Plus every element of G1DList by "other" """ newObj = self.clone() - for i in xrange(len(newObj)): + for i in range(len(newObj)): newObj[i] += other return newObj - def __sub__(self, other): + def __sub__(self, other): # TODO: __rsub__ is needed """ Plus every element of G1DList by "other" """ newObj = self.clone() - for i in xrange(len(newObj)): + for i in range(len(newObj)): newObj[i] -= other return newObj diff --git a/pyevolve/G2DBinaryString.py b/pyevolve/G2DBinaryString.py index 2b2bdab..7d5d65e 100644 --- a/pyevolve/G2DBinaryString.py +++ b/pyevolve/G2DBinaryString.py @@ -37,10 +37,11 @@ Class ------------------------------------------------------------- """ +from future.builtins import range -from GenomeBase import GenomeBase -import Consts -import Util +from .GenomeBase import GenomeBase +from . import Consts +from . import Util class G2DBinaryString(GenomeBase): @@ -68,7 +69,7 @@ def __init__(self, height, width): self.width = width self.genomeString = [None] * height - for i in xrange(height): + for i in range(height): self.genomeString[i] = [None] * width self.initializator.set(Consts.CDefG2DBinaryStringInit) @@ -167,7 +168,7 @@ def clearString(self): del self.genomeString[:] self.genomeString = [None] * self.height - for i in xrange(self.height): + for i in range(self.height): self.genomeString[i] = [None] * self.width def copy(self, g): @@ -182,7 +183,7 @@ def copy(self, g): GenomeBase.copy(self, g) g.height = self.height g.width = self.width - for i in xrange(self.height): + for i in range(self.height): g.genomeString[i] = self.genomeString[i][:] def clone(self): diff --git a/pyevolve/G2DList.py b/pyevolve/G2DList.py index 6c864e3..e9736e8 100644 --- a/pyevolve/G2DList.py +++ b/pyevolve/G2DList.py @@ -34,9 +34,10 @@ """ +from future.builtins import range -from GenomeBase import GenomeBase -import Consts +from .GenomeBase import GenomeBase +from . import Consts class G2DList(GenomeBase): @@ -97,7 +98,7 @@ def __init__(self, height, width, cloning=False): self.width = width self.genomeList = [None] * height - for i in xrange(height): + for i in range(height): self.genomeList[i] = [None] * width if not cloning: @@ -110,7 +111,7 @@ def __eq__(self, other): cond1 = (self.genomeList == other.genomeList) cond2 = (self.height == other.height) cond3 = (self.width == other.width) - return True if cond1 and cond2 and cond3 else False + return True if cond1 and cond2 and cond3 else False # TODO remove bool values, leave only condition def getItem(self, x, y): """ Return the specified gene of List @@ -141,7 +142,7 @@ def setItem(self, x, y, value): """ self.genomeList[x][y] = value - def __getitem__(self, key): + def __getitem__(self, key): # TODO __setitem__ is needed """ Return the specified gene of List """ return self.genomeList[key] @@ -199,7 +200,7 @@ def clearList(self): del self.genomeList[:] self.genomeList = [None] * self.height - for i in xrange(self.height): + for i in range(self.height): self.genomeList[i] = [None] * self.width def copy(self, g): @@ -214,7 +215,7 @@ def copy(self, g): GenomeBase.copy(self, g) g.height = self.height g.width = self.width - for i in xrange(self.height): + for i in range(self.height): g.genomeList[i] = self.genomeList[i][:] def clone(self): diff --git a/pyevolve/GAllele.py b/pyevolve/GAllele.py index 2012568..4f9ebb4 100644 --- a/pyevolve/GAllele.py +++ b/pyevolve/GAllele.py @@ -8,277 +8,286 @@ class that holds the allele types) and all the allele types to use with the supported chromosomes. """ +from future.builtins import range + import random -import Consts -import Util +from . import Consts +from . import Util + class GAlleles(object): - """ 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 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 length 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 position 0:\n" - ret += self.allele_list[0].__repr__() - else: - for i in xrange(len(self)): - ret += "Allele for position %d:\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 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): # deprecated since 2.0, handled by getitem + """ 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,)) # TODO IndexError will be better here + 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 # TODO 'else' might be missed + + 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 length 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 position 0:\n" + ret += self.allele_list[0].__repr__() + else: + for i in range(len(self)): + ret += "Allele for position %d:\n" % (i,) + ret += self.allele_list[i].__repr__() + return ret class GAlleleList(object): - """ 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 __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 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 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): # deprecated since 2.0, handled by getitem + """ 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 length of the options list """ + return len(self.options) - def __len__(self): - """ Returns the length 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(object): - """ 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): - """ Pass in True if the range is real or False if 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]) + # TODO crashes on empty 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]: # TODO type checking is needed + 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 + # TODO might crash if we access minimum immediately after + # processminmax is needed + + 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: # TODO crashes after clear + choice = 0 + else: + choice = random.randint(0, len(self.beginEnd) - 1) # TODO refactor needed + return rand_func(self.beginEnd[choice][0], self.beginEnd[choice][1]) + + def setReal(self, flag=True): + """ Pass in True if the range is real or False if integer + + :param flag: True or False + + """ + self.real = flag # TODO type-checking is needed + + 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 919ad18..3b117f1 100644 --- a/pyevolve/GPopulation.py +++ b/pyevolve/GPopulation.py @@ -32,460 +32,470 @@ """ -import Consts -import Util -from FunctionSlot import FunctionSlot -from Statistics import Statistics +from future.builtins import range +from functools import cmp_to_key + +from . import Consts +from . import Util +from .FunctionSlot import FunctionSlot +from .Statistics import Statistics from math import sqrt as math_sqrt 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(object): - """ 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, None) - - # Statistics - self.statted = False - self.stats = Statistics() - - def setMultiProcessing(self, flag=True, full_copy=False, max_processes=None): - """ 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) - :param max_processes: None (default) or an integer value - - .. 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, max_processes) - - 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 """ - 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 - - len_pop = len(self) - for ind in xrange(len_pop): - raw_sum += self[ind].score - - 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) - - tmpvar = 0.0 - for ind in xrange(len_pop): - s = self[ind].score - self.stats["rawAve"] - s *= s - tmpvar += s + """ 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 range(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, None) + + # Statistics + self.statted = False + self.stats = Statistics() + + def setMultiProcessing(self, flag=True, full_copy=False, max_processes=None): + """ 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) + :param max_processes: None (default) or an integer value + + .. 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, max_processes) + + 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 """ + ret = "- GPopulation\n" + ret += "\tPopulation Size:\t %d\n" % (self.popSize,) + ret += "\tSort Type:\t\t %s\n" % \ + (list(Consts.sortType.keys())[list(Consts.sortType.values()).index(self.sortType)].capitalize(),) + ret += "\tMinimax Type:\t\t %s\n" % \ + (list(Consts.minimaxType.keys())[list(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 + + len_pop = len(self) + for ind in range(len_pop): + raw_sum += self[ind].score + + 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) - tmpvar /= float((len(self) - 1)) - try: - self.stats["rawDev"] = math_sqrt(tmpvar) - except: - self.stats["rawDev"] = 0.0 + tmpvar = 0.0 + for ind in range(len_pop): + s = self[ind].score - self.stats["rawAve"] + s *= s + tmpvar += s - self.stats["rawVar"] = tmpvar + tmpvar /= float((len(self) - 1)) + try: + self.stats["rawDev"] = math_sqrt(tmpvar) + except ValueError: # TODO test needed + self.stats["rawDev"] = 0.0 - self.statted = True + self.stats["rawVar"] = tmpvar - def bestFitness(self, index=0): - """ Return the best scaled fitness individual of population + self.statted = True - :param index: the *index* best individual - :rtype: the individual + def bestFitness(self, index=0): + """ Return the best scaled fitness individual of population - """ - self.sort() - return self.internalPop[index] - - def worstFitness(self): - """ Return the worst scaled fitness individual of the population - - :rtype: the individual + :param index: the *index* best individual + :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(processes=self.multiProcessing[2]) - - # 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 - 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 + """ + 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"]: + # TODO update to proper python3 sorting + # https://docs.python.org/3.3/howto/sorting.html + self.internalPop.sort(key=cmp_to_key(Util.cmp_individual_raw), reverse=rev) + else: + self.scale() + self.internalPop.sort(key=cmp_to_key(Util.cmp_individual_scaled), reverse=rev) + self.internalPopRaw = self.internalPop[:] + self.internalPopRaw.sort(key=cmp_to_key(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 range(self.popSize)] + self.clearFlags() + + def __findIndividual(self, individual, end): + for i in range(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 range(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(processes=self.multiProcessing[2]) + + # 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 range(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 range(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 + 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/GSimpleGA.py b/pyevolve/GSimpleGA.py index 44775d5..8a93e5b 100644 --- a/pyevolve/GSimpleGA.py +++ b/pyevolve/GSimpleGA.py @@ -1,3 +1,4 @@ +from __future__ import print_function """ :mod:`GSimpleGA` -- the genetic algorithm by itself @@ -58,20 +59,22 @@ ------------------------------------------------------------- """ + +from future.builtins import range + import random import logging from time import time -from types import BooleanType from sys import platform as sys_platform from sys import stdout as sys_stdout import code -from GPopulation import GPopulation -from FunctionSlot import FunctionSlot -from GenomeBase import GenomeBase -from DBAdapters import DBBaseAdapter -import Consts -import Util +from .GPopulation import GPopulation +from .FunctionSlot import FunctionSlot +from .GenomeBase import GenomeBase +from .DBAdapters import DBBaseAdapter +from . import Consts +from . import Util import pyevolve # Platform dependant code for the Interactive Mode @@ -214,7 +217,7 @@ def __init__(self, genome, seed=None, interactiveMode=True): if seed: random.seed(seed) - if type(interactiveMode) != BooleanType: + if not isinstance(interactiveMode, bool): Util.raiseException("Interactive Mode option must be True or False", TypeError) if not isinstance(genome, GenomeBase): @@ -360,13 +363,13 @@ def setInteractiveMode(self, flag=True): The *setInteractiveMode* method. """ - if type(flag) != BooleanType: + if not isinstance(flag, bool): Util.raiseException("Interactive Mode option must be True or False", TypeError) self.interactiveMode = flag def __repr__(self): """ The string representation of the GA Engine """ - minimax_type = Consts.minimaxType.keys()[Consts.minimaxType.values().index(self.minimax)] + minimax_type = list(Consts.minimaxType.keys())[list(Consts.minimaxType.values()).index(self.minimax)] ret = "- GSimpleGA\n" ret += "\tGP Mode:\t\t %s\n" % self.getGPMode() ret += "\tPopulation Size:\t %d\n" % self.internalPop.popSize @@ -419,10 +422,10 @@ def setMultiProcessing(self, flag=True, full_copy=False, max_processes=None): The `setMultiProcessing` method. """ - if type(flag) != BooleanType: + if not isinstance(flag, bool): Util.raiseException("Multiprocessing option must be True or False", TypeError) - if type(full_copy) != BooleanType: + if not isinstance(full_copy, bool): Util.raiseException("Multiprocessing 'full_copy' option must be True or False", TypeError) self.internalPop.setMultiProcessing(flag, full_copy, max_processes) @@ -471,7 +474,7 @@ def setSortType(self, sort_type): :param sort_type: the Sort Type """ - if sort_type not in Consts.sortType.values(): + if sort_type not in list(Consts.sortType.values()): Util.raiseException("sort type must be a Consts.sortType type", TypeError) self.internalPop.sortType = sort_type @@ -529,7 +532,7 @@ def setMinimax(self, mtype): :param mtype: the minimax mode, from Consts.minimaxType """ - if mtype not in Consts.minimaxType.values(): + if mtype not in list(Consts.minimaxType.values()): Util.raiseException("Minimax must be maximize or minimize", TypeError) self.minimax = mtype @@ -547,7 +550,7 @@ def setElitism(self, flag): :param flag: True or False """ - if type(flag) != BooleanType: + if not isinstance(flag, bool): Util.raiseException("Elitism option must be True or False", TypeError) self.elitism = flag @@ -597,11 +600,11 @@ def __gp_catch_functions(self, prefix): function_set = {} main_dict = mod_main.__dict__ - for obj, addr in main_dict.items(): + for obj, addr in list(main_dict.items()): if obj[0:len(prefix)] == prefix: try: - op_len = addr.func_code.co_argcount - except: + op_len = addr.__code__.co_argcount + except: # noqa # TODO need to do some investigate here continue function_set[obj] = op_len @@ -645,7 +648,7 @@ def step(self): crossover_empty = self.select(popID=self.currentGeneration).crossover.isEmpty() - for i in xrange(0, size_iterate, 2): + for i in range(0, size_iterate, 2): genomeMom = self.select(popID=self.currentGeneration) genomeDad = self.select(popID=self.currentGeneration) @@ -686,14 +689,14 @@ def step(self): if self.elitism: logging.debug("Doing elitism.") if self.getMinimax() == Consts.minimaxType["maximize"]: - for i in xrange(self.nElitismReplacement): - #re-evaluate before being sure this is the best + for i in range(self.nElitismReplacement): + # re-evaluate before being sure this is the best self.internalPop.bestRaw(i).evaluate() if self.internalPop.bestRaw(i).score > newPop.bestRaw(i).score: newPop[len(newPop) - 1 - i] = self.internalPop.bestRaw(i) elif self.getMinimax() == Consts.minimaxType["minimize"]: - for i in xrange(self.nElitismReplacement): - #re-evaluate before being sure this is the best + for i in range(self.nElitismReplacement): + # re-evaluate before being sure this is the best self.internalPop.bestRaw(i).evaluate() if self.internalPop.bestRaw(i).score < newPop.bestRaw(i).score: newPop[len(newPop) - 1 - i] = self.internalPop.bestRaw(i) @@ -706,9 +709,9 @@ def step(self): self.currentGeneration += 1 if self.max_time: - total_time = time() - self.time_init - if total_time > self.max_time: - return True + total_time = time() - self.time_init + if total_time > self.max_time: + return True return self.currentGeneration == self.nGenerations def printStats(self): @@ -722,7 +725,7 @@ def printStats(self): percent = self.currentGeneration * 100 / float(self.nGenerations) message = "Gen. %d (%.2f%%):" % (self.currentGeneration, percent) logging.info(message) - print message, + print(message, end=" ") sys_stdout.flush() self.internalPop.statistics() stat_ret = self.internalPop.printStats() @@ -731,7 +734,7 @@ def printStats(self): def printTimeElapsed(self): """ Shows the time elapsed since the begin of evolution """ total_time = time() - self.time_init - print "Total time elapsed: %.3f seconds." % total_time + print("Total time elapsed: %.3f seconds." % total_time) return total_time def dumpStatsDB(self): @@ -805,26 +808,26 @@ def evolve(self, freq_stats=0): if stopFlagTerminationCriteria: logging.debug("Evolution stopped by the Termination Criteria !") if freq_stats: - print "\n\tEvolution stopped by Termination Criteria function !\n" + print("\n\tEvolution stopped by Termination Criteria function !\n") break if stopFlagCallback: logging.debug("Evolution stopped by Step Callback function !") if freq_stats: - print "\n\tEvolution stopped by Step Callback function !\n" + print("\n\tEvolution stopped by Step Callback function !\n") break if self.interactiveMode: if sys_platform[:3] == "win": if msvcrt.kbhit(): if ord(msvcrt.getch()) == Consts.CDefESCKey: - print "Loading modules for Interactive Mode...", + print("Loading modules for Interactive Mode...", end=" ") logging.debug( "Windows Interactive Mode key detected ! generation=%d", self.getCurrentGeneration() ) from pyevolve import Interaction - print " done !" + print(" done !") interact_banner = "## Pyevolve v.%s - Interactive Mode ##\n" \ "Press CTRL-Z to quit interactive mode." % (pyevolve.__version__,) session_locals = { @@ -833,18 +836,18 @@ def evolve(self, freq_stats=0): "pyevolve": pyevolve, "it": Interaction, } - print + print() code.interact(interact_banner, local=session_locals) is_interactive_generation = self.getInteractiveGeneration() == self.getCurrentGeneration() if self.getInteractiveGeneration() >= 0 and is_interactive_generation: - print "Loading modules for Interactive Mode...", + print("Loading modules for Interactive Mode...", end=" ") logging.debug( "Manual Interactive Mode key detected ! generation=%d", self.getCurrentGeneration() ) from pyevolve import Interaction - print " done !" + print(" done !") interact_banner = "## Pyevolve v.%s - Interactive Mode ##" % (pyevolve.__version__,) session_locals = { "ga_engine": self, @@ -852,7 +855,7 @@ def evolve(self, freq_stats=0): "pyevolve": pyevolve, "it": Interaction } - print + print() code.interact(interact_banner, local=session_locals) if self.step(): @@ -861,7 +864,7 @@ def evolve(self, freq_stats=0): except KeyboardInterrupt: logging.debug("CTRL-C detected, finishing evolution.") if freq_stats: - print "\n\tA break was detected, you have interrupted the evolution !\n" + print("\n\tA break was detected, you have interrupted the evolution !\n") if freq_stats != 0: self.printStats() diff --git a/pyevolve/GTree.py b/pyevolve/GTree.py index 381580d..7b05a91 100644 --- a/pyevolve/GTree.py +++ b/pyevolve/GTree.py @@ -1,3 +1,4 @@ +from __future__ import print_function, absolute_import """ :mod:`GTree` and GTreeGP -- the tree chromosomes @@ -34,12 +35,11 @@ ------------------------------------------------------------- """ import random -from GenomeBase import GenomeBase, GTreeBase, GTreeNodeBase -import Consts -import Util +from .GenomeBase import GenomeBase, GTreeBase, GTreeNodeBase +from . import Util try: - import pydot + import pydot_ng as pydot HAVE_PYDOT = True except ImportError: HAVE_PYDOT = False @@ -60,6 +60,7 @@ class GTree(GTreeBase): """ def __init__(self, root_node=None): + from . import Consts super(GTree, self).__init__(root_node) self.initializator.set(Consts.CDefGTreeInit) self.mutator.set(Consts.CDefGGTreeMutator) @@ -185,7 +186,7 @@ def buildGTreeGrow(depth, value_callback, max_siblings, max_depth): if depth == max_depth: return n - for i in xrange(random.randint(0, abs(max_siblings))): + for i in range(random.randint(0, abs(max_siblings))): child = buildGTreeGrow(depth + 1, value_callback, max_siblings, max_depth) child.setParent(n) n.addChild(child) @@ -216,7 +217,7 @@ def buildGTreeFull(depth, value_callback, max_siblings, max_depth): else: range_val = random.randint(1, abs(max_siblings)) - for i in xrange(range_val): + for i in range(range_val): child = buildGTreeFull(depth + 1, value_callback, max_siblings, max_depth) child.setParent(n) n.addChild(child) @@ -342,7 +343,9 @@ class GTreeGP(GTreeBase): :param root_node: the Root node of the GP Tree """ + def __init__(self, root_node=None, cloning=False): + from . import Consts super(GTreeGP, self).__init__(root_node) if not cloning: self.initializator.set(Consts.CDefGTreeGPInit) @@ -388,8 +391,10 @@ def writeDotGraph(self, graph, startNode=0): :param graph: the pydot Graph instance :param startNode: used to plot more than one individual """ + from . import Consts + if not HAVE_PYDOT: - print "You must install Pydot to use this feature !" + print("You must install Pydot to use this feature !") return count = startNode @@ -397,7 +402,7 @@ def writeDotGraph(self, graph, startNode=0): nodes_dict = {} import __main__ as main_module - for i in xrange(len(self.nodes_list)): + for i in range(len(self.nodes_list)): newnode = pydot.Node(str(count), style="filled") count += 1 @@ -480,7 +485,7 @@ def getPreOrderExpression(self, start_node=None): all_childs = start_node.getChilds() str_buff += "(" + self.getPreOrderExpression(all_childs[0]) - for index in xrange(1, len(all_childs)): + for index in range(1, len(all_childs)): child = all_childs[index] str_buff += ", " + self.getPreOrderExpression(child) str_buff += ")" @@ -569,7 +574,7 @@ def writePopulationDot(ga_engine, filename, format="jpeg", start=0, end=0): n = 0 end_index = len(pop) if end == 0 else end - for i in xrange(start, end_index): + for i in range(start, end_index): ind = pop[i] subg = pydot.Cluster( "cluster_%d" % i, @@ -606,7 +611,7 @@ def writePopulationDotRaw(ga_engine, filename, start=0, end=0): n = 0 end_index = len(pop) if end == 0 else end - for i in xrange(start, end_index): + for i in range(start, end_index): ind = pop[i] subg = pydot.Cluster( "cluster_%d" % i, @@ -656,7 +661,7 @@ def buildGTreeGPGrow(ga_engine, depth, max_depth): :max_depth: the maximum depth of the tree :rtype: the root node """ - + from . import Consts gp_terminals = ga_engine.getParam("gp_terminals") assert gp_terminals is not None @@ -670,9 +675,9 @@ def buildGTreeGPGrow(ga_engine, depth, max_depth): else: # Do not generate degenerative trees if depth == 0: - random_node = random.choice(gp_function_set.keys()) + random_node = random.choice(list(gp_function_set.keys())) else: - fchoice = random.choice([gp_function_set.keys(), gp_terminals]) + fchoice = random.choice([list(gp_function_set.keys()), gp_terminals]) random_node = random.choice(fchoice) if random_node in gp_terminals: @@ -681,7 +686,7 @@ def buildGTreeGPGrow(ga_engine, depth, max_depth): n = GTreeNodeGP(random_node, Consts.nodeType["NONTERMINAL"]) if n.getType() == Consts.nodeType["NONTERMINAL"]: - for i in xrange(gp_function_set[n.getData()]): + for i in range(gp_function_set[n.getData()]): child = buildGTreeGPGrow(ga_engine, depth + 1, max_depth) child.setParent(n) n.addChild(child) @@ -698,6 +703,8 @@ def buildGTreeGPFull(ga_engine, depth, max_depth): :max_depth: the maximum depth of the tree :rtype: the root node """ + from . import Consts + gp_terminals = ga_engine.getParam("gp_terminals") assert gp_terminals is not None @@ -709,11 +716,11 @@ def buildGTreeGPFull(ga_engine, depth, max_depth): n = GTreeNodeGP(random_terminal, Consts.nodeType["TERMINAL"]) return n else: - random_oper = random.choice(gp_function_set.keys()) + random_oper = random.choice(list(gp_function_set.keys())) n = GTreeNodeGP(random_oper, Consts.nodeType["NONTERMINAL"]) if n.getType() == Consts.nodeType["NONTERMINAL"]: - for i in xrange(gp_function_set[n.getData()]): + for i in range(gp_function_set[n.getData()]): child = buildGTreeGPFull(ga_engine, depth + 1, max_depth) child.setParent(n) n.addChild(child) diff --git a/pyevolve/GenomeBase.py b/pyevolve/GenomeBase.py index f22b1cf..6e16543 100644 --- a/pyevolve/GenomeBase.py +++ b/pyevolve/GenomeBase.py @@ -8,600 +8,607 @@ take a inside look into this module. """ +from future.builtins import range + from random import choice as rand_choice import inspect -from FunctionSlot import FunctionSlot -import Util +from .FunctionSlot import FunctionSlot +from . import Util + class GenomeBase(object): - """ GenomeBase Class - The base of all chromosome representation """ - __slots__ = ["evaluator", "initializator", "mutator", "crossover", "internalParams", "score", "fitness"] + """ GenomeBase Class - The base of all chromosome representation """ + __slots__ = ["evaluator", "initializator", "mutator", "crossover", "internalParams", "score", "fitness"] + + def __init__(self): + """Genome Constructor""" + self.evaluator = FunctionSlot("Evaluator") + self.initializator = FunctionSlot("Initializator") + self.mutator = FunctionSlot("Mutator") + self.crossover = FunctionSlot("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 - self.internalParams = {} - self.score = 0.0 - self.fitness = 0.0 + def getRawScore(self): + """ Get the Raw Score of the genome - def getRawScore(self): - """ Get the Raw Score of the genome + :rtype: genome raw score - :rtype: genome raw score + """ + return self.score - """ - return self.score + def getFitnessScore(self): + """ Get the Fitness Score of the genome - def getFitnessScore(self): - """ Get the Fitness Score of the genome + :rtype: genome fitness score - :rtype: genome fitness score + """ + return self.fitness - """ - return self.fitness + def __repr__(self): + """String representation of Genome""" + allSlots = [self.evaluator, self.initializator, self.mutator, + self.crossover] - def __repr__(self): - """String representation of Genome""" - allSlots = [self.evaluator, self.initializator, self.mutator, - self.crossover] + 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,) - 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,) + for slot in allSlots: + ret += "\t" + slot.__repr__() + ret += "\n" - for slot in allSlots: - ret += "\t" + slot.__repr__() - ret += "\n" + return ret - return ret + def setParams(self, **args): + """ Set the internal params - def setParams(self, **args): - """ Set the internal params + Example: + >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1) - Example: - >>> genome.setParams(rangemin=0, rangemax=100, gauss_mu=0, gauss_sigma=1) + .. note:: All the individuals of the population shares this parameters and uses + the same instance of this dict. - .. note:: All the individuals of the population shares this parameters and uses - the same instance of this dict. + :param args: this params will saved in every chromosome for genetic op. use - :param args: this params will saved in every chromosome for genetic op. use + """ + self.internalParams.update(args) - """ - self.internalParams.update(args) + def getParam(self, key, nvl=None): + """ Gets an internal parameter - def getParam(self, key, nvl=None): - """ Gets an internal parameter + Example: + >>> genome.getParam("rangemax") + 100 - Example: - >>> genome.getParam("rangemax") - 100 + .. note:: All the individuals of the population shares this parameters and uses + the same instance of this dict. - .. note:: All the individuals of the population shares this parameters and uses - the same instance of this dict. + :param key: the key of param + :param nvl: if the key doesn't exist, the nvl will be returned - :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) - """ - return self.internalParams.get(key, nvl) + def resetStats(self): + """ Clear score and fitness of genome """ + self.score = 0.0 + self.fitness = 0.0 - 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 - def evaluate(self, **args): - """ Called to evaluate genome + :param args: this parameters will be passes to the evaluator - :param args: this parameters will be passes to the evaluator + """ + self.resetStats() + for it in self.evaluator.applyFunctions(self, **args): + self.score += it - """ - self.resetStats() - for it in self.evaluator.applyFunctions(self, **args): - self.score += it + def initialize(self, **args): + """ Called to initialize genome - def initialize(self, **args): - """ Called to initialize genome + :param args: this parameters will be passed to the initializator - :param args: this parameters will be passed to the initializator + """ + for it in self.initializator.applyFunctions(self, **args): + pass - """ - for it in self.initializator.applyFunctions(self, **args): - pass + def mutate(self, **args): + """ Called to mutate the 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 - :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 - """ - nmuts = 0 - for it in self.mutator.applyFunctions(self, **args): - nmuts += it - return nmuts + def copy(self, g): + """ Copy the current GenomeBase to 'g' - def copy(self, g): - """ Copy the current GenomeBase to 'g' + :param g: the destination genome - :param g: the destination genome + .. note:: If you are planning to create a new chromosome representation, you + **must** implement this method on your class. - .. 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 - """ - 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 + def clone(self): + """ Clone this GenomeBase - def clone(self): - """ Clone this GenomeBase + :rtype: the clone genome - :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. - """ - newcopy = GenomeBase() - self.copy(newcopy) - return newcopy class G1DBase(GenomeBase): - """ G1DBase Class - The base class for 1D chromosomes + """ G1DBase Class - The base class for 1D chromosomes - This chromosome class extends the :class:`GenomeBase` classes. + This chromosome class extends the :class:`GenomeBase` classes. - :param size: the 1D list size + :param size: the 1D list size - .. versionadded:: 0.6 - Added the *G1DBase* class - """ - __slots__ = ["genomeSize", "genomeList"] + .. versionadded:: 0.6 + Added the *G1DBase* class + """ + __slots__ = ["genomeSize", "genomeList"] - def __init__(self, size): - super(G1DBase, self).__init__() - self.genomeSize = size - self.genomeList = [] + def __init__(self, size): + super(G1DBase, self).__init__() + self.genomeSize = size + self.genomeList = [] - def __iadd__(self, item): - """ To add more items using the += operator """ - self.genomeList.append(item) - return self + 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 __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 __contains__(self, value): + """ Used on: *value in genome* """ + return value in self.genomeList + # These methods are correctly overrided in every child class + # Deprecated in Python 3 - def __getslice__(self, a, b): - """ Return the sliced part of chromosome """ - return self.genomeList[a:b] + # 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 __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 __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 __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 __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 __len__(self): - """ Return the size of the List """ - return len(self.genomeList) + def getListSize(self): + """ Returns the list supposed size - def getListSize(self): - """ Returns the list supposed size + .. warning:: this is different from what the len(obj) returns + """ + return self.genomeSize - .. 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 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 - def append(self, value): - """ Appends an item to the end of the list + Example: + >>> genome.append(44) - Example: - >>> genome.append(44) + :param value: value to be added - :param value: value to be added + """ + self.genomeList.append(value) - """ - self.genomeList.append(value) + def remove(self, value): + """ Removes an item from the list - def remove(self, value): - """ Removes an item from the list + Example: + >>> genome.remove(44) - Example: - >>> genome.remove(44) + :param value: value to be added - :param value: value to be added + """ + self.genomeList.remove(value) - """ - self.genomeList.remove(value) + def clearList(self): + """ Remove all genes from Genome """ + del self.genomeList[:] - def clearList(self): - """ Remove all genes from Genome """ - del self.genomeList[:] + def copy(self, g): + """ Copy genome to 'g' - def copy(self, g): - """ Copy genome to 'g' + Example: + >>> genome_origin.copy(genome_destination) - Example: - >>> genome_origin.copy(genome_destination) + :param g: the destination instance - :param g: the destination instance + """ + g.genomeSize = self.genomeSize + g.genomeList = self.genomeList[:] - """ - g.genomeSize = self.genomeSize - g.genomeList = self.genomeList[:] + def getInternalList(self): + """ Returns the internal list of the genome - 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 - ... 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 - 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 - :param lst: the list to assign the internal list of the chromosome - """ - self.genomeList = lst class GTreeNodeBase(object): - """ GTreeNodeBase Class - The base class for the node tree genomes + """ 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 + :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 the *GTreeNodeBase* class - """ - __slots__ = ["parent", "childs"] + .. versionadded:: 0.6 + Added the *GTreeNodeBase* class + """ + __slots__ = ["parent", "childs"] - def __init__(self, parent, childs=None): - self.parent = parent - self.childs = [] + 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 + if childs is not None: + if type(childs) != list: + Util.raiseException("Childs must be a list of nodes", TypeError) + typecheck_list = [x for x in childs if not isinstance(x, GTreeNodeBase)] + 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 + def isLeaf(self): + """ Return True if the node is a leaf - :rtype: True or False - """ - return len(self.childs) == 0 + :rtype: True or False + """ + return len(self.childs) == 0 - def getChild(self, index): - """ Returns the index-child of the node + def getChild(self, index): + """ Returns the index-child of the node - :rtype: child node - """ - return self.childs[index] + :rtype: child node + """ + return self.childs[index] - def getChilds(self): - """ Return the childs of the node + 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. + .. 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 + :rtype: a list of nodes + """ + return self.childs - def addChild(self, child): - """ Adds a child to the node + 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) + :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 + 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 + :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 + def setParent(self, parent): + """ Sets the parent of the node - :param parent: the parent node - """ - self.parent = parent + :param parent: the parent node + """ + self.parent = parent - def getParent(self): - """ Get the parent node of the node + def getParent(self): + """ Get the parent node of the node - :rtype: the parent node - """ - return self.parent + :rtype: the parent node + """ + return self.parent - def __repr__(self): - str_repr = "GTreeNodeBase [Childs=%d]" % len(self) - return str_repr + def __repr__(self): + str_repr = "GTreeNodeBase [Childs=%d]" % len(self) + return str_repr - def __len__(self): - return len(self.childs) + def __len__(self): + return len(self.childs) - def copy(self, g): - """ Copy the current contents GTreeNodeBase to 'g' + def copy(self, g): + """ Copy the current contents GTreeNodeBase to 'g' - :param g: the destination node + :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[:] + .. 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 + def clone(self): + """ Clone this GenomeBase - :rtype: the clone genome + :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 + .. 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(GenomeBase): - """ GTreeBase Class - The base class for the tree genomes - - This chromosome class extends the :class:`GenomeBase` classes. - - :param root_node: the root node of the tree - - .. versionadded:: 0.6 - Added the *GTreeBase* class - """ - __slots__ = ["root_node", "tree_height", "nodes_list", "nodes_leaf", "nodes_branch"] - - def __init__(self, root_node): - super(GTreeBase, self).__init__() - 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() is 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 + """ GTreeBase Class - The base class for the tree genomes + + This chromosome class extends the :class:`GenomeBase` classes. + + :param root_node: the root node of the tree + + .. versionadded:: 0.6 + Added the *GTreeBase* class + """ + __slots__ = ["root_node", "tree_height", "nodes_list", "nodes_leaf", "nodes_branch"] + + def __init__(self, root_node): + super(GTreeBase, self).__init__() + 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 = [n for n in self.nodes_list if n.isLeaf()] + self.nodes_branch = [n for n in self.nodes_list if n.isLeaf() is False] + + 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 range(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 diff --git a/pyevolve/Initializators.py b/pyevolve/Initializators.py index ff99c8b..aff177a 100644 --- a/pyevolve/Initializators.py +++ b/pyevolve/Initializators.py @@ -13,24 +13,21 @@ """ +from future.builtins import range from random import randint as rand_randint, uniform as rand_uniform, choice as rand_choice -import GTree -import Util +from . import GTree +from . import Util -############################# -## 1D Binary String ## -############################# +# 1D Binary String def G1DBinaryStringInitializator(genome, **args): """ 1D Binary String initializator """ - genome.genomeList = [rand_choice((0, 1)) for _ in xrange(genome.getListSize())] + genome.genomeList = [rand_choice((0, 1)) for _ in range(genome.getListSize())] -############################# -## 2D Binary String ## -############################# +# 2D Binary String def G2DBinaryStringInitializator(genome, **args): """ Integer initialization function of 2D Binary String @@ -40,15 +37,13 @@ def G2DBinaryStringInitializator(genome, **args): """ genome.clearString() - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): random_gene = rand_choice((0, 1)) genome.setItem(i, j, random_gene) -#################### -## 1D List ## -#################### +# 1D List def G1DListInitializatorAllele(genome, **args): """ Allele initialization function of G1DList @@ -62,7 +57,7 @@ def G1DListInitializatorAllele(genome, **args): if allele is None: Util.raiseException("to use the G1DListInitializatorAllele, you must specify the 'allele' parameter") - genome.genomeList = [allele[i].getRandomAllele() for i in xrange(genome.getListSize())] + genome.genomeList = [allele[i].getRandomAllele() for i in range(genome.getListSize())] def G1DListInitializatorInteger(genome, **args): @@ -74,7 +69,7 @@ def G1DListInitializatorInteger(genome, **args): range_min = genome.getParam("rangemin", 0) range_max = genome.getParam("rangemax", 100) - genome.genomeList = [rand_randint(range_min, range_max) for i in xrange(genome.getListSize())] + genome.genomeList = [rand_randint(range_min, range_max) for i in range(genome.getListSize())] def G1DListInitializatorReal(genome, **args): @@ -86,12 +81,10 @@ def G1DListInitializatorReal(genome, **args): range_min = genome.getParam("rangemin", 0) range_max = genome.getParam("rangemax", 100) - genome.genomeList = [rand_uniform(range_min, range_max) for i in xrange(genome.getListSize())] + genome.genomeList = [rand_uniform(range_min, range_max) for i in range(genome.getListSize())] -#################### -## 2D List ## -#################### +# 2D List def G2DListInitializatorInteger(genome, **args): """ Integer initialization function of G2DList @@ -101,8 +94,8 @@ def G2DListInitializatorInteger(genome, **args): """ genome.clearList() - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): randomInteger = rand_randint(genome.getParam("rangemin", 0), genome.getParam("rangemax", 100)) genome.setItem(i, j, randomInteger) @@ -116,8 +109,8 @@ def G2DListInitializatorReal(genome, **args): """ genome.clearList() - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): randomReal = rand_uniform(genome.getParam("rangemin", 0), genome.getParam("rangemax", 100)) genome.setItem(i, j, randomReal) @@ -142,15 +135,13 @@ def G2DListInitializatorAllele(genome, **args): genome.clearList() - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): random_allele = allele[0].getRandomAllele() genome.setItem(i, j, random_allele) -#################### -## Tree ## -#################### +# Tree def GTreeInitializatorInteger(genome, **args): """ Integer initialization function of GTree @@ -176,7 +167,8 @@ def GTreeInitializatorInteger(genome, **args): range_min = genome.getParam("rangemin", 0) range_max = genome.getParam("rangemax", 100) - lambda_generator = lambda: rand_randint(range_min, range_max) + def lambda_generator(): + return rand_randint(range_min, range_max) method = genome.getParam("method", "grow") @@ -192,7 +184,7 @@ def GTreeInitializatorInteger(genome, **args): else: Util.raiseException("Unknown tree initialization method [%s] !" % method) - genome.setRoot(root) + genome.setRoot(root) # TODO probably additional check needed genome.processNodes() assert genome.getHeight() <= max_depth @@ -236,9 +228,7 @@ def GTreeInitializatorAllele(genome, **args): assert genome.getHeight() <= max_depth -#################### -## Tree GP ## -#################### +# Tree GP ## def GTreeGPInitializator(genome, **args): """This initializator accepts the follow parameters: diff --git a/pyevolve/Interaction.py b/pyevolve/Interaction.py index e1f5774..9435c89 100644 --- a/pyevolve/Interaction.py +++ b/pyevolve/Interaction.py @@ -14,71 +14,77 @@ generation using the :meth:`GSimpleGA.GSimpleGA.setInteractiveGeneration` method. """ + import logging try: - import pylab -except: - logging.debug("cannot import Matplotlib ! Plots will not be available !") - print "Warning: cannot import Matplotlib ! Plots will not be available !" + import pylab +except ImportError: + msg = "cannot import Matplotlib ! Plots will not be available !" + logging.debug(msg) + print(msg) try: - import numpy -except: - logging.debug("cannot import Numpy ! Some functions will not be available !") - print "Warning: cannot import Numpy ! Some functions will not be available !" + import numpy +except ImportError: + msg = "cannot import Numpy ! Some functions will not be available !" + logging.debug(msg) + print(msg) + def getPopScores(population, fitness=False): - """ Returns a list of population scores + """ Returns a list of population scores - Example: - >>> lst = Interaction.getPopScores(population) + Example: + >>> lst = Interaction.getPopScores(population) - :param population: population object (:class:`GPopulation.GPopulation`) - :param fitness: if True, the fitness score will be used, otherwise, the raw. - :rtype: list of population scores + :param population: population object (:class:`GPopulation.GPopulation`) + :param fitness: if True, the fitness score will be used, otherwise, the raw. + :rtype: list of population scores + + """ + score_list = [] + for individual in population: + score_list.append(individual.fitness if fitness else individual.score) + return score_list - """ - score_list = [] - for individual in population: - score_list.append(individual.fitness if fitness else individual.score) - return score_list def plotPopScore(population, fitness=False): - """ Plot the population score distribution + """ Plot the population score distribution + + Example: + >>> Interaction.plotPopScore(population) - Example: - >>> Interaction.plotPopScore(population) + :param population: population object (:class:`GPopulation.GPopulation`) + :param fitness: if True, the fitness score will be used, otherwise, the raw. + :rtype: None - :param population: population object (:class:`GPopulation.GPopulation`) - :param fitness: if True, the fitness score will be used, otherwise, the raw. - :rtype: None + """ + score_list = getPopScores(population, fitness) + pylab.plot(score_list, 'o') + pylab.title("Plot of population score distribution") + pylab.xlabel('Individual') + pylab.ylabel('Score') + pylab.grid(True) + pylab.show() - """ - score_list = getPopScores(population, fitness) - pylab.plot(score_list, 'o') - pylab.title("Plot of population score distribution") - pylab.xlabel('Individual') - pylab.ylabel('Score') - pylab.grid(True) - pylab.show() def plotHistPopScore(population, fitness=False): - """ Population score distribution histogram - - Example: - >>> Interaction.plotHistPopScore(population) - - :param population: population object (:class:`GPopulation.GPopulation`) - :param fitness: if True, the fitness score will be used, otherwise, the raw. - :rtype: None - - """ - score_list = getPopScores(population, fitness) - n, bins, patches = pylab.hist(score_list, 50, facecolor='green', alpha=0.75, normed=1) - pylab.plot(bins, pylab.normpdf(bins, numpy.mean(score_list), numpy.std(score_list)), 'r--') - pylab.xlabel('Score') - pylab.ylabel('Frequency') - pylab.grid(True) - pylab.title("Plot of population score distribution") - pylab.show() + """ Population score distribution histogram + + Example: + >>> Interaction.plotHistPopScore(population) + + :param population: population object (:class:`GPopulation.GPopulation`) + :param fitness: if True, the fitness score will be used, otherwise, the raw. + :rtype: None + + """ + score_list = getPopScores(population, fitness) + n, bins, patches = pylab.hist(score_list, 50, facecolor='green', alpha=0.75, normed=1) + pylab.plot(bins, pylab.normpdf(bins, numpy.mean(score_list), numpy.std(score_list)), 'r--') + pylab.xlabel('Score') + pylab.ylabel('Frequency') + pylab.grid(True) + pylab.title("Plot of population score distribution") + pylab.show() diff --git a/pyevolve/Migration.py b/pyevolve/Migration.py index 3f566c9..e3d5e59 100644 --- a/pyevolve/Migration.py +++ b/pyevolve/Migration.py @@ -9,333 +9,335 @@ The :mod:`Migration` module. """ +from future.builtins import range -import Util +from . import Util from random import randint as rand_randint, choice as rand_choice -import Network -import Consts -from FunctionSlot import FunctionSlot +from . import Network +from . import Consts +from .FunctionSlot import FunctionSlot import logging try: - from mpi4py import MPI - HAS_MPI4PY = True + from mpi4py import MPI + HAS_MPI4PY = True except ImportError: - HAS_MPI4PY = False + HAS_MPI4PY = False + class MigrationScheme(object): - """ This is the base class for all migration schemes """ + """ This is the base class for all migration schemes """ - selector = None - """ This is the function slot for the selection method - if you want to change the default selector, you must do this: :: + selector = None + """ This is the function slot for the selection method + if you want to change the default selector, you must do this: :: - migration_scheme.selector.set(Selectors.GRouletteWheel) """ + migration_scheme.selector.set(Selectors.GRouletteWheel) """ - def __init__(self): - self.selector = FunctionSlot("Selector") - self.GAEngine = None - self.nMigrationRate = Consts.CDefGenMigrationRate - self.nIndividuals = Consts.CDefMigrationNIndividuals - self.nReplacement = Consts.CDefGenMigrationReplacement - self.networkCompression = 9 + def __init__(self): + self.selector = FunctionSlot("Selector") + self.GAEngine = None + self.nMigrationRate = Consts.CDefGenMigrationRate + self.nIndividuals = Consts.CDefMigrationNIndividuals + self.nReplacement = Consts.CDefGenMigrationReplacement + self.networkCompression = 9 - def isReady(self): - """ Returns true if is time to migrate """ - return True if self.GAEngine.getCurrentGeneration() % self.nMigrationRate == 0 else False + def isReady(self): + """ Returns true if is time to migrate """ + return True if self.GAEngine.getCurrentGeneration() % self.nMigrationRate == 0 else False - def getCompressionLevel(self): - """ Get the zlib compression level of network data + def getCompressionLevel(self): + """ Get the zlib compression level of network data - The values are in the interval described on the :func:`Network.pickleAndCompress` - """ - return self.networkCompression + The values are in the interval described on the :func:`Network.pickleAndCompress` + """ + return self.networkCompression - def setCompressionLevel(self, level): - """ Set the zlib compression level of network data + def setCompressionLevel(self, level): + """ Set the zlib compression level of network data - The values are in the interval described on the :func:`Network.pickleAndCompress` + The values are in the interval described on the :func:`Network.pickleAndCompress` - :param level: the zlib compression level - """ - self.networkCompression = level + :param level: the zlib compression level + """ + self.networkCompression = level - def getNumReplacement(self): - """ Return the number of individuals that will be - replaced in the migration process """ - return self.nReplacement + def getNumReplacement(self): + """ Return the number of individuals that will be + replaced in the migration process """ + return self.nReplacement - def setNumReplacement(self, num_individuals): - """ Return the number of individuals that will be - replaced in the migration process + def setNumReplacement(self, num_individuals): + """ Return the number of individuals that will be + replaced in the migration process - :param num_individuals: the number of individuals to be replaced - """ - self.nReplacement = num_individuals + :param num_individuals: the number of individuals to be replaced + """ + self.nReplacement = num_individuals - def getNumIndividuals(self): - """ Return the number of individuals that will migrate + def getNumIndividuals(self): + """ Return the number of individuals that will migrate - :rtype: the number of individuals to be replaced - """ - return self.nIndividuals + :rtype: the number of individuals to be replaced + """ + return self.nIndividuals - def setNumIndividuals(self, num_individuals): - """ Set the number of individuals that will migrate + def setNumIndividuals(self, num_individuals): + """ Set the number of individuals that will migrate - :param num_individuals: the number of individuals - """ - self.nIndividuals = num_individuals + :param num_individuals: the number of individuals + """ + self.nIndividuals = num_individuals - def setMigrationRate(self, generations): - """ Sets the generation frequency supposed to migrate - and receive individuals. + def setMigrationRate(self, generations): + """ Sets the generation frequency supposed to migrate + and receive individuals. - :param generations: the number of generations - """ - self.nMigrationRate = generations + :param generations: the number of generations + """ + self.nMigrationRate = generations - def getMigrationRate(self): - """ Return the the generation frequency supposed to migrate - and receive individuals + def getMigrationRate(self): + """ Return the the generation frequency supposed to migrate + and receive individuals - :rtype: the number of generations - """ - return self.nMigrationRate + :rtype: the number of generations + """ + return self.nMigrationRate - def setGAEngine(self, ga_engine): - """ Sets the GA Engine handler """ - self.GAEngine = ga_engine + def setGAEngine(self, ga_engine): + """ Sets the GA Engine handler """ + self.GAEngine = ga_engine - def start(self): - """ Initializes the migration scheme """ - pass + def start(self): + """ Initializes the migration scheme """ + pass - def stop(self): - """ Stops the migration engine """ - pass + def stop(self): + """ Stops the migration engine """ + pass - def select(self): - """ Picks an individual from population using specific selection method + def select(self): + """ Picks an individual from population using specific selection method - :rtype: an individual object - """ - if self.selector.isEmpty(): - return self.GAEngine.select(popID=self.GAEngine.currentGeneration) - else: - for it in self.selector.applyFunctions(self.GAEngine.internalPop, - popID=self.GAEngine.currentGeneration): - return it + :rtype: an individual object + """ + if self.selector.isEmpty(): + return self.GAEngine.select(popID=self.GAEngine.currentGeneration) + else: + for it in self.selector.applyFunctions(self.GAEngine.internalPop, + popID=self.GAEngine.currentGeneration): + return it - def selectPool(self, num_individuals): - """ Select num_individuals number of individuals and return a pool + def selectPool(self, num_individuals): + """ Select num_individuals number of individuals and return a pool - :param num_individuals: the number of individuals to select - :rtype: list with individuals - """ - pool = [self.select() for i in xrange(num_individuals)] - return pool + :param num_individuals: the number of individuals to select + :rtype: list with individuals + """ + pool = [self.select() for i in range(num_individuals)] + return pool - def exchange(self): - """ Exchange individuals """ - pass + def exchange(self): + """ Exchange individuals """ + pass class WANMigration(MigrationScheme): - """ This is the Simple Migration class for distributed GA + """ This is the Simple Migration class for distributed GA - Example: - >>> mig = WANMigration("192.168.0.1", "10000", "group1") + Example: + >>> mig = WANMigration("192.168.0.1", "10000", "group1") - :param host: the source hostname - :param port: the source port number - :param group_name: the group name - """ - - selector = None - """ This is the function slot for the selection method - if you want to change the default selector, you must do this: :: - - migration_scheme.selector.set(Selectors.GRouletteWheel) """ - - def __init__(self, host, port, group_name): - super(WANMigration, self).__init__() - self.setMyself(host, port) - self.setGroupName(group_name) - self.topologyGraph = None - self.serverThread = Network.UDPThreadServer(host, port) - self.clientThread = Network.UDPThreadUnicastClient(self.myself[0], rand_randint(30000, 65534)) - - def setMyself(self, host, port): - """ Which interface you will use to send/receive data + :param host: the source hostname + :param port: the source port number + :param group_name: the group name + """ + + selector = None + """ This is the function slot for the selection method + if you want to change the default selector, you must do this: :: + + migration_scheme.selector.set(Selectors.GRouletteWheel) """ + + def __init__(self, host, port, group_name): + super(WANMigration, self).__init__() + self.setMyself(host, port) + self.setGroupName(group_name) + self.topologyGraph = None + self.serverThread = Network.UDPThreadServer(host, port) + self.clientThread = Network.UDPThreadUnicastClient(self.myself[0], rand_randint(30000, 65534)) + + def setMyself(self, host, port): + """ Which interface you will use to send/receive data - :param host: your hostname - :param port: your port - """ - self.myself = (host, port) + :param host: your hostname + :param port: your port + """ + self.myself = (host, port) - def getGroupName(self): - """ Gets the group name + def getGroupName(self): + """ Gets the group name - .. note:: all islands of evolution which are supposed to exchange - individuals, must have the same group name. - """ - return self.groupName + .. note:: all islands of evolution which are supposed to exchange + individuals, must have the same group name. + """ + return self.groupName - def setGroupName(self, name): - """ Sets the group name + def setGroupName(self, name): + """ Sets the group name - :param name: the group name + :param name: the group name - .. note:: all islands of evolution which are supposed to exchange - individuals, must have the same group name. - """ - self.groupName = name + .. note:: all islands of evolution which are supposed to exchange + individuals, must have the same group name. + """ + self.groupName = name - def setTopology(self, graph): - """ Sets the topology of the migrations + def setTopology(self, graph): + """ Sets the topology of the migrations - :param graph: the :class:`Util.Graph` instance - """ - self.topologyGraph = graph + :param graph: the :class:`Util.Graph` instance + """ + self.topologyGraph = graph - def start(self): - """ Start capture of packets and initialize the migration scheme """ - self.serverThread.start() + def start(self): + """ Start capture of packets and initialize the migration scheme """ + self.serverThread.start() - if self.topologyGraph is None: - Util.raiseException("You must add a topology graph to the migration scheme !") + if self.topologyGraph is None: + Util.raiseException("You must add a topology graph to the migration scheme !") - targets = self.topologyGraph.getNeighbors(self.myself) - self.clientThread.setMultipleTargetHost(targets) - self.clientThread.start() + targets = self.topologyGraph.getNeighbors(self.myself) + self.clientThread.setMultipleTargetHost(targets) + self.clientThread.start() - def stop(self): - """ Stops the migration engine """ - self.serverThread.shutdown() - self.clientThread.shutdown() - server_timeout = self.serverThread.timeout - client_timeout = self.clientThread.timeout + def stop(self): + """ Stops the migration engine """ + self.serverThread.shutdown() + self.clientThread.shutdown() + server_timeout = self.serverThread.timeout + client_timeout = self.clientThread.timeout - self.serverThread.join(server_timeout + 3) - self.clientThread.join(client_timeout + 3) + self.serverThread.join(server_timeout + 3) + self.clientThread.join(client_timeout + 3) - if self.serverThread.isAlive(): - logging.warning("warning: server thread not joined !") + if self.serverThread.isAlive(): + logging.warning("warning: server thread not joined !") - if self.clientThread.isAlive(): - logging.warning("warning: client thread not joined !") + if self.clientThread.isAlive(): + logging.warning("warning: client thread not joined !") - def exchange(self): - """ This is the main method, is where the individuals - are exchanged """ + def exchange(self): + """ This is the main method, is where the individuals + are exchanged """ - if not self.isReady(): - return + if not self.isReady(): + return - # Client section -------------------------------------- - # How many will migrate ? - pool = self.selectPool(self.getNumIndividuals()) + # Client section -------------------------------------- + # How many will migrate ? + pool = self.selectPool(self.getNumIndividuals()) - for individual in pool: - # (code, group name, individual) - networkObject = (Consts.CDefNetworkIndividual, self.getGroupName(), individual) - networkData = Network.pickleAndCompress(networkObject, self.getCompressionLevel()) - # Send the individuals to the topology - self.clientThread.addData(networkData) + for individual in pool: + # (code, group name, individual) + networkObject = (Consts.CDefNetworkIndividual, self.getGroupName(), individual) + networkData = Network.pickleAndCompress(networkObject, self.getCompressionLevel()) + # Send the individuals to the topology + self.clientThread.addData(networkData) - # Server section -------------------------------------- - pool = [] - while self.serverThread.isReady(): - # (IP source, data) - networkData = self.serverThread.popPool() - networkObject = Network.unpickleAndDecompress(networkData[1]) - # (code, group name, individual) - pool.append(networkObject) + # Server section -------------------------------------- + pool = [] + while self.serverThread.isReady(): + # (IP source, data) + networkData = self.serverThread.popPool() + networkObject = Network.unpickleAndDecompress(networkData[1]) + # (code, group name, individual) + pool.append(networkObject) - # No individuals received - if len(pool) <= 0: - return + # No individuals received + if len(pool) <= 0: + return - population = self.GAEngine.getPopulation() + population = self.GAEngine.getPopulation() - for i in xrange(self.getNumReplacement()): - if len(pool) <= 0: - break - choice = rand_choice(pool) - pool.remove(choice) + for i in range(self.getNumReplacement()): + if len(pool) <= 0: + break + choice = rand_choice(pool) + pool.remove(choice) - # replace the worst - population[len(population) - 1 - i] = choice[2] + # replace the worst + population[len(population) - 1 - i] = choice[2] class MPIMigration(MigrationScheme): - """ This is the MPIMigration """ + """ This is the MPIMigration """ - def __init__(self): - # Delayed ImportError of mpi4py - if not HAS_MPI4PY: - raise ImportError("No module named mpi4py, you must install mpi4py to use MPIMIgration!") + def __init__(self): + # Delayed ImportError of mpi4py + if not HAS_MPI4PY: + raise ImportError("No module named mpi4py, you must install mpi4py to use MPIMIgration!") - super(MPIMigration, self).__init__() + super(MPIMigration, self).__init__() - self.comm = MPI.COMM_WORLD - self.pid = self.comm.rank + self.comm = MPI.COMM_WORLD + self.pid = self.comm.rank - if self.pid == 0: - self.source = self.comm.size - 1 - else: - self.source = self.comm.rank - 1 + if self.pid == 0: + self.source = self.comm.size - 1 + else: + self.source = self.comm.rank - 1 - self.dest = (self.comm.rank + 1) % (self.comm.size) + self.dest = (self.comm.rank + 1) % (self.comm.size) - self.all_stars = None + self.all_stars = None - def isReady(self): - """ Returns true if is time to migrate """ + def isReady(self): + """ Returns true if is time to migrate """ - if self.GAEngine.getCurrentGeneration() == 0: - return False + if self.GAEngine.getCurrentGeneration() == 0: + return False - if self.GAEngine.getCurrentGeneration() % self.nMigrationRate == 0: - return True - else: - return False + if self.GAEngine.getCurrentGeneration() % self.nMigrationRate == 0: + return True + else: + return False - def gather_bests(self): - ''' - Collect all the best individuals from the various populations. The - result is stored in process 0 - ''' - best_guy = self.select() - self.all_stars = self.comm.gather(sendobj=best_guy, root=0) + def gather_bests(self): + ''' + Collect all the best individuals from the various populations. The + result is stored in process 0 + ''' + best_guy = self.select() + self.all_stars = self.comm.gather(sendobj=best_guy, root=0) - def exchange(self): - """ This is the main method, is where the individuals - are exchanged """ + def exchange(self): + """ This is the main method, is where the individuals + are exchanged """ - if not self.isReady(): - return + if not self.isReady(): + return - pool_to_send = self.selectPool(self.getNumIndividuals()) - pool_received = self.comm.sendrecv(sendobj=pool_to_send, - dest=self.dest, - sendtag=0, - recvobj=None, - source=self.source, - recvtag=0) + pool_to_send = self.selectPool(self.getNumIndividuals()) + pool_received = self.comm.sendrecv(sendobj=pool_to_send, + dest=self.dest, + sendtag=0, + recvobj=None, + source=self.source, + recvtag=0) - population = self.GAEngine.getPopulation() + population = self.GAEngine.getPopulation() - pool = pool_received - for i in xrange(self.getNumReplacement()): - if len(pool) <= 0: - break + pool = pool_received + for i in range(self.getNumReplacement()): + if len(pool) <= 0: + break - choice = rand_choice(pool) - pool.remove(choice) + choice = rand_choice(pool) + pool.remove(choice) - # replace the worst - population[len(population) - 1 - i] = choice + # replace the worst + population[len(population) - 1 - i] = choice - self.gather_bests() + self.gather_bests() diff --git a/pyevolve/Mutators.py b/pyevolve/Mutators.py index 3c757df..5f7d526 100644 --- a/pyevolve/Mutators.py +++ b/pyevolve/Mutators.py @@ -6,419 +6,438 @@ In this module we have the genetic operators of mutation for each chromosome representation. """ +from future.builtins import range -import Util +from . import Util from random import randint as rand_randint, gauss as rand_gauss, uniform as rand_uniform from random import choice as rand_choice -import Consts -import GTree +from . import GTree -############################# -## 1D Binary String ## -############################# + +# 1D Binary String def G1DBinaryStringMutatorSwap(genome, **args): - """ The 1D Binary String Swap Mutator """ - - if args["pmut"] <= 0.0: - return 0 - stringLength = len(genome) - mutations = args["pmut"] * (stringLength) - - if mutations < 1.0: - mutations = 0 - for it in xrange(stringLength): - if Util.randomFlipCoin(args["pmut"]): - Util.listSwapElement(genome, it, rand_randint(0, stringLength - 1)) - mutations += 1 + """ The 1D Binary String Swap Mutator """ + + if args["pmut"] <= 0.0: + return 0 + stringLength = len(genome) + mutations = args["pmut"] * (stringLength) + + if mutations < 1.0: + mutations = 0 + for it in range(stringLength): + if Util.randomFlipCoin(args["pmut"]): + Util.listSwapElement(genome, it, rand_randint(0, stringLength - 1)) + mutations += 1 + + else: + for it in range(int(round(mutations))): + Util.listSwapElement(genome, rand_randint(0, stringLength - 1), + rand_randint(0, stringLength - 1)) - else: - for it in xrange(int(round(mutations))): - Util.listSwapElement(genome, rand_randint(0, stringLength - 1), - rand_randint(0, stringLength - 1)) + return int(mutations) - return int(mutations) def G1DBinaryStringMutatorFlip(genome, **args): - """ The classical flip mutator for binary strings """ - if args["pmut"] <= 0.0: - return 0 - stringLength = len(genome) - mutations = args["pmut"] * (stringLength) - - if mutations < 1.0: - mutations = 0 - for it in xrange(stringLength): - if Util.randomFlipCoin(args["pmut"]): - if genome[it] == 0: - genome[it] = 1 + """ The classical flip mutator for binary strings """ + if args["pmut"] <= 0.0: + return 0 + stringLength = len(genome) + mutations = args["pmut"] * (stringLength) + + if mutations < 1.0: + mutations = 0 + for it in range(stringLength): + if Util.randomFlipCoin(args["pmut"]): + if genome[it] == 0: + genome[it] = 1 + else: + genome[it] = 0 + mutations += 1 + + else: + for it in range(int(round(mutations))): + which = rand_randint(0, stringLength - 1) + if genome[which] == 0: + genome[which] = 1 else: - genome[it] = 0 - mutations += 1 + genome[which] = 0 - else: - for it in xrange(int(round(mutations))): - which = rand_randint(0, stringLength - 1) - if genome[which] == 0: - genome[which] = 1 - else: - genome[which] = 0 + return int(mutations) - return int(mutations) -#################### -## 1D List ## -#################### +# 1D List def G1DListMutatorSwap(genome, **args): - """ The mutator of G1DList, Swap Mutator + """ The mutator of G1DList, Swap Mutator - .. note:: this mutator is :term:`Data Type Independent` + .. note:: this mutator is :term:`Data Type Independent` - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * listSize + """ + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * listSize - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - Util.listSwapElement(genome, it, rand_randint(0, listSize - 1)) - mutations += 1 - else: - for it in xrange(int(round(mutations))): - Util.listSwapElement(genome, rand_randint(0, listSize - 1), rand_randint(0, listSize - 1)) + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + Util.listSwapElement(genome, it, rand_randint(0, listSize - 1)) + mutations += 1 + else: + for it in range(int(round(mutations))): + Util.listSwapElement(genome, rand_randint(0, listSize - 1), rand_randint(0, listSize - 1)) + + return int(mutations) - return int(mutations) def G1DListMutatorSIM(genome, **args): - """ The mutator of G1DList, Simple Inversion Mutation + """ The mutator of G1DList, Simple Inversion Mutation - .. note:: this mutator is :term:`Data Type Independent` + .. note:: this mutator is :term:`Data Type Independent` - """ - mutations = 0 - if args["pmut"] <= 0.0: - return 0 + """ + mutations = 0 + if args["pmut"] <= 0.0: + return 0 + + cuts = [rand_randint(0, len(genome)), rand_randint(0, len(genome))] - cuts = [rand_randint(0, len(genome)), rand_randint(0, len(genome))] + if cuts[0] > cuts[1]: + Util.listSwapElement(cuts, 0, 1) - if cuts[0] > cuts[1]: - Util.listSwapElement(cuts, 0, 1) + if (cuts[1] - cuts[0]) <= 0: + cuts[1] = rand_randint(cuts[0], len(genome)) - if (cuts[1] - cuts[0]) <= 0: - cuts[1] = rand_randint(cuts[0], len(genome)) + if Util.randomFlipCoin(args["pmut"]): + part = genome[cuts[0]:cuts[1]] + if len(part) == 0: + return 0 + part.reverse() + genome[cuts[0]:cuts[1]] = part + mutations += 1 - if Util.randomFlipCoin(args["pmut"]): - part = genome[cuts[0]:cuts[1]] - if len(part) == 0: - return 0 - part.reverse() - genome[cuts[0]:cuts[1]] = part - mutations += 1 + return mutations - return mutations def G1DListMutatorIntegerRange(genome, **args): - """ Simple integer range mutator for G1DList - - Accepts the *rangemin* and *rangemax* genome parameters, both optional. - - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * listSize - - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - genome[it] = rand_randint(genome.getParam("rangemin", Consts.CDefRangeMin), - genome.getParam("rangemax", Consts.CDefRangeMax)) - mutations += 1 + """ Simple integer range mutator for G1DList + + Accepts the *rangemin* and *rangemax* genome parameters, both optional. + + """ + from . import Consts + + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * listSize - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - genome[which_gene] = rand_randint(genome.getParam("rangemin", Consts.CDefRangeMin), - genome.getParam("rangemax", Consts.CDefRangeMax)) + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + genome[it] = rand_randint(genome.getParam("rangemin", Consts.CDefRangeMin), + genome.getParam("rangemax", Consts.CDefRangeMax)) + mutations += 1 - return int(mutations) + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + genome[which_gene] = rand_randint(genome.getParam("rangemin", Consts.CDefRangeMin), + genome.getParam("rangemax", Consts.CDefRangeMax)) + + return int(mutations) def G1DListMutatorRealRange(genome, **args): - """ Simple real range mutator for G1DList - - Accepts the *rangemin* and *rangemax* genome parameters, both optional. - - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * (listSize) - - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - genome[it] = rand_uniform(genome.getParam("rangemin", Consts.CDefRangeMin), - genome.getParam("rangemax", Consts.CDefRangeMax)) - mutations += 1 + """ Simple real range mutator for G1DList + + Accepts the *rangemin* and *rangemax* genome parameters, both optional. + + """ + from . import Consts - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - genome[which_gene] = rand_uniform(genome.getParam("rangemin", Consts.CDefRangeMin), - genome.getParam("rangemax", Consts.CDefRangeMax)) + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * (listSize) + + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + genome[it] = rand_uniform(genome.getParam("rangemin", Consts.CDefRangeMin), + genome.getParam("rangemax", Consts.CDefRangeMax)) + mutations += 1 + + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + genome[which_gene] = rand_uniform(genome.getParam("rangemin", Consts.CDefRangeMin), + genome.getParam("rangemax", Consts.CDefRangeMax)) + + return int(mutations) - return int(mutations) def G1DListMutatorIntegerGaussianGradient(genome, **args): - """ A gaussian mutator for G1DList of Integers + """ A gaussian mutator for G1DList of Integers - Accepts the *rangemin* and *rangemax* genome parameters, both optional. The - random distribution is set with mu=1.0 and std=0.0333 + Accepts the *rangemin* and *rangemax* genome parameters, both optional. The + random distribution is set with mu=1.0 and std=0.0333 - Same as IntegerGaussian, except that this uses relative gradient rather than - absolute gaussian. A value is randomly generated about gauss(mu=1, sigma=.0333) - and multiplied by the gene to drift it up or down (depending on what side of - 1 the random value falls on) and cast to integer + Same as IntegerGaussian, except that this uses relative gradient rather than + absolute gaussian. A value is randomly generated about gauss(mu=1, sigma=.0333) + and multiplied by the gene to drift it up or down (depending on what side of + 1 the random value falls on) and cast to integer - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * (listSize) + """ + from . import Consts - mu = Consts.CDefGaussianGradientMU - sigma = Consts.CDefGaussianGradientSIGMA + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * (listSize) - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - final_value = int(genome[it] * abs(rand_gauss(mu, sigma))) + mu = Consts.CDefGaussianGradientMU + sigma = Consts.CDefGaussianGradientSIGMA + + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + final_value = int(genome[it] * abs(rand_gauss(mu, sigma))) + + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + + genome[it] = final_value + mutations += 1 + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + final_value = int(genome[which_gene] * abs(rand_gauss(mu, sigma))) final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - genome[it] = final_value - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - final_value = int(genome[which_gene] * abs(rand_gauss(mu, sigma))) - - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + genome[which_gene] = final_value - genome[which_gene] = final_value + return int(mutations) - return int(mutations) def G1DListMutatorIntegerGaussian(genome, **args): - """ A gaussian mutator for G1DList of Integers + """ A gaussian mutator for G1DList of Integers + + Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also + accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively + represents the mean and the std. dev. of the random distribution. + + """ + from . import Consts - Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also - accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively - represents the mean and the std. dev. of the random distribution. + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * (listSize) - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * (listSize) + mu = genome.getParam("gauss_mu") + sigma = genome.getParam("gauss_sigma") - mu = genome.getParam("gauss_mu") - sigma = genome.getParam("gauss_sigma") + if mu is None: + mu = Consts.CDefG1DListMutIntMU - if mu is None: - mu = Consts.CDefG1DListMutIntMU + if sigma is None: + sigma = Consts.CDefG1DListMutIntSIGMA + + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + final_value = genome[it] + int(rand_gauss(mu, sigma)) - if sigma is None: - sigma = Consts.CDefG1DListMutIntSIGMA + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - final_value = genome[it] + int(rand_gauss(mu, sigma)) + genome[it] = final_value + mutations += 1 + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + final_value = genome[which_gene] + int(rand_gauss(mu, sigma)) final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - genome[it] = final_value - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - final_value = genome[which_gene] + int(rand_gauss(mu, sigma)) + genome[which_gene] = final_value - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + return int(mutations) - genome[which_gene] = final_value - return int(mutations) +def G1DListMutatorRealGaussian(genome, **args): + """ The mutator of G1DList, Gaussian Mutator + Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also + accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively + represents the mean and the std. dev. of the random distribution. -def G1DListMutatorRealGaussian(genome, **args): - """ The mutator of G1DList, Gaussian Mutator + """ + from . import Consts - Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also - accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively - represents the mean and the std. dev. of the random distribution. + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * (listSize) - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * (listSize) + mu = genome.getParam("gauss_mu") + sigma = genome.getParam("gauss_sigma") - mu = genome.getParam("gauss_mu") - sigma = genome.getParam("gauss_sigma") + if mu is None: + mu = Consts.CDefG1DListMutRealMU - if mu is None: - mu = Consts.CDefG1DListMutRealMU + if sigma is None: + sigma = Consts.CDefG1DListMutRealSIGMA - if sigma is None: - sigma = Consts.CDefG1DListMutRealSIGMA + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + final_value = genome[it] + rand_gauss(mu, sigma) - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - final_value = genome[it] + rand_gauss(mu, sigma) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + + genome[it] = final_value + mutations += 1 + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + final_value = genome[which_gene] + rand_gauss(mu, sigma) final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - genome[it] = final_value - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - final_value = genome[which_gene] + rand_gauss(mu, sigma) - - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + genome[which_gene] = final_value - genome[which_gene] = final_value + return int(mutations) - return int(mutations) def G1DListMutatorRealGaussianGradient(genome, **args): - """ The mutator of G1DList, Gaussian Gradient Mutator + """ The mutator of G1DList, Gaussian Gradient Mutator - Accepts the *rangemin* and *rangemax* genome parameters, both optional. The - random distribution is set with mu=1.0 and std=0.0333 + Accepts the *rangemin* and *rangemax* genome parameters, both optional. The + random distribution is set with mu=1.0 and std=0.0333 - The difference between this routine and the normal Gaussian Real is that the - other function generates a gaussian value and adds it to the value. If the - mu is 0, and the std is 1, a typical value could be 1.8 or -0.5. These small - values are fine if your range is 0-10, but if your range is much larger, like - 0-100,000, a relative gradient makes sense. + The difference between this routine and the normal Gaussian Real is that the + other function generates a gaussian value and adds it to the value. If the + mu is 0, and the std is 1, a typical value could be 1.8 or -0.5. These small + values are fine if your range is 0-10, but if your range is much larger, like + 0-100,000, a relative gradient makes sense. - This routine generates a gaussian value with mu=1.0 and std=0.0333 and then - the gene is multiplied by this value. This will cause the gene to drift - no matter how large it is. + This routine generates a gaussian value with mu=1.0 and std=0.0333 and then + the gene is multiplied by this value. This will cause the gene to drift + no matter how large it is. - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * (listSize) + """ + from . import Consts + + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * (listSize) - mu = Consts.CDefGaussianGradientMU - sigma = Consts.CDefGaussianGradientSIGMA + mu = Consts.CDefGaussianGradientMU + sigma = Consts.CDefGaussianGradientSIGMA - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - final_value = genome[it] * abs(rand_gauss(mu, sigma)) + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + final_value = genome[it] * abs(rand_gauss(mu, sigma)) + + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + + genome[it] = final_value + mutations += 1 + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + final_value = genome[which_gene] * abs(rand_gauss(mu, sigma)) final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - genome[it] = final_value - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - final_value = genome[which_gene] * abs(rand_gauss(mu, sigma)) - - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + genome[which_gene] = final_value - genome[which_gene] = final_value + return int(mutations) - return int(mutations) def G1DListMutatorIntegerBinary(genome, **args): - """ The mutator of G1DList, the binary mutator + """ The mutator of G1DList, the binary mutator - This mutator will random change the 0 and 1 elements of the 1D List. + This mutator will random change the 0 and 1 elements of the 1D List. - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * (listSize) + """ + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * (listSize) - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - if genome[it] == 0: - genome[it] = 1 - elif genome[it] == 1: - genome[it] = 0 + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + if genome[it] == 0: + genome[it] = 1 + elif genome[it] == 1: + genome[it] = 0 - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - if genome[which_gene] == 0: - genome[which_gene] = 1 - elif genome[which_gene] == 1: - genome[which_gene] = 0 + mutations += 1 + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + if genome[which_gene] == 0: + genome[which_gene] = 1 + elif genome[which_gene] == 1: + genome[which_gene] = 0 + + return int(mutations) - return int(mutations) def G1DListMutatorAllele(genome, **args): - """ The mutator of G1DList, Allele Mutator - - To use this mutator, you must specify the *allele* genome parameter with the - :class:`GAllele.GAlleles` instance. - - """ - if args["pmut"] <= 0.0: - return 0 - listSize = len(genome) - mutations = args["pmut"] * listSize - - allele = genome.getParam("allele", None) - if allele is None: - Util.raiseException("to use the G1DListMutatorAllele, you must specify the 'allele' parameter", TypeError) - - if mutations < 1.0: - mutations = 0 - for it in xrange(listSize): - if Util.randomFlipCoin(args["pmut"]): - new_val = allele[it].getRandomAllele() - genome[it] = new_val - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_gene = rand_randint(0, listSize - 1) - new_val = allele[which_gene].getRandomAllele() - genome[which_gene] = new_val + """ The mutator of G1DList, Allele Mutator + + To use this mutator, you must specify the *allele* genome parameter with the + :class:`GAllele.GAlleles` instance. + + """ + if args["pmut"] <= 0.0: + return 0 + listSize = len(genome) + mutations = args["pmut"] * listSize + + allele = genome.getParam("allele", None) + if allele is None: + Util.raiseException("to use the G1DListMutatorAllele, you must specify the 'allele' parameter", TypeError) + + if mutations < 1.0: + mutations = 0 + for it in range(listSize): + if Util.randomFlipCoin(args["pmut"]): + new_val = allele[it].getRandomAllele() + genome[it] = new_val + mutations += 1 + else: + for it in range(int(round(mutations))): + which_gene = rand_randint(0, listSize - 1) + new_val = allele[which_gene].getRandomAllele() + genome[which_gene] = new_val + + return int(mutations) - return int(mutations) def G1DListMutatorAlleleGaussian(genome, **arguments): """An allele-based mutator based on G1DListMutatorRealGaussian. @@ -427,6 +446,8 @@ def G1DListMutatorAlleleGaussian(genome, **arguments): respectively represents the mean and the std. dev. of the random distribution. """ + from . import Consts + if arguments["pmut"] <= 0.0: return 0 listSize = len(genome) @@ -445,7 +466,7 @@ def G1DListMutatorAlleleGaussian(genome, **arguments): if mutations < 1.0: mutations = 0 - for it in xrange(listSize): + for it in range(listSize): if Util.randomFlipCoin(arguments["pmut"]): final_value = genome[it] + rand_gauss(mu, sigma) assert len(allele[it].beginEnd) == 1, "only single ranges are supported" @@ -455,7 +476,7 @@ def G1DListMutatorAlleleGaussian(genome, **arguments): genome[it] = final_value mutations += 1 else: - for it in xrange(int(round(mutations))): + for it in range(int(round(mutations))): which_gene = rand_randint(0, listSize - 1) final_value = genome[which_gene] + rand_gauss(mu, sigma) assert len(allele[which_gene].beginEnd) == 1, "only single ranges are supported" @@ -466,669 +487,686 @@ def G1DListMutatorAlleleGaussian(genome, **arguments): return int(mutations) -#################### -## 2D List ## -#################### +# 2D List def G2DListMutatorSwap(genome, **args): - """ The mutator of G1DList, Swap Mutator + """ The mutator of G1DList, Swap Mutator - .. note:: this mutator is :term:`Data Type Independent` + .. note:: this mutator is :term:`Data Type Independent` - """ + """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mutations = args["pmut"] * elements + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 - for i in xrange(height): - for j in xrange(width): - if Util.randomFlipCoin(args["pmut"]): - index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) - Util.list2DSwapElement(genome.genomeList, (i, j), index_b) - mutations += 1 - else: - for it in xrange(int(round(mutations))): - index_a = (rand_randint(0, height - 1), rand_randint(0, width - 1)) - index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) - Util.list2DSwapElement(genome.genomeList, index_a, index_b) + if mutations < 1.0: + mutations = 0 + for i in range(height): + for j in range(width): + if Util.randomFlipCoin(args["pmut"]): + index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) + Util.list2DSwapElement(genome.genomeList, (i, j), index_b) + mutations += 1 + else: + for it in range(int(round(mutations))): + index_a = (rand_randint(0, height - 1), rand_randint(0, width - 1)) + index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) + Util.list2DSwapElement(genome.genomeList, index_a, index_b) + + return int(mutations) - return int(mutations) def G2DListMutatorIntegerRange(genome, **args): - """ Simple integer range mutator for G2DList + """ Simple integer range mutator for G2DList - Accepts the *rangemin* and *rangemax* genome parameters, both optional. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. - """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + """ + from . import Consts - mutations = args["pmut"] * elements + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - range_min = genome.getParam("rangemin", Consts.CDefRangeMin) - range_max = genome.getParam("rangemax", Consts.CDefRangeMax) + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - random_int = rand_randint(range_min, range_max) - genome.setItem(i, j, random_int) - mutations += 1 + range_min = genome.getParam("rangemin", Consts.CDefRangeMin) + range_max = genome.getParam("rangemax", Consts.CDefRangeMax) - else: - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getWidth() - 1) - which_y = rand_randint(0, genome.getHeight() - 1) - random_int = rand_randint(range_min, range_max) - genome.setItem(which_y, which_x, random_int) + if mutations < 1.0: + mutations = 0 + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + random_int = rand_randint(range_min, range_max) + genome.setItem(i, j, random_int) + mutations += 1 - return int(mutations) + else: + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getWidth() - 1) + which_y = rand_randint(0, genome.getHeight() - 1) + random_int = rand_randint(range_min, range_max) + genome.setItem(which_y, which_x, random_int) + + return int(mutations) def G2DListMutatorIntegerGaussianGradient(genome, **args): - """ A gaussian mutator for G2DList of Integers + """ A gaussian mutator for G2DList of Integers - Accepts the *rangemin* and *rangemax* genome parameters, both optional. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. - This routine generates a gaussian value with mu=1.0 and std=0.0333 and then - the gene is multiplied by this value. This will cause the gene to drift - no matter how large it is. + This routine generates a gaussian value with mu=1.0 and std=0.0333 and then + the gene is multiplied by this value. This will cause the gene to drift + no matter how large it is. - """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + """ + from . import Consts - mutations = args["pmut"] * elements + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mu = Consts.CDefGaussianGradientMU - sigma = Consts.CDefGaussianGradientSIGMA + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 + mu = Consts.CDefGaussianGradientMU + sigma = Consts.CDefGaussianGradientSIGMA - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - final_value = int(genome[i][j] * abs(rand_gauss(mu, sigma))) + if mutations < 1.0: + mutations = 0 - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + final_value = int(genome[i][j] * abs(rand_gauss(mu, sigma))) - genome.setItem(i, j, final_value) - mutations += 1 - else: + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getWidth() - 1) - which_y = rand_randint(0, genome.getHeight() - 1) + genome.setItem(i, j, final_value) + mutations += 1 + else: - final_value = int(genome[which_y][which_x] * abs(rand_gauss(mu, sigma))) + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getWidth() - 1) + which_y = rand_randint(0, genome.getHeight() - 1) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + final_value = int(genome[which_y][which_x] * abs(rand_gauss(mu, sigma))) - genome.setItem(which_y, which_x, final_value) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + + genome.setItem(which_y, which_x, final_value) + + return int(mutations) - return int(mutations) def G2DListMutatorIntegerGaussian(genome, **args): - """ A gaussian mutator for G2DList of Integers + """ A gaussian mutator for G2DList of Integers - Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also - accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively - represents the mean and the std. dev. of the random distribution. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also + accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively + represents the mean and the std. dev. of the random distribution. - """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + """ + from . import Consts + + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mutations = args["pmut"] * elements + mutations = args["pmut"] * elements - mu = genome.getParam("gauss_mu") - sigma = genome.getParam("gauss_sigma") + mu = genome.getParam("gauss_mu") + sigma = genome.getParam("gauss_sigma") - if mu is None: - mu = Consts.CDefG2DListMutIntMU + if mu is None: + mu = Consts.CDefG2DListMutIntMU - if sigma is None: - sigma = Consts.CDefG2DListMutIntSIGMA + if sigma is None: + sigma = Consts.CDefG2DListMutIntSIGMA - if mutations < 1.0: - mutations = 0 + if mutations < 1.0: + mutations = 0 - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - final_value = genome[i][j] + int(rand_gauss(mu, sigma)) + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + final_value = genome[i][j] + int(rand_gauss(mu, sigma)) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - genome.setItem(i, j, final_value) - mutations += 1 - else: + genome.setItem(i, j, final_value) + mutations += 1 + else: - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getWidth() - 1) - which_y = rand_randint(0, genome.getHeight() - 1) + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getWidth() - 1) + which_y = rand_randint(0, genome.getHeight() - 1) - final_value = genome[which_y][which_x] + int(rand_gauss(mu, sigma)) + final_value = genome[which_y][which_x] + int(rand_gauss(mu, sigma)) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - genome.setItem(which_y, which_x, final_value) + genome.setItem(which_y, which_x, final_value) - return int(mutations) + return int(mutations) def G2DListMutatorAllele(genome, **args): - """ The mutator of G2DList, Allele Mutator + """ The mutator of G2DList, Allele Mutator - To use this mutator, you must specify the *allele* genome parameter with the - :class:`GAllele.GAlleles` instance. + To use this mutator, you must specify the *allele* genome parameter with the + :class:`GAllele.GAlleles` instance. - .. warning:: the :class:`GAllele.GAlleles` instance must have the homogeneous flag enabled + .. warning:: the :class:`GAllele.GAlleles` instance must have the homogeneous flag enabled - """ - if args["pmut"] <= 0.0: - return 0 - listSize = genome.getHeight() * genome.getWidth() - 1 - mutations = args["pmut"] * (listSize + 1) + """ + if args["pmut"] <= 0.0: + return 0 + listSize = genome.getHeight() * genome.getWidth() - 1 + mutations = args["pmut"] * (listSize + 1) - allele = genome.getParam("allele", None) - if allele is None: - Util.raiseException("to use the G2DListMutatorAllele, you must specify the 'allele' parameter", TypeError) + allele = genome.getParam("allele", None) + if allele is None: + Util.raiseException("to use the G2DListMutatorAllele, you must specify the 'allele' parameter", TypeError) - if not allele.homogeneous: - Util.raiseException("to use the G2DListMutatorAllele, the 'allele' must be homogeneous") + if not allele.homogeneous: + Util.raiseException("to use the G2DListMutatorAllele, the 'allele' must be homogeneous") - if mutations < 1.0: - mutations = 0 + if mutations < 1.0: + mutations = 0 - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - new_val = allele[0].getRandomAllele() - genome.setItem(i, j, new_val) - mutations += 1 - else: - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getHeight() - 1) - which_y = rand_randint(0, genome.getWidth() - 1) + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + new_val = allele[0].getRandomAllele() + genome.setItem(i, j, new_val) + mutations += 1 + else: + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getHeight() - 1) + which_y = rand_randint(0, genome.getWidth() - 1) - new_val = allele[0].getRandomAllele() - genome.setItem(which_x, which_y, new_val) + new_val = allele[0].getRandomAllele() + genome.setItem(which_x, which_y, new_val) - return int(mutations) + return int(mutations) def G2DListMutatorRealGaussian(genome, **args): - """ A gaussian mutator for G2DList of Real + """ A gaussian mutator for G2DList of Real - Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also - accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively - represents the mean and the std. dev. of the random distribution. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also + accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively + represents the mean and the std. dev. of the random distribution. - """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + """ + from . import Consts - mutations = args["pmut"] * elements + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mu = genome.getParam("gauss_mu") - sigma = genome.getParam("gauss_sigma") + mutations = args["pmut"] * elements - if mu is None: - mu = Consts.CDefG2DListMutRealMU + mu = genome.getParam("gauss_mu") + sigma = genome.getParam("gauss_sigma") - if sigma is None: - sigma = Consts.CDefG2DListMutRealSIGMA + if mu is None: + mu = Consts.CDefG2DListMutRealMU - if mutations < 1.0: - mutations = 0 + if sigma is None: + sigma = Consts.CDefG2DListMutRealSIGMA - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - final_value = genome[i][j] + rand_gauss(mu, sigma) + if mutations < 1.0: + mutations = 0 - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + final_value = genome[i][j] + rand_gauss(mu, sigma) - genome.setItem(i, j, final_value) - mutations += 1 - else: + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getWidth() - 1) - which_y = rand_randint(0, genome.getHeight() - 1) + genome.setItem(i, j, final_value) + mutations += 1 + else: + + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getWidth() - 1) + which_y = rand_randint(0, genome.getHeight() - 1) + + final_value = genome[which_y][which_x] + rand_gauss(mu, sigma) - final_value = genome[which_y][which_x] + rand_gauss(mu, sigma) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + genome.setItem(which_y, which_x, final_value) - genome.setItem(which_y, which_x, final_value) + return int(mutations) - return int(mutations) def G2DListMutatorRealGaussianGradient(genome, **args): - """ A gaussian gradient mutator for G2DList of Real + """ A gaussian gradient mutator for G2DList of Real - Accepts the *rangemin* and *rangemax* genome parameters, both optional. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. - The difference is that this multiplies the gene by gauss(1.0, 0.0333), allowing - for a smooth gradient drift about the value. + The difference is that this multiplies the gene by gauss(1.0, 0.0333), allowing + for a smooth gradient drift about the value. - """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + """ + from . import Consts - mutations = args["pmut"] * elements + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mu = Consts.CDefGaussianGradientMU - sigma = Consts.CDefGaussianGradientSIGMA + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 + mu = Consts.CDefGaussianGradientMU + sigma = Consts.CDefGaussianGradientSIGMA - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - final_value = genome[i][j] * abs(rand_gauss(mu, sigma)) + if mutations < 1.0: + mutations = 0 - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + final_value = genome[i][j] * abs(rand_gauss(mu, sigma)) - genome.setItem(i, j, final_value) - mutations += 1 - else: + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getWidth() - 1) - which_y = rand_randint(0, genome.getHeight() - 1) + genome.setItem(i, j, final_value) + mutations += 1 + else: + + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getWidth() - 1) + which_y = rand_randint(0, genome.getHeight() - 1) + + final_value = genome[which_y][which_x] * abs(rand_gauss(mu, sigma)) - final_value = genome[which_y][which_x] * abs(rand_gauss(mu, sigma)) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + genome.setItem(which_y, which_x, final_value) - genome.setItem(which_y, which_x, final_value) + return int(mutations) - return int(mutations) -############################# -## 2D Binary String ## -############################# +# 2D Binary String def G2DBinaryStringMutatorSwap(genome, **args): - """ The mutator of G2DBinaryString, Swap Mutator + """ The mutator of G2DBinaryString, Swap Mutator - .. versionadded:: 0.6 - The *G2DBinaryStringMutatorSwap* function - """ + .. versionadded:: 0.6 + The *G2DBinaryStringMutatorSwap* function + """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mutations = args["pmut"] * elements + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 - for i in xrange(height): - for j in xrange(width): - if Util.randomFlipCoin(args["pmut"]): - index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) - Util.list2DSwapElement(genome.genomeString, (i, j), index_b) - mutations += 1 - else: - for it in xrange(int(round(mutations))): - index_a = (rand_randint(0, height - 1), rand_randint(0, width - 1)) - index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) - Util.list2DSwapElement(genome.genomeString, index_a, index_b) + if mutations < 1.0: + mutations = 0 + for i in range(height): + for j in range(width): + if Util.randomFlipCoin(args["pmut"]): + index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) + Util.list2DSwapElement(genome.genomeString, (i, j), index_b) + mutations += 1 + else: + for it in range(int(round(mutations))): + index_a = (rand_randint(0, height - 1), rand_randint(0, width - 1)) + index_b = (rand_randint(0, height - 1), rand_randint(0, width - 1)) + Util.list2DSwapElement(genome.genomeString, index_a, index_b) - return int(mutations) + return int(mutations) def G2DBinaryStringMutatorFlip(genome, **args): - """ A flip mutator for G2DBinaryString + """ A flip mutator for G2DBinaryString - .. versionadded:: 0.6 - The *G2DBinaryStringMutatorFlip* function - """ - if args["pmut"] <= 0.0: - return 0 - height, width = genome.getSize() - elements = height * width + .. versionadded:: 0.6 + The *G2DBinaryStringMutatorFlip* function + """ + if args["pmut"] <= 0.0: + return 0 + height, width = genome.getSize() + elements = height * width - mutations = args["pmut"] * elements + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 + if mutations < 1.0: + mutations = 0 + + for i in range(genome.getHeight()): + for j in range(genome.getWidth()): + if Util.randomFlipCoin(args["pmut"]): + if genome[i][j] == 0: + genome.setItem(i, j, 1) + else: + genome.setItem(i, j, 0) + mutations += 1 + else: # TODO very suspicious branch + + for it in range(int(round(mutations))): + which_x = rand_randint(0, genome.getWidth() - 1) + which_y = rand_randint(0, genome.getHeight() - 1) + + if genome[i][j] == 0: + genome.setItem(which_y, which_x, 1) + else: + genome.setItem(which_y, which_x, 0) + + return int(mutations) + + +# Tree - for i in xrange(genome.getHeight()): - for j in xrange(genome.getWidth()): - if Util.randomFlipCoin(args["pmut"]): - if genome[i][j] == 0: - genome.setItem(i, j, 1) - else: - genome.setItem(i, j, 0) - mutations += 1 - else: - - for it in xrange(int(round(mutations))): - which_x = rand_randint(0, genome.getWidth() - 1) - which_y = rand_randint(0, genome.getHeight() - 1) - - if genome[i][j] == 0: - genome.setItem(which_y, which_x, 1) - else: - genome.setItem(which_y, which_x, 0) - - return int(mutations) - -################# -## Tree ## -################# def GTreeMutatorSwap(genome, **args): - """ The mutator of GTree, Swap Mutator - - .. versionadded:: 0.6 - The *GTreeMutatorSwap* function - """ - if args["pmut"] <= 0.0: - return 0 - elements = len(genome) - mutations = args["pmut"] * elements - - if mutations < 1.0: - mutations = 0 - for i in xrange(len(genome)): - if Util.randomFlipCoin(args["pmut"]): - mutations += 1 + """ The mutator of GTree, Swap Mutator + + .. versionadded:: 0.6 + The *GTreeMutatorSwap* function + """ + if args["pmut"] <= 0.0: + return 0 + elements = len(genome) + mutations = args["pmut"] * elements + + if mutations < 1.0: + mutations = 0 + for i in range(len(genome)): + if Util.randomFlipCoin(args["pmut"]): + mutations += 1 + nodeOne = genome.getRandomNode() + nodeTwo = genome.getRandomNode() + nodeOne.swapNodeData(nodeTwo) + else: + for it in range(int(round(mutations))): nodeOne = genome.getRandomNode() nodeTwo = genome.getRandomNode() nodeOne.swapNodeData(nodeTwo) - else: - for it in xrange(int(round(mutations))): - nodeOne = genome.getRandomNode() - nodeTwo = genome.getRandomNode() - nodeOne.swapNodeData(nodeTwo) - return int(mutations) + return int(mutations) def GTreeMutatorIntegerRange(genome, **args): - """ The mutator of GTree, Integer Range Mutator + """ The mutator of GTree, Integer Range Mutator - Accepts the *rangemin* and *rangemax* genome parameters, both optional. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. - .. versionadded:: 0.6 - The *GTreeMutatorIntegerRange* function - """ - if args["pmut"] <= 0.0: - return 0 - elements = len(genome) - mutations = args["pmut"] * elements + .. versionadded:: 0.6 + The *GTreeMutatorIntegerRange* function + """ + from . import Consts - range_min = genome.getParam("rangemin", Consts.CDefRangeMin) - range_max = genome.getParam("rangemax", Consts.CDefRangeMax) + if args["pmut"] <= 0.0: + return 0 + elements = len(genome) + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 - for i in xrange(len(genome)): - if Util.randomFlipCoin(args["pmut"]): - mutations += 1 + range_min = genome.getParam("rangemin", Consts.CDefRangeMin) + range_max = genome.getParam("rangemax", Consts.CDefRangeMax) + + if mutations < 1.0: + mutations = 0 + for i in range(len(genome)): + if Util.randomFlipCoin(args["pmut"]): + mutations += 1 + rand_node = genome.getRandomNode() + random_int = rand_randint(range_min, range_max) + rand_node.setData(random_int) + + else: + for it in range(int(round(mutations))): rand_node = genome.getRandomNode() random_int = rand_randint(range_min, range_max) rand_node.setData(random_int) - else: - for it in xrange(int(round(mutations))): - rand_node = genome.getRandomNode() - random_int = rand_randint(range_min, range_max) - rand_node.setData(random_int) - - return int(mutations) + return int(mutations) def GTreeMutatorRealRange(genome, **args): - """ The mutator of GTree, Real Range Mutator + """ The mutator of GTree, Real Range Mutator + + Accepts the *rangemin* and *rangemax* genome parameters, both optional. - Accepts the *rangemin* and *rangemax* genome parameters, both optional. + .. versionadded:: 0.6 + The *GTreeMutatorRealRange* function + """ + from . import Consts - .. versionadded:: 0.6 - The *GTreeMutatorRealRange* function - """ - if args["pmut"] <= 0.0: - return 0 - elements = len(genome) - mutations = args["pmut"] * elements + if args["pmut"] <= 0.0: + return 0 + elements = len(genome) + mutations = args["pmut"] * elements - range_min = genome.getParam("rangemin", Consts.CDefRangeMin) - range_max = genome.getParam("rangemax", Consts.CDefRangeMax) + range_min = genome.getParam("rangemin", Consts.CDefRangeMin) + range_max = genome.getParam("rangemax", Consts.CDefRangeMax) - if mutations < 1.0: - mutations = 0 - for i in xrange(len(genome)): - if Util.randomFlipCoin(args["pmut"]): - mutations += 1 + if mutations < 1.0: + mutations = 0 + for i in range(len(genome)): + if Util.randomFlipCoin(args["pmut"]): + mutations += 1 + rand_node = genome.getRandomNode() + random_real = rand_uniform(range_min, range_max) + rand_node.setData(random_real) + + else: + for it in range(int(round(mutations))): rand_node = genome.getRandomNode() random_real = rand_uniform(range_min, range_max) rand_node.setData(random_real) - else: - for it in xrange(int(round(mutations))): - rand_node = genome.getRandomNode() - random_real = rand_uniform(range_min, range_max) - rand_node.setData(random_real) - - return int(mutations) + return int(mutations) def GTreeMutatorIntegerGaussian(genome, **args): - """ A gaussian mutator for GTree of Integers + """ A gaussian mutator for GTree of Integers - Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also - accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively - represents the mean and the std. dev. of the random distribution. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also + accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively + represents the mean and the std. dev. of the random distribution. - """ - if args["pmut"] <= 0.0: - return 0 - elements = len(genome) - mutations = args["pmut"] * elements + """ + from . import Consts - mu = genome.getParam("gauss_mu", Consts.CDefG1DListMutIntMU) - sigma = genome.getParam("gauss_sigma", Consts.CDefG1DListMutIntSIGMA) + if args["pmut"] <= 0.0: + return 0 + elements = len(genome) + mutations = args["pmut"] * elements - if mutations < 1.0: - mutations = 0 - for i in xrange(len(genome)): - if Util.randomFlipCoin(args["pmut"]): - mutations += 1 + mu = genome.getParam("gauss_mu", Consts.CDefG1DListMutIntMU) + sigma = genome.getParam("gauss_sigma", Consts.CDefG1DListMutIntSIGMA) + + if mutations < 1.0: + mutations = 0 + for i in range(len(genome)): + if Util.randomFlipCoin(args["pmut"]): + mutations += 1 + rand_node = genome.getRandomNode() + final_value = rand_node.getData() + int(rand_gauss(mu, sigma)) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + rand_node.setData(final_value) + else: + for it in range(int(round(mutations))): rand_node = genome.getRandomNode() final_value = rand_node.getData() + int(rand_gauss(mu, sigma)) final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) rand_node.setData(final_value) - else: - for it in xrange(int(round(mutations))): - rand_node = genome.getRandomNode() - final_value = rand_node.getData() + int(rand_gauss(mu, sigma)) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - rand_node.setData(final_value) - return int(mutations) + return int(mutations) def GTreeMutatorRealGaussian(genome, **args): - """ A gaussian mutator for GTree of Real numbers + """ A gaussian mutator for GTree of Real numbers - Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also - accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively - represents the mean and the std. dev. of the random distribution. + Accepts the *rangemin* and *rangemax* genome parameters, both optional. Also + accepts the parameter *gauss_mu* and the *gauss_sigma* which respectively + represents the mean and the std. dev. of the random distribution. - """ - if args["pmut"] <= 0.0: - return 0 - elements = len(genome) - mutations = args["pmut"] * elements + """ + from . import Consts + if args["pmut"] <= 0.0: + return 0 + elements = len(genome) + mutations = args["pmut"] * elements - mu = genome.getParam("gauss_mu", Consts.CDefG1DListMutRealMU) - sigma = genome.getParam("gauss_sigma", Consts.CDefG1DListMutRealSIGMA) + mu = genome.getParam("gauss_mu", Consts.CDefG1DListMutRealMU) + sigma = genome.getParam("gauss_sigma", Consts.CDefG1DListMutRealSIGMA) - if mutations < 1.0: - mutations = 0 - for i in xrange(len(genome)): - if Util.randomFlipCoin(args["pmut"]): - mutations += 1 + if mutations < 1.0: + mutations = 0 + for i in range(len(genome)): + if Util.randomFlipCoin(args["pmut"]): + mutations += 1 + rand_node = genome.getRandomNode() + final_value = rand_node.getData() + rand_gauss(mu, sigma) + final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) + final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) + rand_node.setData(final_value) + else: + for it in range(int(round(mutations))): rand_node = genome.getRandomNode() final_value = rand_node.getData() + rand_gauss(mu, sigma) final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) rand_node.setData(final_value) - else: - for it in xrange(int(round(mutations))): - rand_node = genome.getRandomNode() - final_value = rand_node.getData() + rand_gauss(mu, sigma) - final_value = min(final_value, genome.getParam("rangemax", Consts.CDefRangeMax)) - final_value = max(final_value, genome.getParam("rangemin", Consts.CDefRangeMin)) - rand_node.setData(final_value) - return int(mutations) + return int(mutations) + -################### -## Tree GP ## -################### +# Tree GP def GTreeGPMutatorOperation(genome, **args): - """ The mutator of GTreeGP, Operation Mutator - - .. versionadded:: 0.6 - The *GTreeGPMutatorOperation* function - """ + """ The mutator of GTreeGP, Operation Mutator - if args["pmut"] <= 0.0: - return 0 - elements = len(genome) - mutations = args["pmut"] * elements - ga_engine = args["ga_engine"] + .. versionadded:: 0.6 + The *GTreeGPMutatorOperation* function + """ + from . import Consts + if args["pmut"] <= 0.0: + return 0 + elements = len(genome) + mutations = args["pmut"] * elements + ga_engine = args["ga_engine"] - gp_terminals = ga_engine.getParam("gp_terminals") - assert gp_terminals is not None + gp_terminals = ga_engine.getParam("gp_terminals") + assert gp_terminals is not None - gp_function_set = ga_engine.getParam("gp_function_set") - assert gp_function_set is not None + gp_function_set = ga_engine.getParam("gp_function_set") + assert gp_function_set is not None - if mutations < 1.0: - mutations = 0 - for i in xrange(len(genome)): - if Util.randomFlipCoin(args["pmut"]): - mutations += 1 + if mutations < 1.0: + mutations = 0 + for i in range(len(genome)): + if Util.randomFlipCoin(args["pmut"]): + mutations += 1 + rand_node = genome.getRandomNode() + assert rand_node is not None + if rand_node.getType() == Consts.nodeType["TERMINAL"]: + term_operator = rand_choice(gp_terminals) + else: + op_len = gp_function_set[rand_node.getData()] + fun_candidates = [] + for o, length in list(gp_function_set.items()): + if length == op_len: + fun_candidates.append(o) + + if len(fun_candidates) <= 0: + continue + + term_operator = rand_choice(fun_candidates) + rand_node.setData(term_operator) + else: + for _ in range(int(round(mutations))): # TODO probably inoptimal rand_node = genome.getRandomNode() assert rand_node is not None if rand_node.getType() == Consts.nodeType["TERMINAL"]: - term_operator = rand_choice(gp_terminals) + term_operator = rand_choice(gp_terminals) else: - op_len = gp_function_set[rand_node.getData()] - fun_candidates = [] - for o, l in gp_function_set.items(): - if l == op_len: - fun_candidates.append(o) + op_len = gp_function_set[rand_node.getData()] + fun_candidates = [] + for o, length in list(gp_function_set.items()): + if length == op_len: + fun_candidates.append(o) - if len(fun_candidates) <= 0: - continue + if len(fun_candidates) <= 0: + continue - term_operator = rand_choice(fun_candidates) + term_operator = rand_choice(fun_candidates) rand_node.setData(term_operator) - else: - for it in xrange(int(round(mutations))): - rand_node = genome.getRandomNode() - assert rand_node is not None - if rand_node.getType() == Consts.nodeType["TERMINAL"]: - term_operator = rand_choice(gp_terminals) - else: - op_len = gp_function_set[rand_node.getData()] - fun_candidates = [] - for o, l in gp_function_set.items(): - if l == op_len: - fun_candidates.append(o) - - if len(fun_candidates) <= 0: - continue - term_operator = rand_choice(fun_candidates) - rand_node.setData(term_operator) - - return int(mutations) + return int(mutations) def GTreeGPMutatorSubtree(genome, **args): - """ The mutator of GTreeGP, Subtree Mutator + """ The mutator of GTreeGP, Subtree Mutator - This mutator will recreate random subtree of the tree using the grow algorithm. + This mutator will recreate random subtree of the tree using the grow algorithm. - .. versionadded:: 0.6 - The *GTreeGPMutatorSubtree* function - """ + .. versionadded:: 0.6 + The *GTreeGPMutatorSubtree* function + """ - if args["pmut"] <= 0.0: - return 0 - ga_engine = args["ga_engine"] - max_depth = genome.getParam("max_depth", None) - mutations = 0 + if args["pmut"] <= 0.0: + return 0 + ga_engine = args["ga_engine"] + max_depth = genome.getParam("max_depth", None) + mutations = 0 - if max_depth is None: - Util.raiseException("You must specify the max_depth genome parameter !", ValueError) + if max_depth is None: + Util.raiseException("You must specify the max_depth genome parameter !", ValueError) - if max_depth < 0: - Util.raiseException("The max_depth must be >= 1, if you want to use GTreeGPMutatorSubtree crossover !", ValueError) + if max_depth < 0: + Util.raiseException( + "The max_depth must be >= 1, if you want to use GTreeGPMutatorSubtree crossover !", ValueError) - branch_list = genome.nodes_branch - elements = len(branch_list) + branch_list = genome.nodes_branch + elements = len(branch_list) - for i in xrange(elements): + for i in range(elements): - node = branch_list[i] - assert node is not None + node = branch_list[i] + assert node is not None - if Util.randomFlipCoin(args["pmut"]): - depth = genome.getNodeDepth(node) - mutations += 1 + if Util.randomFlipCoin(args["pmut"]): + depth = genome.getNodeDepth(node) + mutations += 1 - root_subtree = GTree.buildGTreeGPGrow(ga_engine, 0, max_depth - depth) - node_parent = node.getParent() + root_subtree = GTree.buildGTreeGPGrow(ga_engine, 0, max_depth - depth) + node_parent = node.getParent() - if node_parent is None: - genome.setRoot(root_subtree) + if node_parent is None: + genome.setRoot(root_subtree) + genome.processNodes() + return mutations + else: + root_subtree.setParent(node_parent) + node_parent.replaceChild(node, root_subtree) genome.processNodes() - return mutations - else: - root_subtree.setParent(node_parent) - node_parent.replaceChild(node, root_subtree) - genome.processNodes() - return int(mutations) + return int(mutations) diff --git a/pyevolve/Network.py b/pyevolve/Network.py index 726c82e..8e2ffc8 100644 --- a/pyevolve/Network.py +++ b/pyevolve/Network.py @@ -9,13 +9,15 @@ The *Network* module. """ -from __future__ import with_statement -import threading +from __future__ import print_function + +import logging +import pickle import socket -import time import sys -import Util -import cPickle +import threading +import time + try: import zlib @@ -23,422 +25,429 @@ except ImportError: ZLIB_SUPPORT = False -import Consts -import logging +from . import Consts +from . import Util + def getMachineIP(): - """ Return all the IPs from current machine. + """ Return all the IPs from current machine. - Example: - >>> Util.getMachineIP() - ['200.12.124.181', '192.168.0.1'] + Example: + >>> Util.getMachineIP() + ['200.12.124.181', '192.168.0.1'] - :rtype: a python list with the string IPs + :rtype: a python list with the string IPs + + """ + hostname = socket.gethostname() + addresses = socket.getaddrinfo(hostname, None) + ips = [x[4][0] for x in addresses] + return ips - """ - hostname = socket.gethostname() - addresses = socket.getaddrinfo(hostname, None) - ips = [x[4][0] for x in addresses] - return ips class UDPThreadBroadcastClient(threading.Thread): - """ The Broadcast UDP client thread class. + """ The Broadcast UDP client thread class. + + This class is a thread to serve as Pyevolve client on the UDP + datagrams, it is used to send data over network lan/wan. - This class is a thread to serve as Pyevolve client on the UDP - datagrams, it is used to send data over network lan/wan. + Example: + >>> s = Network.UDPThreadClient('192.168.0.2', 1500, 666) + >>> s.setData("Test data") + >>> s.start() + >>> s.join() - Example: - >>> s = Network.UDPThreadClient('192.168.0.2', 1500, 666) - >>> s.setData("Test data") - >>> s.start() - >>> s.join() + :param host: the hostname to bind the socket on sender (this is NOT the target host) + :param port: the sender port (this is NOT the target port) + :param target_port: the destination port target - :param host: the hostname to bind the socket on sender (this is NOT the target host) - :param port: the sender port (this is NOT the target port) - :param target_port: the destination port target + """ + def __init__(self, host, port, target_port): + super(UDPThreadBroadcastClient, self).__init__() + self.host = host + self.port = port + self.targetPort = target_port + self.data = None + self.sentBytes = None + self.sentBytesLock = threading.Lock() - """ - def __init__(self, host, port, target_port): - super(UDPThreadBroadcastClient, self).__init__() - self.host = host - self.port = port - self.targetPort = target_port - self.data = None - self.sentBytes = None - self.sentBytesLock = threading.Lock() + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + self.sock.bind((host, port)) - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - self.sock.bind((host, port)) + def setData(self, data): + """ Set the data to send - def setData(self, data): - """ Set the data to send + :param data: the data to send - :param data: the data to send + """ + self.data = data - """ - self.data = data + def getData(self): + """ Get the data to send - def getData(self): - """ Get the data to send + :rtype: data to send - :rtype: data to send + """ + return self.data - """ - return self.data + def close(self): + """ Close the internal socket """ + self.sock.close() - def close(self): - """ Close the internal socket """ - self.sock.close() + def getSentBytes(self): + """ Returns the number of sent bytes. The use of this method makes sense + when you already have sent the data - def getSentBytes(self): - """ Returns the number of sent bytes. The use of this method makes sense - when you already have sent the data + :rtype: sent bytes - :rtype: sent bytes + """ + sent = None + with self.sentBytesLock: + if self.sentBytes is None: + Util.raiseException('Bytes sent is None') + else: + sent = self.sentBytes + return sent - """ - sent = None - with self.sentBytesLock: - if self.sentBytes is None: - Util.raiseException('Bytes sent is None') - else: - sent = self.sentBytes - return sent + def send(self): + """ Broadcasts the data """ + return self.sock.sendto(self.data, (Consts.CDefBroadcastAddress, self.targetPort)) - def send(self): - """ Broadcasts the data """ - return self.sock.sendto(self.data, (Consts.CDefBroadcastAddress, self.targetPort)) + def run(self): + """ Method called when you call *.start()* of the thread """ + if self.data is None: + Util.raiseException('You must set the data with setData method', ValueError) - def run(self): - """ Method called when you call *.start()* of the thread """ - if self.data is None: - Util.raiseException('You must set the data with setData method', ValueError) + with self.sentBytesLock: + self.sentBytes = self.send() + self.close() - with self.sentBytesLock: - self.sentBytes = self.send() - self.close() class UDPThreadUnicastClient(threading.Thread): - """ The Unicast UDP client thread class. + """ The Unicast UDP client thread class. + + This class is a thread to serve as Pyevolve client on the UDP + datagrams, it is used to send data over network lan/wan. - This class is a thread to serve as Pyevolve client on the UDP - datagrams, it is used to send data over network lan/wan. + Example: + >>> s = Network.UDPThreadClient('192.168.0.2', 1500) + >>> s.setData("Test data") + >>> s.setTargetHost('192.168.0.50', 666) + >>> s.start() + >>> s.join() - Example: - >>> s = Network.UDPThreadClient('192.168.0.2', 1500) - >>> s.setData("Test data") - >>> s.setTargetHost('192.168.0.50', 666) - >>> s.start() - >>> s.join() + :param host: the hostname to bind the socket on sender (this is not the target host) + :param port: the sender port (this is not the target port) + :param pool_size: the size of send pool + :param timeout: the time interval to check if the client have data to send - :param host: the hostname to bind the socket on sender (this is not the target host) - :param port: the sender port (this is not the target port) - :param pool_size: the size of send pool - :param timeout: the time interval to check if the client have data to send + """ + def __init__(self, host, port, pool_size=10, timeout=0.5): + super(UDPThreadUnicastClient, self).__init__() + self.host = host + self.port = port + self.target = [] + self.sendPool = [] + self.poolSize = pool_size + self.sendPoolLock = threading.Lock() + self.timeout = timeout - """ - def __init__(self, host, port, pool_size=10, timeout=0.5): - super(UDPThreadUnicastClient, self).__init__() - self.host = host - self.port = port - self.target = [] - self.sendPool = [] - self.poolSize = pool_size - self.sendPoolLock = threading.Lock() - self.timeout = timeout + self.doshutdown = False - self.doshutdown = False + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.bind((host, port)) - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.sock.bind((host, port)) + def poolLength(self): + """ Returns the size of the pool - def poolLength(self): - """ Returns the size of the pool + :rtype: integer - :rtype: integer + """ + with self.sendPoolLock: + ret = len(self.sendPool) + return ret - """ - with self.sendPoolLock: - ret = len(self.sendPool) - return ret + def popPool(self): + """ Return the last data received on the pool - def popPool(self): - """ Return the last data received on the pool + :rtype: object - :rtype: object + """ + with self.sendPoolLock: + ret = self.sendPool.pop() + return ret - """ - with self.sendPoolLock: - ret = self.sendPool.pop() - return ret + def isReady(self): + """ Returns True when there is data on the pool or False when not - def isReady(self): - """ Returns True when there is data on the pool or False when not + :rtype: boolean - :rtype: boolean + """ + with self.sendPoolLock: + ret = True if len(self.sendPool) >= 1 else False + return ret - """ - with self.sendPoolLock: - ret = True if len(self.sendPool) >= 1 else False - return ret + def shutdown(self): + """ Shutdown the server thread, when called, this method will stop + the thread on the next socket timeout """ + self.doshutdown = True - def shutdown(self): - """ Shutdown the server thread, when called, this method will stop - the thread on the next socket timeout """ - self.doshutdown = True + def addData(self, data): + """ Set the data to send - def addData(self, data): - """ Set the data to send + :param data: the data to send - :param data: the data to send + """ + if self.poolLength() >= self.poolSize: + logging.warning('the send pool is full, consider increasing the pool size or decreasing the timeout !') + return - """ - if self.poolLength() >= self.poolSize: - logging.warning('the send pool is full, consider increasing the pool size or decreasing the timeout !') - return + with self.sendPoolLock: + self.sendPool.append(data) - with self.sendPoolLock: - self.sendPool.append(data) + def setTargetHost(self, host, port): + """ Set the host/port of the target, the destination - def setTargetHost(self, host, port): - """ Set the host/port of the target, the destination + :param host: the target host + :param port: the target port - :param host: the target host - :param port: the target port + .. note:: the host will be ignored when using broadcast mode + """ + del self.target[:] + self.target.append((host, port)) - .. note:: the host will be ignored when using broadcast mode - """ - del self.target[:] - self.target.append((host, port)) + def setMultipleTargetHost(self, address_list): + """ Sets multiple host/port targets, the destinations - def setMultipleTargetHost(self, address_list): - """ Sets multiple host/port targets, the destinations + :param address_list: a list with tuples (ip, port) + """ + del self.target[:] + self.target = address_list[:] - :param address_list: a list with tuples (ip, port) - """ - del self.target[:] - self.target = address_list[:] + def close(self): + """ Close the internal socket """ + self.sock.close() - def close(self): - """ Close the internal socket """ - self.sock.close() + def send(self, data): + """ Send the data - def send(self, data): - """ Send the data + :param data: the data to send + :rtype: bytes sent to each destination + """ + bytes = -1 + for destination in self.target: + bytes = self.sock.sendto(data, destination) + return bytes - :param data: the data to send - :rtype: bytes sent to each destination - """ - bytes = -1 - for destination in self.target: - bytes = self.sock.sendto(data, destination) - return bytes + def run(self): + """ Method called when you call *.start()* of the thread """ + if len(self.target) <= 0: + Util.raiseException('You must set the target(s) before send data', ValueError) - def run(self): - """ Method called when you call *.start()* of the thread """ - if len(self.target) <= 0: - Util.raiseException('You must set the target(s) before send data', ValueError) + while True: + if self.doshutdown: + break - while True: - if self.doshutdown: - break + while self.isReady(): + data = self.popPool() + self.send(data) - while self.isReady(): - data = self.popPool() - self.send(data) + time.sleep(self.timeout) - time.sleep(self.timeout) + self.close() - self.close() class UDPThreadServer(threading.Thread): - """ The UDP server thread class. + """ The UDP server thread class. - This class is a thread to serve as Pyevolve server on the UDP - datagrams, it is used to receive data from network lan/wan. + This class is a thread to serve as Pyevolve server on the UDP + datagrams, it is used to receive data from network lan/wan. - Example: - >>> s = UDPThreadServer("192.168.0.2", 666, 10) - >>> s.start() - >>> s.shutdown() + Example: + >>> s = UDPThreadServer("192.168.0.2", 666, 10) + >>> s.start() + >>> s.shutdown() + + :param host: the host to bind the server + :param port: the server port to bind + :param poolSize: the size of the server pool + :param timeout: the socket timeout + + .. note:: this thread implements a pool to keep the received data, + the *poolSize* parameter specifies how much individuals + we must keep on the pool until the *popPool* method + is called; when the pool is full, the sever will + discard the received individuals. + + """ + def __init__(self, host, port, poolSize=10, timeout=3): + super(UDPThreadServer, self).__init__() + self.recvPool = [] + self.recvPoolLock = threading.Lock() + self.bufferSize = 4096 + self.host = host + self.port = port + self.timeout = timeout + self.doshutdown = False + self.poolSize = poolSize + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.bind((host, port)) + self.sock.settimeout(self.timeout) + + def shutdown(self): + """ Shutdown the server thread, when called, this method will stop + the thread on the next socket timeout """ + self.doshutdown = True + + def isReady(self): + """ Returns True when there is data on the pool or False when not + + :rtype: boolean + + """ + with self.recvPoolLock: + ret = True if len(self.recvPool) >= 1 else False + return ret + + def poolLength(self): + """ Returns the size of the pool + + :rtype: integer + + """ + with self.recvPoolLock: + ret = len(self.recvPool) + return ret + + def popPool(self): + """ Return the last data received on the pool + + :rtype: object + + """ + with self.recvPoolLock: + ret = self.recvPool.pop() + return ret + + def close(self): + """ Closes the internal socket """ + self.sock.close() + + def setBufferSize(self, size): + """ Sets the receive buffer size + + :param size: integer + + """ + self.bufferSize = size + + def getBufferSize(self): + """ Gets the current receive buffer size + + :rtype: integer + + """ + return self.bufferSize + + def getData(self): + """ Calls the socket *recvfrom* method and waits for the data, + when the data is received, the method will return a tuple + with the IP of the sender and the data received. When a timeout + exception occurs, the method return None. + + :rtype: tuple (sender ip, data) or None when timeout exception + + """ + try: + data, sender = self.sock.recvfrom(self.bufferSize) + except socket.timeout: + return None + return (sender[0], data) + + def run(self): + """ Called when the thread is started by the user. This method + is the main of the thread, when called, it will enter in loop + to wait data or shutdown when needed. + """ + while True: + # Get the data + data = self.getData() + # Shutdown called + if self.doshutdown: + break + # The pool is full + if self.poolLength() >= self.poolSize: + continue + # There is no data received + if data is None: + continue + # It's a packet from myself + if data[0] == self.host: + continue + with self.recvPoolLock: + self.recvPool.append(data) + + self.close() - :param host: the host to bind the server - :param port: the server port to bind - :param poolSize: the size of the server pool - :param timeout: the socket timeout - - .. note:: this thread implements a pool to keep the received data, - the *poolSize* parameter specifies how much individuals - we must keep on the pool until the *popPool* method - is called; when the pool is full, the sever will - discard the received individuals. - - """ - def __init__(self, host, port, poolSize=10, timeout=3): - super(UDPThreadServer, self).__init__() - self.recvPool = [] - self.recvPoolLock = threading.Lock() - self.bufferSize = 4096 - self.host = host - self.port = port - self.timeout = timeout - self.doshutdown = False - self.poolSize = poolSize - - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.sock.bind((host, port)) - self.sock.settimeout(self.timeout) - - def shutdown(self): - """ Shutdown the server thread, when called, this method will stop - the thread on the next socket timeout """ - self.doshutdown = True - - def isReady(self): - """ Returns True when there is data on the pool or False when not - - :rtype: boolean - - """ - with self.recvPoolLock: - ret = True if len(self.recvPool) >= 1 else False - return ret - - def poolLength(self): - """ Returns the size of the pool - - :rtype: integer - - """ - with self.recvPoolLock: - ret = len(self.recvPool) - return ret - - def popPool(self): - """ Return the last data received on the pool - - :rtype: object - - """ - with self.recvPoolLock: - ret = self.recvPool.pop() - return ret - - def close(self): - """ Closes the internal socket """ - self.sock.close() - - def setBufferSize(self, size): - """ Sets the receive buffer size - - :param size: integer - - """ - self.bufferSize = size - - def getBufferSize(self): - """ Gets the current receive buffer size - - :rtype: integer - - """ - return self.bufferSize - - def getData(self): - """ Calls the socket *recvfrom* method and waits for the data, - when the data is received, the method will return a tuple - with the IP of the sender and the data received. When a timeout - exception occurs, the method return None. - - :rtype: tuple (sender ip, data) or None when timeout exception - - """ - try: - data, sender = self.sock.recvfrom(self.bufferSize) - except socket.timeout: - return None - return (sender[0], data) - - def run(self): - """ Called when the thread is started by the user. This method - is the main of the thread, when called, it will enter in loop - to wait data or shutdown when needed. - """ - while True: - # Get the data - data = self.getData() - # Shutdown called - if self.doshutdown: - break - # The pool is full - if self.poolLength() >= self.poolSize: - continue - # There is no data received - if data is None: - continue - # It's a packet from myself - if data[0] == self.host: - continue - with self.recvPoolLock: - self.recvPool.append(data) - - self.close() def pickleAndCompress(obj, level=9): - """ Pickles the object and compress the dumped string with zlib - - :param obj: the object to be pickled - :param level: the compression level, 9 is the best - and -1 is to not compress - - """ - pickled = cPickle.dumps(obj) - if level < 0: - return pickled - else: - if not ZLIB_SUPPORT: - Util.raiseException('zlib not found !', ImportError) - pickled_zlib = zlib.compress(pickled, level) - return pickled_zlib + """ Pickles the object and compress the dumped string with zlib + + :param obj: the object to be pickled + :param level: the compression level, 9 is the best + and -1 is to not compress + + """ + pickled = pickle.dumps(obj) + if level < 0: + return pickled + else: + if not ZLIB_SUPPORT: + Util.raiseException('zlib not found !', ImportError) + pickled_zlib = zlib.compress(pickled, level) + return pickled_zlib + def unpickleAndDecompress(obj_dump, decompress=True): - """ Decompress a zlib compressed string and unpickle the data - - :param obj: the object to be decompressend and unpickled - """ - if decompress: - if not ZLIB_SUPPORT: - Util.raiseException('zlib not found !', ImportError) - obj_decompress = zlib.decompress(obj_dump) - else: - obj_decompress = obj_dump - return cPickle.loads(obj_decompress) + """ Decompress a zlib compressed string and unpickle the data + + :param obj: the object to be decompressend and unpickled + """ + if decompress: + if not ZLIB_SUPPORT: + Util.raiseException('zlib not found !', ImportError) + obj_decompress = zlib.decompress(obj_dump) + else: + obj_decompress = obj_dump + return pickle.loads(obj_decompress) + if __name__ == "__main__": - arg = sys.argv[1] - myself = getMachineIP() - - if arg == "server": - s = UDPThreadServer(myself[0], 666) - s.start() - - while True: - print ".", - time.sleep(10) - if s.isReady(): - item = s.popPool() - print item - time.sleep(4) - s.shutdown() - break - elif arg == "client": - print "Binding on %s..." % myself[0] - s = UDPThreadUnicastClient(myself[0], 1500) - s.setData("dsfssdfsfddf") - s.setTargetHost(myself[0], 666) - s.start() - s.join() - print s.getSentBytes() - - print "end..." + arg = sys.argv[1] + myself = getMachineIP() + + if arg == "server": + s = UDPThreadServer(myself[0], 666) + s.start() + + while True: + print(".", end="") + time.sleep(10) + if s.isReady(): + item = s.popPool() + print(item) + time.sleep(4) + s.shutdown() + break + elif arg == "client": + print("Binding on %s..." % myself[0]) + s = UDPThreadUnicastClient(myself[0], 1500) + s.setData("dsfssdfsfddf") # TODO needs testing + s.setTargetHost(myself[0], 666) + s.start() + s.join() + print(s.getSentBytes()) + + print("end...") diff --git a/pyevolve/Scaling.py b/pyevolve/Scaling.py index a169dbf..f1da060 100644 --- a/pyevolve/Scaling.py +++ b/pyevolve/Scaling.py @@ -6,125 +6,135 @@ This module have the *scaling schemes* like Linear scaling, etc. """ -import Consts -import Util +from future.builtins import range + import math import logging + def LinearScaling(pop): - """ Linear Scaling scheme - - .. warning :: Linear Scaling is only for positive raw scores - - """ - logging.debug("Running linear scaling.") - pop.statistics() - c = Consts.CDefScaleLinearMultiplier - a = b = delta = 0.0 - - pop_rawAve = pop.stats["rawAve"] - pop_rawMax = pop.stats["rawMax"] - pop_rawMin = pop.stats["rawMin"] - - if pop_rawAve == pop_rawMax: - a = 1.0 - b = 0.0 - elif pop_rawMin > (c * pop_rawAve - pop_rawMax / c - 1.0): - delta = pop_rawMax - pop_rawAve - a = (c - 1.0) * pop_rawAve / delta - b = pop_rawAve * (pop_rawMax - (c * pop_rawAve)) / delta - else: - delta = pop_rawAve - pop_rawMin - a = pop_rawAve / delta - b = -pop_rawMin * pop_rawAve / delta - - for i in xrange(len(pop)): - f = pop[i].score - if f < 0.0: - Util.raiseException("Score %r is negative, linear scaling not supported !" % (f,), ValueError) - f = f * a + b - if f < 0: - f = 0.0 - pop[i].fitness = f + """ Linear Scaling scheme + + .. warning :: Linear Scaling is only for positive raw scores + + """ + from . import Consts, Util + logging.debug("Running linear scaling.") + pop.statistics() + c = Consts.CDefScaleLinearMultiplier + a = b = delta = 0.0 + + pop_rawAve = pop.stats["rawAve"] + pop_rawMax = pop.stats["rawMax"] + pop_rawMin = pop.stats["rawMin"] + + if pop_rawAve == pop_rawMax: + a = 1.0 + b = 0.0 + elif pop_rawMin > (c * pop_rawAve - pop_rawMax / c - 1.0): + delta = pop_rawMax - pop_rawAve + a = (c - 1.0) * pop_rawAve / delta + b = pop_rawAve * (pop_rawMax - (c * pop_rawAve)) / delta + else: + delta = pop_rawAve - pop_rawMin + a = pop_rawAve / delta + b = -pop_rawMin * pop_rawAve / delta + + for i in range(len(pop)): + f = pop[i].score + if f < 0.0: + Util.raiseException("Score %r is negative, linear scaling not supported !" % (f,), ValueError) + f = f * a + b + if f < 0: + f = 0.0 + pop[i].fitness = f + def SigmaTruncScaling(pop): - """ Sigma Truncation scaling scheme, allows negative scores """ - logging.debug("Running sigma truncation scaling.") - pop.statistics() - c = Consts.CDefScaleSigmaTruncMultiplier - pop_rawAve = pop.stats["rawAve"] - pop_rawDev = pop.stats["rawDev"] - for i in xrange(len(pop)): - f = pop[i].score - pop_rawAve - f += c * pop_rawDev - if f < 0: - f = 0.0 - pop[i].fitness = f + """ Sigma Truncation scaling scheme, allows negative scores """ + from . import Consts + logging.debug("Running sigma truncation scaling.") + pop.statistics() + c = Consts.CDefScaleSigmaTruncMultiplier + pop_rawAve = pop.stats["rawAve"] + pop_rawDev = pop.stats["rawDev"] + for i in range(len(pop)): + f = pop[i].score - pop_rawAve + f += c * pop_rawDev + if f < 0: + f = 0.0 + pop[i].fitness = f + def PowerLawScaling(pop): - """ Power Law scaling scheme + """ Power Law scaling scheme - .. warning :: Power Law Scaling is only for positive raw scores + .. warning :: Power Law Scaling is only for positive raw scores - """ - logging.debug("Running power law scaling.") - k = Consts.CDefScalePowerLawFactor - for i in xrange(len(pop)): - f = pop[i].score - if f < 0.0: - Util.raiseException("Score %r is negative, power law scaling not supported !" % (f,), ValueError) - f = math.pow(f, k) - pop[i].fitness = f + """ + from . import Consts + from . import Util + logging.debug("Running power law scaling.") + k = Consts.CDefScalePowerLawFactor + for i in range(len(pop)): + f = pop[i].score + if f < 0.0: + Util.raiseException("Score %r is negative, power law scaling not supported !" % (f,), ValueError) + f = math.pow(f, k) + pop[i].fitness = f def BoltzmannScaling(pop): - """ Boltzmann scaling scheme. You can specify the **boltz_temperature** to the - population parameters, this parameter will set the start temperature. You - can specify the **boltz_factor** and the **boltz_min** parameters, the **boltz_factor** - is the value that the temperature will be subtracted and the **boltz_min** is the - mininum temperature of the scaling scheme. + """ Boltzmann scaling scheme. You can specify the **boltz_temperature** to the + population parameters, this parameter will set the start temperature. You + can specify the **boltz_factor** and the **boltz_min** parameters, the **boltz_factor** + is the value that the temperature will be subtracted and the **boltz_min** is the + mininum temperature of the scaling scheme. + + .. versionadded: 0.6 + The `BoltzmannScaling` function. - .. versionadded: 0.6 - The `BoltzmannScaling` function. + """ + from . import Consts + boltz_temperature = pop.getParam("boltz_temperature", Consts.CDefScaleBoltzStart) + boltz_factor = pop.getParam("boltz_factor", Consts.CDefScaleBoltzFactor) + boltz_min = pop.getParam("boltz_min", Consts.CDefScaleBoltzMinTemp) - """ - boltz_temperature = pop.getParam("boltz_temperature", Consts.CDefScaleBoltzStart) - boltz_factor = pop.getParam("boltz_factor", Consts.CDefScaleBoltzFactor) - boltz_min = pop.getParam("boltz_min", Consts.CDefScaleBoltzMinTemp) + boltz_temperature -= boltz_factor + boltz_temperature = max(boltz_temperature, boltz_min) + pop.setParams(boltzTemperature=boltz_temperature) - boltz_temperature -= boltz_factor - boltz_temperature = max(boltz_temperature, boltz_min) - pop.setParams(boltzTemperature=boltz_temperature) + boltz_e = [] + avg = 0.0 - boltz_e = [] - avg = 0.0 + for i in range(len(pop)): + val = math.exp(pop[i].score / boltz_temperature) + boltz_e.append(val) + avg += val - for i in xrange(len(pop)): - val = math.exp(pop[i].score / boltz_temperature) - boltz_e.append(val) - avg += val + avg /= len(pop) - avg /= len(pop) + for i in range(len(pop)): + pop[i].fitness = boltz_e[i] / avg - for i in xrange(len(pop)): - pop[i].fitness = boltz_e[i] / avg def ExponentialScaling(pop): - """ Exponential Scaling Scheme. The fitness will be the same as (e^score). + """ Exponential Scaling Scheme. The fitness will be the same as (e^score). + + .. versionadded: 0.6 + The `ExponentialScaling` function. + """ + for i in range(len(pop)): + score = pop[i].score + pop[i].fitness = math.exp(score) - .. versionadded: 0.6 - The `ExponentialScaling` function. - """ - for i in xrange(len(pop)): - score = pop[i].score - pop[i].fitness = math.exp(score) def SaturatedScaling(pop): - """ Saturated Scaling Scheme. The fitness will be the same as 1.0-(e^score) - - .. versionadded: 0.6 - The `SaturatedScaling` function. - """ - for i in xrange(len(pop)): - score = pop[i].score - pop[i].fitness = 1.0 - math.exp(score) + """ Saturated Scaling Scheme. The fitness will be the same as 1.0-(e^score) + + .. versionadded: 0.6 + The `SaturatedScaling` function. + """ + for i in range(len(pop)): + score = pop[i].score + pop[i].fitness = 1.0 - math.exp(score) diff --git a/pyevolve/Selectors.py b/pyevolve/Selectors.py index 9ee11f1..4e62dc6 100644 --- a/pyevolve/Selectors.py +++ b/pyevolve/Selectors.py @@ -6,174 +6,186 @@ This module have the *selection methods*, like roulette wheel, tournament, ranking, etc. """ +from future.builtins import range import random -import Consts + def GRankSelector(population, **args): - """ The Rank Selector - This selector will pick the best individual of - the population every time. - """ - count = 0 - - if args["popID"] != GRankSelector.cachePopID: - if population.sortType == Consts.sortType["scaled"]: - best_fitness = population.bestFitness().fitness - for index in xrange(1, len(population.internalPop)): - if population[index].fitness == best_fitness: - count += 1 - else: - best_raw = population.bestRaw().score - for index in xrange(1, len(population.internalPop)): - if population[index].score == best_raw: - count += 1 - - GRankSelector.cachePopID = args["popID"] - GRankSelector.cacheCount = count - - else: - count = GRankSelector.cacheCount - - return population[random.randint(0, count)] + """ The Rank Selector - This selector will pick the best individual of + the population every time. + """ + from . import Consts + count = 0 + + if args["popID"] != GRankSelector.cachePopID: + if population.sortType == Consts.sortType["scaled"]: + best_fitness = population.bestFitness().fitness + for index in range(1, len(population.internalPop)): + if population[index].fitness == best_fitness: + count += 1 + else: + best_raw = population.bestRaw().score + for index in range(1, len(population.internalPop)): + if population[index].score == best_raw: + count += 1 + + GRankSelector.cachePopID = args["popID"] + GRankSelector.cacheCount = count + + else: + count = GRankSelector.cacheCount + + return population[random.randint(0, count)] + GRankSelector.cachePopID = None GRankSelector.cacheCount = None + def GUniformSelector(population, **args): - """ The Uniform Selector """ - return population[random.randint(0, len(population) - 1)] + """ The Uniform Selector """ + return population[random.randint(0, len(population) - 1)] + def GTournamentSelector(population, **args): - """ The Tournament Selector + """ The Tournament Selector + + It accepts the *tournamentPool* population parameter. - It accepts the *tournamentPool* population parameter. + .. note:: + the Tournament Selector uses the Roulette Wheel to + pick individuals for the pool - .. note:: - the Tournament Selector uses the Roulette Wheel to - pick individuals for the pool + .. versionchanged:: 0.6 + Changed the parameter `poolSize` to the `tournamentPool`, now the selector + gets the pool size from the population. - .. versionchanged:: 0.6 - Changed the parameter `poolSize` to the `tournamentPool`, now the selector - gets the pool size from the population. + """ + from . import Consts + choosen = None + should_minimize = population.minimax == Consts.minimaxType["minimize"] + minimax_operator = min if should_minimize else max - """ - choosen = None - should_minimize = population.minimax == Consts.minimaxType["minimize"] - minimax_operator = min if should_minimize else max + poolSize = population.getParam("tournamentPool", Consts.CDefTournamentPoolSize) + tournament_pool = [GRouletteWheel(population, **args) for i in range(poolSize)] - poolSize = population.getParam("tournamentPool", Consts.CDefTournamentPoolSize) - tournament_pool = [GRouletteWheel(population, **args) for i in xrange(poolSize)] + if population.sortType == Consts.sortType["scaled"]: + choosen = minimax_operator(tournament_pool, key=lambda ind: ind.fitness) + else: + choosen = minimax_operator(tournament_pool, key=lambda ind: ind.score) - if population.sortType == Consts.sortType["scaled"]: - choosen = minimax_operator(tournament_pool, key=lambda ind: ind.fitness) - else: - choosen = minimax_operator(tournament_pool, key=lambda ind: ind.score) + return choosen - return choosen def GTournamentSelectorAlternative(population, **args): - """ The alternative Tournament Selector + """ The alternative Tournament Selector - This Tournament Selector don't uses the Roulette Wheel + This Tournament Selector don't uses the Roulette Wheel - It accepts the *tournamentPool* population parameter. + It accepts the *tournamentPool* population parameter. - .. versionadded: 0.6 - Added the GTournamentAlternative function. + .. versionadded: 0.6 + Added the GTournamentAlternative function. - """ - pool_size = population.getParam("tournamentPool", Consts.CDefTournamentPoolSize) - len_pop = len(population) - should_minimize = population.minimax == Consts.minimaxType["minimize"] - minimax_operator = min if should_minimize else max - tournament_pool = [population[random.randint(0, len_pop - 1)] for i in xrange(pool_size)] + """ + from . import Consts + pool_size = population.getParam("tournamentPool", Consts.CDefTournamentPoolSize) + len_pop = len(population) + should_minimize = population.minimax == Consts.minimaxType["minimize"] + minimax_operator = min if should_minimize else max + tournament_pool = [population[random.randint(0, len_pop - 1)] for i in range(pool_size)] - if population.sortType == Consts.sortType["scaled"]: - choosen = minimax_operator(tournament_pool, key=lambda ind: ind.fitness) - else: - choosen = minimax_operator(tournament_pool, key=lambda ind: ind.score) + if population.sortType == Consts.sortType["scaled"]: + choosen = minimax_operator(tournament_pool, key=lambda ind: ind.fitness) + else: + choosen = minimax_operator(tournament_pool, key=lambda ind: ind.score) + + return choosen - return choosen def GRouletteWheel(population, **args): - """ The Roulette Wheel selector """ - psum = None - if args["popID"] != GRouletteWheel.cachePopID: - GRouletteWheel.cachePopID = args["popID"] - psum = GRouletteWheel_PrepareWheel(population) - GRouletteWheel.cacheWheel = psum - else: - psum = GRouletteWheel.cacheWheel - - cutoff = random.random() - lower = 0 - upper = len(population) - 1 - while(upper >= lower): - i = lower + ((upper - lower) / 2) - if psum[i] > cutoff: - upper = i - 1 - else: - lower = i + 1 - - lower = min(len(population) - 1, lower) - lower = max(0, lower) - - return population.bestFitness(lower) + """ The Roulette Wheel selector """ + psum = None + if args["popID"] != GRouletteWheel.cachePopID: + GRouletteWheel.cachePopID = args["popID"] + psum = GRouletteWheel_PrepareWheel(population) + GRouletteWheel.cacheWheel = psum + else: + psum = GRouletteWheel.cacheWheel + + cutoff = random.random() + lower = 0 + upper = len(population) - 1 + while(upper >= lower): + i = lower + ((upper - lower) // 2) + if psum[i] > cutoff: + upper = i - 1 + else: + lower = i + 1 + + lower = min(len(population) - 1, lower) + lower = max(0, lower) + + return population.bestFitness(lower) + GRouletteWheel.cachePopID = None GRouletteWheel.cacheWheel = None + def GRouletteWheel_PrepareWheel(population): - """ A preparation for Roulette Wheel selection """ - - len_pop = len(population) - - psum = [i for i in xrange(len_pop)] - - population.statistics() - - if population.sortType == Consts.sortType["scaled"]: - pop_fitMax = population.stats["fitMax"] - pop_fitMin = population.stats["fitMin"] - - if pop_fitMax == pop_fitMin: - for index in xrange(len_pop): - psum[index] = (index + 1) / float(len_pop) - elif (pop_fitMax > 0 and pop_fitMin >= 0) or (pop_fitMax <= 0 and pop_fitMin < 0): - population.sort() - if population.minimax == Consts.minimaxType["maximize"]: - psum[0] = population[0].fitness - for i in xrange(1, len_pop): - psum[i] = population[i].fitness + psum[i - 1] - for i in xrange(len_pop): - psum[i] /= float(psum[len_pop - 1]) - else: - psum[0] = -population[0].fitness + pop_fitMax + pop_fitMin - for i in xrange(1, len_pop): - psum[i] = -population[i].fitness + pop_fitMax + pop_fitMin + psum[i - 1] - for i in xrange(len_pop): - psum[i] /= float(psum[len_pop - 1]) - else: - pop_rawMax = population.stats["rawMax"] - pop_rawMin = population.stats["rawMin"] - - if pop_rawMax == pop_rawMin: - for index in xrange(len_pop): - psum[index] = (index + 1) / float(len_pop) - - elif (pop_rawMax > 0 and pop_rawMin >= 0) or (pop_rawMax <= 0 and pop_rawMin < 0): - population.sort() - if population.minimax == Consts.minimaxType["maximize"]: - psum[0] = population[0].score - for i in xrange(1, len_pop): - psum[i] = population[i].score + psum[i - 1] - for i in xrange(len_pop): - psum[i] /= float(psum[len_pop - 1]) - else: - psum[0] = - population[0].score + pop_rawMax + pop_rawMin - for i in xrange(1, len_pop): - psum[i] = - population[i].score + pop_rawMax + pop_rawMin + psum[i - 1] - for i in xrange(len_pop): - psum[i] /= float(psum[len_pop - 1]) - - return psum + """ A preparation for Roulette Wheel selection """ + from . import Consts + + len_pop = len(population) + + psum = [i for i in range(len_pop)] + + population.statistics() + + if population.sortType == Consts.sortType["scaled"]: + pop_fitMax = population.stats["fitMax"] + pop_fitMin = population.stats["fitMin"] + + if pop_fitMax == pop_fitMin: + for index in range(len_pop): + psum[index] = (index + 1) / float(len_pop) + elif (pop_fitMax > 0 and pop_fitMin >= 0) or (pop_fitMax <= 0 and pop_fitMin < 0): + population.sort() + if population.minimax == Consts.minimaxType["maximize"]: + psum[0] = population[0].fitness + for i in range(1, len_pop): + psum[i] = population[i].fitness + psum[i - 1] + for i in range(len_pop): + psum[i] /= float(psum[len_pop - 1]) + else: + psum[0] = -population[0].fitness + pop_fitMax + pop_fitMin + for i in range(1, len_pop): + psum[i] = -population[i].fitness + pop_fitMax + pop_fitMin + psum[i - 1] + for i in range(len_pop): + psum[i] /= float(psum[len_pop - 1]) + else: + pop_rawMax = population.stats["rawMax"] + pop_rawMin = population.stats["rawMin"] + + if pop_rawMax == pop_rawMin: + for index in range(len_pop): + psum[index] = (index + 1) / float(len_pop) + + elif (pop_rawMax > 0 and pop_rawMin >= 0) or (pop_rawMax <= 0 and pop_rawMin < 0): + population.sort() + if population.minimax == Consts.minimaxType["maximize"]: + psum[0] = population[0].score + for i in range(1, len_pop): + psum[i] = population[i].score + psum[i - 1] + for i in range(len_pop): + psum[i] /= float(psum[len_pop - 1]) + else: + psum[0] = - population[0].score + pop_rawMax + pop_rawMin + for i in range(1, len_pop): + psum[i] = - population[i].score + pop_rawMax + pop_rawMin + psum[i - 1] + for i in range(len_pop): + psum[i] /= float(psum[len_pop - 1]) + + return psum diff --git a/pyevolve/Statistics.py b/pyevolve/Statistics.py index a6f1188..abb01a0 100644 --- a/pyevolve/Statistics.py +++ b/pyevolve/Statistics.py @@ -73,7 +73,7 @@ def __len__(self): def __repr__(self): """ Return a string representation of the statistics """ strBuff = "- Statistics\n" - for k, v in self.internalDict.items(): + for k, v in list(self.internalDict.items()): strBuff += "\t%-45s = %.2f\n" % (self.descriptions.get(k, k), v) return strBuff @@ -83,12 +83,12 @@ def asTuple(self): def clear(self): """ Set all statistics to zero """ - for k in self.internalDict.keys(): + for k in list(self.internalDict.keys()): self.internalDict[k] = 0 def items(self): """ Return a tuple (name, value) for all stored statistics """ - return self.internalDict.items() + return list(self.internalDict.items()) def clone(self): """ Instantiate a new Statistic class with the same contents """ diff --git a/pyevolve/Util.py b/pyevolve/Util.py index 4674797..0a08ffe 100644 --- a/pyevolve/Util.py +++ b/pyevolve/Util.py @@ -7,11 +7,11 @@ use, like list item swap, random utilities and etc. """ +from future.builtins import range from random import random as rand_random from math import sqrt as math_sqrt import logging -import Consts def randomFlipCoin(p): @@ -88,7 +88,7 @@ def raiseException(message, expt=None): if expt is None: raise Exception(message) else: - raise (expt, message) + raise expt(message) def cmp_individual_raw(a, b): @@ -145,6 +145,8 @@ def importSpecial(name): .. versionadded:: 0.6 The *import_special* function """ + from . import Consts + try: imp_mod = __import__(name) except ImportError: @@ -274,7 +276,7 @@ def getNodes(self): :rtype: the list of nodes """ - return self.adjacent.keys() + return list(self.adjacent.keys()) def reset(self): """ Deletes all nodes of the graph """ @@ -285,11 +287,11 @@ def getNeighbors(self, node): :param node: the node """ - return self.adjacent[node].keys() + return list(self.adjacent[node].keys()) def __getitem__(self, node): """ Returns the adjacent nodes of the node """ - return self.adjacent[node].keys() + return list(self.adjacent[node].keys()) def __repr__(self): ret = "- Graph\n" @@ -319,7 +321,7 @@ def G1DListGetEdges(individual): """ edg = {} ind_list = individual.getInternalList() - for i in xrange(len(ind_list)): + for i in range(len(ind_list)): a, b = ind_list[i], ind_list[i - 1] if a not in edg: @@ -342,7 +344,7 @@ def G1DListMergeEdges(eda, edb): :rtype: the merged dictionary """ edges = {} - for value, near in eda.items(): + for value, near in list(eda.items()): for adj in near: if (value in edb) and (adj in edb[value]): edges.setdefault(value, []).append(adj) diff --git a/pyevolve/__init__.py b/pyevolve/__init__.py index 73961fa..d4be7fd 100644 --- a/pyevolve/__init__.py +++ b/pyevolve/__init__.py @@ -19,27 +19,28 @@ __version__ = '0.6' __author__ = 'Christian S. Perone' -import pyevolve.Consts +from . import Consts import sys if sys.version_info[:2] < Consts.CDefPythonRequire: - raise Exception("Python 2.5+ required, the version %s was found on your system !" % (sys.version_info[:2],)) + raise Exception("Python 2.5+ required, the version %s was found on your system !" % (sys.version_info[:2],)) del sys + def logEnable(filename=Consts.CDefLogFile, level=Consts.CDefLogLevel): - """ Enable the log system for pyevolve + """ Enable the log system for pyevolve - :param filename: the log filename - :param level: the debugging level + :param filename: the log filename + :param level: the debugging level - Example: - >>> pyevolve.logEnable() + Example: + >>> pyevolve.logEnable() - """ - import logging - logging.basicConfig(level=level, - format='%(asctime)s [%(module)s:%(funcName)s:%(lineno)d] %(levelname)s %(message)s', - filename=filename, - filemode='w') - logging.info("Pyevolve v.%s, the log was enabled by user.", __version__) + """ + import logging + logging.basicConfig(level=level, + format='%(asctime)s [%(module)s:%(funcName)s:%(lineno)d] %(levelname)s %(message)s', + filename=filename, + filemode='w') + logging.info("Pyevolve v.%s, the log was enabled by user.", __version__) diff --git a/pyevolve_graph.py b/pyevolve_graph.py index 7956331..eb20aa2 100755 --- a/pyevolve_graph.py +++ b/pyevolve_graph.py @@ -5,603 +5,641 @@ from optparse import OptionParser from optparse import OptionGroup + def graph_pop_heatmap_raw(pop, minimize, colormap="jet", filesave=None): - pylab.imshow(pop, aspect="auto", interpolation="gaussian", cmap=matplotlib.cm.__dict__[colormap]) - pylab.title("Plot of pop. raw scores along the generations") - pylab.xlabel('Population') - pylab.ylabel('Generations') - pylab.grid(True) - pylab.colorbar() - - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + pylab.imshow(pop, aspect="auto", interpolation="gaussian", cmap=matplotlib.cm.__dict__[colormap]) + pylab.title("Plot of pop. raw scores along the generations") + pylab.xlabel('Population') + pylab.ylabel('Generations') + pylab.grid(True) + pylab.colorbar() + + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() + def graph_pop_heatmap_fitness(pop, minimize, colormap="jet", filesave=None): - pylab.imshow(pop, aspect="equal", interpolation="gaussian", cmap=matplotlib.cm.__dict__[colormap]) - pylab.title("Plot of pop. fitness scores along the generations") - pylab.xlabel('Population') - pylab.ylabel('Generations') - pylab.grid(True) - pylab.colorbar() + pylab.imshow(pop, aspect="equal", interpolation="gaussian", cmap=matplotlib.cm.__dict__[colormap]) + pylab.title("Plot of pop. fitness scores along the generations") + pylab.xlabel('Population') + pylab.ylabel('Generations') + pylab.grid(True) + pylab.colorbar() - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() def graph_diff_raw(pop, minimize, filesave=None): - x = [] + x = [] - diff_raw_y = [] - diff_fit_y = [] + diff_raw_y = [] + diff_fit_y = [] - for it in pop: - x.append(it["generation"]) - diff_raw_y.append(it["rawMax"] - it["rawMin"]) - diff_fit_y.append(it["fitMax"] - it["fitMin"]) + for it in pop: + x.append(it["generation"]) + diff_raw_y.append(it["rawMax"] - it["rawMin"]) + diff_fit_y.append(it["fitMax"] - it["fitMin"]) - pylab.figure() - pylab.subplot(211) - - pylab.plot(x, diff_raw_y, "g", label="Raw difference", linewidth=1.2) - pylab.fill_between(x, diff_raw_y, color="g", alpha=0.1) + pylab.figure() + pylab.subplot(211) - diff_raw_max= max(diff_raw_y) - gen_max_raw = x[diff_raw_y.index(diff_raw_max)] + pylab.plot(x, diff_raw_y, "g", label="Raw difference", linewidth=1.2) + pylab.fill_between(x, diff_raw_y, color="g", alpha=0.1) - pylab.annotate("Maximum (%.2f)" % (diff_raw_max,), xy=(gen_max_raw, diff_raw_max), xycoords='data', - xytext=(-150, -20), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc"), - ) + diff_raw_max = max(diff_raw_y) + gen_max_raw = x[diff_raw_y.index(diff_raw_max)] - pylab.xlabel("Generation (#)") - pylab.ylabel("Raw difference") - pylab.title("Plot of evolution identified by '%s'" % (options.identify)) + pylab.annotate("Maximum (%.2f)" % (diff_raw_max,), xy=(gen_max_raw, diff_raw_max), xycoords='data', + xytext=(-150, -20), textcoords='offset points', + arrowprops=dict(arrowstyle="->", connectionstyle="arc")) - pylab.grid(True) - pylab.legend(prop=FontProperties(size="smaller")) + pylab.xlabel("Generation (#)") + pylab.ylabel("Raw difference") + pylab.title("Plot of evolution identified by '%s'" % (options.identify)) - pylab.subplot(212) + pylab.grid(True) + pylab.legend(prop=FontProperties(size="smaller")) - pylab.plot(x, diff_fit_y, "b", label="Fitness difference", linewidth=1.2) - pylab.fill_between(x, diff_fit_y, color="b", alpha=0.1) + pylab.subplot(212) + pylab.plot(x, diff_fit_y, "b", label="Fitness difference", linewidth=1.2) + pylab.fill_between(x, diff_fit_y, color="b", alpha=0.1) - diff_fit_max= max(diff_fit_y) - gen_max_fit = x[diff_fit_y.index(diff_fit_max)] + diff_fit_max = max(diff_fit_y) + gen_max_fit = x[diff_fit_y.index(diff_fit_max)] - pylab.annotate("Maximum (%.2f)" % (diff_fit_max,), xy=(gen_max_fit, diff_fit_max), xycoords='data', - xytext=(-150, -20), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc"), - ) + pylab.annotate("Maximum (%.2f)" % (diff_fit_max,), xy=(gen_max_fit, diff_fit_max), xycoords='data', + xytext=(-150, -20), textcoords='offset points', + arrowprops=dict(arrowstyle="->", connectionstyle="arc")) - pylab.xlabel("Generation (#)") - pylab.ylabel("Fitness difference") + pylab.xlabel("Generation (#)") + pylab.ylabel("Fitness difference") - pylab.grid(True) - pylab.legend(prop=FontProperties(size="smaller")) + pylab.grid(True) + pylab.legend(prop=FontProperties(size="smaller")) - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() -def graph_maxmin_raw(pop, minimize, filesave=None): - x = [] - max_y = [] - min_y = [] - std_dev_y = [] - avg_y = [] - for it in pop: - x.append(it["generation"]) - max_y.append(it["rawMax"]) - min_y.append(it["rawMin"]) - std_dev_y.append(it["rawDev"]) - avg_y.append(it["rawAve"]) +def graph_maxmin_raw(pop, minimize, filesave=None): + x = [] + max_y = [] + min_y = [] + std_dev_y = [] + avg_y = [] - pylab.figure() + for it in pop: + x.append(it["generation"]) + max_y.append(it["rawMax"]) + min_y.append(it["rawMin"]) + std_dev_y.append(it["rawDev"]) + avg_y.append(it["rawAve"]) - pylab.plot(x, max_y, "g", label="Max raw", linewidth=1.2) - pylab.plot(x, min_y, "r", label="Min raw", linewidth=1.2) - pylab.plot(x, avg_y, "b", label="Avg raw", linewidth=1.2) - pylab.plot(x, std_dev_y, "k", label="Std Dev raw", linewidth=1.2) + pylab.figure() - pylab.fill_between(x, min_y, max_y, color="g", alpha=0.1, label="Diff max/min") + pylab.plot(x, max_y, "g", label="Max raw", linewidth=1.2) + pylab.plot(x, min_y, "r", label="Min raw", linewidth=1.2) + pylab.plot(x, avg_y, "b", label="Avg raw", linewidth=1.2) + pylab.plot(x, std_dev_y, "k", label="Std Dev raw", linewidth=1.2) - if minimize: raw_max = min(min_y) - else: raw_max= max(max_y) + pylab.fill_between(x, min_y, max_y, color="g", alpha=0.1, label="Diff max/min") - if minimize: gen_max = x[min_y.index(raw_max)] - else: gen_max = x[max_y.index(raw_max)] + if minimize: + raw_max = min(min_y) + else: + raw_max = max(max_y) - min_std = min(std_dev_y) - gen_min_std = x[std_dev_y.index(min_std)] + if minimize: + gen_max = x[min_y.index(raw_max)] + else: + gen_max = x[max_y.index(raw_max)] - max_std = max(std_dev_y) - gen_max_std = x[std_dev_y.index(max_std)] + min_std = min(std_dev_y) + gen_min_std = x[std_dev_y.index(min_std)] - if minimize: annot_label = "Minimum (%.2f)" % (raw_max,) - else: annot_label = "Maximum (%.2f)" % (raw_max,) + max_std = max(std_dev_y) + gen_max_std = x[std_dev_y.index(max_std)] + if minimize: + annot_label = "Minimum (%.2f)" % (raw_max,) + else: + annot_label = "Maximum (%.2f)" % (raw_max,) - pylab.annotate(annot_label, xy=(gen_max, raw_max), xycoords='data', - xytext=(8, 15), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc"), - ) + pylab.annotate(annot_label, xy=(gen_max, raw_max), xycoords='data', + xytext=(8, 15), textcoords='offset points', + arrowprops=dict(arrowstyle="->", connectionstyle="arc")) - pylab.annotate("Min StdDev (%.2f)" % (min_std,), xy=(gen_min_std, min_std), xycoords='data', - xytext=(8, 15), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc"), - ) + pylab.annotate("Min StdDev (%.2f)" % (min_std,), xy=(gen_min_std, min_std), xycoords='data', + xytext=(8, 15), textcoords='offset points', + arrowprops=dict(arrowstyle="->", connectionstyle="arc")) - pylab.annotate("Max StdDev (%.2f)" % (max_std,), xy=(gen_max_std, max_std), xycoords='data', - xytext=(8, 15), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc"), - ) + pylab.annotate("Max StdDev (%.2f)" % (max_std,), xy=(gen_max_std, max_std), xycoords='data', + xytext=(8, 15), textcoords='offset points', + arrowprops=dict(arrowstyle="->", connectionstyle="arc")) - pylab.xlabel("Generation (#)") - pylab.ylabel("Raw score") - pylab.title("Plot of evolution identified by '%s' (raw scores)" % (options.identify)) + pylab.xlabel("Generation (#)") + pylab.ylabel("Raw score") + pylab.title("Plot of evolution identified by '%s' (raw scores)" % (options.identify)) - pylab.grid(True) - pylab.legend(prop=FontProperties(size="smaller")) + pylab.grid(True) + pylab.legend(prop=FontProperties(size="smaller")) - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() def graph_maxmin_fitness(pop, minimize, filesave=None): - x = [] - max_y = [] - min_y = [] - avg_y = [] - - for it in pop: - x.append(it["generation"]) - max_y.append(it["fitMax"]) - min_y.append(it["fitMin"]) - avg_y.append(it["fitAve"]) - - pylab.figure() - pylab.plot(x, max_y, "g", label="Max fitness") - pylab.plot(x, min_y, "r", label="Min fitness") - pylab.plot(x, avg_y, "b", label="Avg fitness") - - pylab.fill_between(x, min_y, max_y, color="g", alpha=0.1, label="Diff max/min") - - if minimize: raw_max = min(min_y) - else: raw_max = max(max_y) - - if minimize: gen_max = x[min_y.index(raw_max)] - else: gen_max = x[max_y.index(raw_max)] - - if minimize: annot_label = "Minimum (%.2f)" % (raw_max,) - else: annot_label = "Maximum (%.2f)" % (raw_max,) - - pylab.annotate(annot_label, xy=(gen_max, raw_max), xycoords='data', - xytext=(8, 15), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc"), - ) - - pylab.xlabel("Generation (#)") - pylab.ylabel("Fitness score") - pylab.title("Plot of evolution identified by '%s' (fitness scores)" % (options.identify)) - pylab.grid(True) - pylab.legend(prop=FontProperties(size="smaller")) - - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + x = [] + max_y = [] + min_y = [] + avg_y = [] + + for it in pop: + x.append(it["generation"]) + max_y.append(it["fitMax"]) + min_y.append(it["fitMin"]) + avg_y.append(it["fitAve"]) + + pylab.figure() + pylab.plot(x, max_y, "g", label="Max fitness") + pylab.plot(x, min_y, "r", label="Min fitness") + pylab.plot(x, avg_y, "b", label="Avg fitness") + + pylab.fill_between(x, min_y, max_y, color="g", alpha=0.1, label="Diff max/min") + + if minimize: + raw_max = min(min_y) + else: + raw_max = max(max_y) + + if minimize: + gen_max = x[min_y.index(raw_max)] + else: + gen_max = x[max_y.index(raw_max)] + + if minimize: + annot_label = "Minimum (%.2f)" % (raw_max,) + else: + annot_label = "Maximum (%.2f)" % (raw_max,) + + pylab.annotate(annot_label, xy=(gen_max, raw_max), xycoords='data', + xytext=(8, 15), textcoords='offset points', + arrowprops=dict(arrowstyle="->", connectionstyle="arc")) + + pylab.xlabel("Generation (#)") + pylab.ylabel("Fitness score") + pylab.title("Plot of evolution identified by '%s' (fitness scores)" % (options.identify)) + pylab.grid(True) + pylab.legend(prop=FontProperties(size="smaller")) + + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() -def graph_errorbars_raw(pop, minimize, filesave=None): - x = [] - y = [] - yerr_max = [] - yerr_min = [] - - for it in pop: - x.append(it["generation"]) - y.append(it["rawAve"]) - ymax = it["rawMax"] - it["rawAve"] - ymin = it["rawAve"] - it["rawMin"] - - yerr_max.append(ymax) - yerr_min.append(ymin) - - pylab.figure() - pylab.errorbar(x, y, [yerr_min, yerr_max], ecolor="g") - pylab.xlabel('Generation (#)') - pylab.ylabel('Raw score Min/Avg/Max') - pylab.title("Plot of evolution identified by '%s' (raw scores)" % (options.identify)) - pylab.grid(True) - - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() -def graph_errorbars_fitness(pop, minimize, filesave=None): - x = [] - y = [] - yerr_max = [] - yerr_min = [] - - for it in pop: - x.append(it["generation"]) - y.append(it["fitAve"]) - ymax = it["fitMax"] - it["fitAve"] - ymin = it["fitAve"] - it["fitMin"] - - yerr_max.append(ymax) - yerr_min.append(ymin) - - pylab.figure() - pylab.errorbar(x, y, [yerr_min, yerr_max], ecolor="g") - pylab.xlabel('Generation (#)') - pylab.ylabel('Fitness score Min/Avg/Max') - pylab.title("Plot of evolution identified by '%s' (fitness scores)" % (options.identify)) - - pylab.grid(True) - - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() - -def graph_compare_raw(pop, minimize, id_list, filesave=None): - colors_list = ["g", "b", "r", "k", "m", "y"] - index = 0 - - pylab.figure() - - for it_out in pop: - x = [] - max_y = [] - min_y = [] +def graph_errorbars_raw(pop, minimize, filesave=None): + x = [] + y = [] + yerr_max = [] + yerr_min = [] + + for it in pop: + x.append(it["generation"]) + y.append(it["rawAve"]) + ymax = it["rawMax"] - it["rawAve"] + ymin = it["rawAve"] - it["rawMin"] + + yerr_max.append(ymax) + yerr_min.append(ymin) + + pylab.figure() + pylab.errorbar(x, y, [yerr_min, yerr_max], ecolor="g") + pylab.xlabel('Generation (#)') + pylab.ylabel('Raw score Min/Avg/Max') + pylab.title("Plot of evolution identified by '%s' (raw scores)" % (options.identify)) + pylab.grid(True) + + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() - for it in it_out: - x.append(it["generation"]) - max_y.append(it["rawMax"]) - min_y.append(it["rawMin"]) +def graph_errorbars_fitness(pop, minimize, filesave=None): + x = [] + y = [] + yerr_max = [] + yerr_min = [] - if minimize: - pylab.plot(x, max_y, colors_list[index], linewidth=0.05) - pylab.plot(x, min_y, colors_list[index], label="Raw min (%s)" % (id_list[index],), linewidth=1.3) - else: - pylab.plot(x, max_y, colors_list[index], label="Raw max (%s)" % (id_list[index],), linewidth=1.3) - pylab.plot(x, min_y, colors_list[index], linewidth=0.05) + for it in pop: + x.append(it["generation"]) + y.append(it["fitAve"]) + ymax = it["fitMax"] - it["fitAve"] + ymin = it["fitAve"] - it["fitMin"] - pylab.fill_between(x, min_y, max_y, color=colors_list[index], alpha=0.06,) + yerr_max.append(ymax) + yerr_min.append(ymin) - index += 1 + pylab.figure() + pylab.errorbar(x, y, [yerr_min, yerr_max], ecolor="g") + pylab.xlabel('Generation (#)') + pylab.ylabel('Fitness score Min/Avg/Max') + pylab.title("Plot of evolution identified by '%s' (fitness scores)" % (options.identify)) - pylab.xlabel("Generation (#)") - pylab.ylabel("Raw score") - pylab.title("Plot of evolution identified by '%s' (raw scores)" % ('many',)) - pylab.grid(True) - pylab.legend(prop=FontProperties(size="smaller")) + pylab.grid(True) - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() -def graph_compare_fitness(pop, minimize, id_list, filesave=None): - colors_list = ["g", "b", "r", "k", "m", "y"] - index = 0 - pylab.figure() - - for it_out in pop: - x = [] - max_y = [] - min_y = [] +def graph_compare_raw(pop, minimize, id_list, filesave=None): + colors_list = ["g", "b", "r", "k", "m", "y"] + index = 0 - for it in it_out: - x.append(it["generation"]) - max_y.append(it["fitMax"]) - min_y.append(it["fitMin"]) + pylab.figure() - if minimize: - pylab.plot(x, max_y, colors_list[index], linewidth=0.05) - pylab.plot(x, min_y, colors_list[index], label="Fitness min (%s)" % (id_list[index],), linewidth=1.3) - else: - pylab.plot(x, max_y, colors_list[index], label="Fitness max (%s)" % (id_list[index],), linewidth=1.3) - pylab.plot(x, min_y, colors_list[index], linewidth=0.05) + for it_out in pop: + x = [] + max_y = [] + min_y = [] - pylab.fill_between(x, min_y, max_y, color=colors_list[index], alpha=0.06,) + for it in it_out: + x.append(it["generation"]) + max_y.append(it["rawMax"]) + min_y.append(it["rawMin"]) - index += 1 + if minimize: + pylab.plot(x, max_y, colors_list[index], linewidth=0.05) + pylab.plot(x, min_y, colors_list[index], label="Raw min (%s)" % (id_list[index],), linewidth=1.3) + else: + pylab.plot(x, max_y, colors_list[index], label="Raw max (%s)" % (id_list[index],), linewidth=1.3) + pylab.plot(x, min_y, colors_list[index], linewidth=0.05) - pylab.xlabel("Generation (#)") - pylab.ylabel("Fitness score") - pylab.title("Plot of evolution identified by '%s' (fitness scores)" % ('many',)) - pylab.grid(True) - pylab.legend(prop=FontProperties(size="smaller")) + pylab.fill_between(x, min_y, max_y, color=colors_list[index], alpha=0.06,) - if filesave: - pylab.savefig(filesave) - print "Graph saved to %s file !" % (filesave,) - else: - pylab.show() + index += 1 + pylab.xlabel("Generation (#)") + pylab.ylabel("Raw score") + pylab.title("Plot of evolution identified by '%s' (raw scores)" % ('many',)) + pylab.grid(True) + pylab.legend(prop=FontProperties(size="smaller")) -if __name__ == "__main__": - from pyevolve import __version__ as pyevolve_version - from pyevolve import __author__ as pyevolve_author + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() - popGraph = False - print "Pyevolve %s - Graph Plot Tool" % (pyevolve_version,) - print "By %s\n" % (pyevolve_author,) - parser = OptionParser() +def graph_compare_fitness(pop, minimize, id_list, filesave=None): + colors_list = ["g", "b", "r", "k", "m", "y"] + index = 0 - parser.add_option("-f", "--file", dest="dbfile", - help="Database file to read (default is 'pyevolve.db').", metavar="FILENAME", default="pyevolve.db") + pylab.figure() - parser.add_option("-i", "--identify", dest="identify", - help="The identify of evolution.", metavar="IDENTIFY") + for it_out in pop: + x = [] + max_y = [] + min_y = [] - parser.add_option("-o", "--outfile", dest="outfile", - help="""Write the graph image to a file (don't use extension, just the filename, default is png format, but you can change using --extension (-e) parameter).""", - metavar="OUTFILE") + for it in it_out: + x.append(it["generation"]) + max_y.append(it["fitMax"]) + min_y.append(it["fitMin"]) - parser.add_option("-e", "--extension", dest="extension", - help="""Graph image file format. Supported options (formats) are: emf, eps, pdf, png, ps, raw, rgba, svg, svgz. Default is 'png'.""", - metavar="EXTENSION", default="png") + if minimize: + pylab.plot(x, max_y, colors_list[index], linewidth=0.05) + pylab.plot(x, min_y, colors_list[index], label="Fitness min (%s)" % (id_list[index],), linewidth=1.3) + else: + pylab.plot(x, max_y, colors_list[index], label="Fitness max (%s)" % (id_list[index],), linewidth=1.3) + pylab.plot(x, min_y, colors_list[index], linewidth=0.05) - parser.add_option("-g", "--genrange", dest="genrange", - help="""This is the generation range of the graph, ex: 1:30 (interval between 1 and 30).""", - metavar="GENRANGE") - - parser.add_option("-l", "--lindrange", dest="lindrange", - help="""This is the individual range of the graph, ex: 1:30 (individuals between 1 and 30), only applies to heatmaps.""", - metavar="LINDRANGE") - - parser.add_option("-c", "--colormap", dest="colormap", - help="""Sets the Color Map for the graph types 8 and 9. Some options are: summer, bone, gray, hot, jet, cooper, spectral. The default is 'jet'.""", - metavar="COLORMAP", default="jet") - - parser.add_option("-m", "--minimize", action="store_true", - help="Sets the 'Minimize' mode, default is the Maximize mode. This option makes sense if you are minimizing your evaluation function.", dest="minimize") - - group = OptionGroup(parser, "Graph types", "This is the supported graph types") - - group.add_option("-0", action="store_true", help="Write all graphs to files. Graph types: 1, 2, 3, 4 and 5.", dest="all_graphs") - - group.add_option("-1", action="store_true", help="Error bars graph (raw scores).", dest="errorbars_raw") - group.add_option("-2", action="store_true", help="Error bars graph (fitness scores).", dest="errorbars_fitness") - group.add_option("-3", action="store_true", help="Max/min/avg/std. dev. graph (raw scores).", dest="maxmin_raw") - group.add_option("-4", action="store_true", help="Max/min/avg graph (fitness scores).", dest="maxmin_fitness") - group.add_option("-5", action="store_true", help="Raw and Fitness min/max difference graph.", dest="diff_raw") - - group.add_option("-6", action="store_true", help="Compare best raw score of two or more evolutions (you must specify the identify comma-separed list with --identify (-i) parameter, like 'one, two, three'), the maximum is 6 items.", dest="compare_raw") - group.add_option("-7", action="store_true", help="Compare best fitness score of two or more evolutions (you must specify the identify comma-separed list with --identify (-i) parameter, like 'one, two, three'), the maximum is 6 items.", dest="compare_fitness") - - group.add_option("-8", action="store_true", help="Show a heat map of population raw score distribution between generations.", dest="pop_heatmap_raw") - group.add_option("-9", action="store_true", help="Show a heat map of population fitness score distribution between generations.", dest="pop_heatmap_fitness") - - - parser.add_option_group(group) - - (options, args) = parser.parse_args() - - if options.identify and (not options.errorbars_raw - and not options.errorbars_fitness - and not options.maxmin_raw - and not options.maxmin_fitness - and not options.diff_raw - and not options.all_graphs - and not options.compare_raw - and not options.pop_heatmap_raw - and not options.pop_heatmap_fitness - and not options.compare_fitness): - parser.error("You must choose one graph type !") - - if (not options.identify) or (not options.dbfile): - parser.print_help() - exit() - - print "Loading modules...." - - import os.path - if not os.path.exists(options.dbfile): - print "Database file '%s' not found !" % (options.dbfile, ) - exit() - - import pylab - from matplotlib.font_manager import FontProperties - import matplotlib.cm - import sqlite3 - import math - import os - - print "Loading database and creating graph..." - - identify_list = options.identify.split(",") - identify_list = map(str.strip, identify_list) - - pop = None - - if options.pop_heatmap_raw or options.pop_heatmap_fitness: - conn = sqlite3.connect(options.dbfile) - conn.row_factory = sqlite3.Row - c = conn.cursor() - - if options.genrange: - genrange = options.genrange.split(":") - ret = c.execute("select distinct generation from population where identify = ? and generation between ? and ?", (options.identify, genrange[0], genrange[1])) - else: - ret = c.execute("select distinct generation from population where identify = ?", (options.identify,)) - - generations = ret.fetchall() - if len(generations) <= 0: - print "No generation data found for the identify '%s' !" % (options.identify,) - exit() - - pop = [] - for gen in generations: - pop_tmp = [] - - if options.lindrange: - individual_range = options.lindrange.split(":") - ret = c.execute(""" - select * from population - where identify = ? - and generation = ? - and individual between ? and ? - """, (options.identify, gen[0], individual_range[0], individual_range[1])) - else: - ret = c.execute(""" - select * from population - where identify = ? - and generation = ? - """, (options.identify, gen[0])) - - ret_fetch = ret.fetchall() - for it in ret_fetch: - if options.pop_heatmap_raw: - pop_tmp.append(it["raw"]) - else: - pop_tmp.append(it["fitness"]) - pop.append(pop_tmp) + pylab.fill_between(x, min_y, max_y, color=colors_list[index], alpha=0.06,) - ret.close() - conn.close() + index += 1 - if len(pop) <= 0: - print "No statistic data found for the identify '%s' !" % (options.identify,) - exit() + pylab.xlabel("Generation (#)") + pylab.ylabel("Fitness score") + pylab.title("Plot of evolution identified by '%s' (fitness scores)" % ('many',)) + pylab.grid(True) + pylab.legend(prop=FontProperties(size="smaller")) - print "%d generations found !" % (len(pop),) + if filesave: + pylab.savefig(filesave) + print("Graph saved to %s file !" % (filesave,)) + else: + pylab.show() - popGraph = True +if __name__ == "__main__": + from pyevolve import __version__ as pyevolve_version + from pyevolve import __author__ as pyevolve_author + + popGraph = False + + print("Pyevolve %s - Graph Plot Tool" % (pyevolve_version,)) + print("By %s\n" % (pyevolve_author,)) + parser = OptionParser() + + parser.add_option("-f", "--file", dest="dbfile", + help="Database file to read (default is 'pyevolve.db').", + metavar="FILENAME", default="pyevolve.db") + + parser.add_option("-i", "--identify", dest="identify", + help="The identify of evolution.", metavar="IDENTIFY") + + parser.add_option("-o", "--outfile", dest="outfile", + help="""Write the graph image to a file (don't use extension, + just the filename, default is png format, + but you can change using --extension (-e) parameter).""", + metavar="OUTFILE") + + parser.add_option("-e", "--extension", dest="extension", + help="""Graph image file format. Supported options (formats) are: + emf, eps, pdf, png, ps, raw, rgba, svg, svgz. Default is 'png'.""", + metavar="EXTENSION", default="png") + + parser.add_option("-g", "--genrange", dest="genrange", + help="""This is the generation range of the graph, + ex: 1:30 (interval between 1 and 30).""", + metavar="GENRANGE") + + parser.add_option("-l", "--lindrange", dest="lindrange", + help="""This is the individual range of the graph, + ex: 1:30 (individuals between 1 and 30), only applies to heatmaps.""", + metavar="LINDRANGE") + + parser.add_option("-c", "--colormap", dest="colormap", + help="""Sets the Color Map for the graph types 8 and 9. + Some options are: summer, bone, gray, hot, jet, cooper, spectral. The default is 'jet'.""", + metavar="COLORMAP", default="jet") + + parser.add_option("-m", "--minimize", action="store_true", + help="Sets the 'Minimize' mode, default is the Maximize mode. " + "This option makes sense if you are minimizing your evaluation function.", dest="minimize") + + group = OptionGroup(parser, "Graph types", "This is the supported graph types") + + group.add_option("-0", action="store_true", help="Write all graphs to files. Graph types: 1, 2, 3, 4 and 5.", + dest="all_graphs") + + group.add_option("-1", action="store_true", help="Error bars graph (raw scores).", dest="errorbars_raw") + group.add_option("-2", action="store_true", help="Error bars graph (fitness scores).", dest="errorbars_fitness") + group.add_option("-3", action="store_true", help="Max/min/avg/std. dev. graph (raw scores).", dest="maxmin_raw") + group.add_option("-4", action="store_true", help="Max/min/avg graph (fitness scores).", dest="maxmin_fitness") + group.add_option("-5", action="store_true", help="Raw and Fitness min/max difference graph.", dest="diff_raw") + + group.add_option("-6", action="store_true", + help="Compare best raw score of two or more evolutions " + "(you must specify the identify comma-separed list with --identify (-i) " + "parameter, like 'one, two, three'), the maximum is 6 items.", dest="compare_raw") + group.add_option("-7", action="store_true", + help="Compare best fitness score of two or more evolutions " + "(you must specify the identify comma-separed list with --identify (-i) parameter, " + "like 'one, two, three'), the maximum is 6 items.", dest="compare_fitness") + + group.add_option("-8", action="store_true", + help="Show a heat map of population raw score distribution between generations.", + dest="pop_heatmap_raw") + group.add_option("-9", action="store_true", + help="Show a heat map of population fitness score distribution between generations.", + dest="pop_heatmap_fitness") + + parser.add_option_group(group) + + (options, args) = parser.parse_args() + + if options.identify and (not options.errorbars_raw and not options.errorbars_fitness and not + options.maxmin_raw and not options.maxmin_fitness and not options.diff_raw and not + options.all_graphs and not options.compare_raw and not options.pop_heatmap_raw and not + options.pop_heatmap_fitness and not options.compare_fitness): + parser.error("You must choose one graph type !") + + if (not options.identify) or (not options.dbfile): + parser.print_help() + exit() + + print("Loading modules....") + + import os.path + from sys import exit + if not os.path.exists(options.dbfile): + print("Database file '%s' not found !" % (options.dbfile, )) + exit() + + import pylab + from matplotlib.font_manager import FontProperties + import matplotlib.cm + import sqlite3 + import os + + print("Loading database and creating graph...") + + identify_list = options.identify.split(",") + identify_list = list(map(str.strip, identify_list)) + + pop = None + + if options.pop_heatmap_raw or options.pop_heatmap_fitness: + conn = sqlite3.connect(options.dbfile) + conn.row_factory = sqlite3.Row + c = conn.cursor() + + if options.genrange: + genrange = options.genrange.split(":") + ret = c.execute( + "select distinct generation from population where identify = ? and generation between ? and ?", + (options.identify, genrange[0], genrange[1])) + else: + ret = c.execute("select distinct generation from population where identify = ?", (options.identify,)) + + generations = ret.fetchall() + if len(generations) <= 0: + print("No generation data found for the identify '%s' !" % (options.identify,)) + exit() + + pop = [] + for gen in generations: + pop_tmp = [] + + if options.lindrange: + individual_range = options.lindrange.split(":") + ret = c.execute(""" + select * from population + where identify = ? + and generation = ? + and individual between ? and ? + """, (options.identify, gen[0], individual_range[0], individual_range[1])) + else: + ret = c.execute(""" + select * from population + where identify = ? + and generation = ? + """, (options.identify, gen[0])) - if len(identify_list) == 1 and not popGraph: - if options.compare_raw or options.compare_fitness: - parser.error("You can't use this graph type with only one identify !") + ret_fetch = ret.fetchall() + for it in ret_fetch: + if options.pop_heatmap_raw: + pop_tmp.append(it["raw"]) + else: + pop_tmp.append(it["fitness"]) + pop.append(pop_tmp) - conn = sqlite3.connect(options.dbfile) - conn.row_factory = sqlite3.Row - c = conn.cursor() + ret.close() + conn.close() - if options.genrange: - genrange = options.genrange.split(":") - ret = c.execute("select * from statistics where identify = ? and generation between ? and ?", (options.identify, genrange[0], genrange[1])) - else: - ret = c.execute("select * from statistics where identify = ?", (options.identify,)) + if len(pop) <= 0: + print("No statistic data found for the identify '%s' !" % (options.identify,)) + exit() - pop = ret.fetchall() + print("%d generations found !" % (len(pop),)) - ret.close() - conn.close() + popGraph = True - if len(pop) <= 0: - print "No statistic data found for the identify '%s' !" % (options.identify,) - exit() + if len(identify_list) == 1 and not popGraph: + if options.compare_raw or options.compare_fitness: + parser.error("You can't use this graph type with only one identify !") - print "%d generations found !" % (len(pop),) - - elif len(identify_list) > 1 and not popGraph: - pop = [] - if (not options.compare_raw) and (not options.compare_fitness): - parser.error("You can't use many ids with this graph type !") + conn = sqlite3.connect(options.dbfile) + conn.row_factory = sqlite3.Row + c = conn.cursor() - conn = sqlite3.connect(options.dbfile) - conn.row_factory = sqlite3.Row - c = conn.cursor() - for item in identify_list: - if options.genrange: + if options.genrange: genrange = options.genrange.split(":") - ret = c.execute("select * from statistics where identify = ? and generation between ? and ?", (item, genrange[0], genrange[1])) - else: - ret = c.execute("select * from statistics where identify = ?", (item,)) - fetchall = ret.fetchall() - if len(fetchall) > 0: - pop.append(fetchall) - - ret.close() - conn.close() - - if len(pop) <= 0: - print "No statistic data found for the identify list '%s' !" % (options.identify,) - exit() - - print "%d identify found !" % (len(pop),) - - if options.errorbars_raw: - if options.outfile: graph_errorbars_raw(pop, options.minimize, options.outfile + "." + options.extension) - else: graph_errorbars_raw(pop, options.minimize) - - if options.errorbars_fitness: - if options.outfile: graph_errorbars_fitness(pop, options.minimize, options.outfile + "." + options.extension) - else: graph_errorbars_fitness(pop, options.minimize) - - if options.maxmin_raw: - if options.outfile: graph_maxmin_raw(pop, options.minimize, options.outfile + "." + options.extension) - else: graph_maxmin_raw(pop, options.minimize) - - if options.maxmin_fitness: - if options.outfile: graph_maxmin_fitness(pop, options.minimize, options.outfile + "." + options.extension) - else: graph_maxmin_fitness(pop, options.minimize) - - if options.diff_raw: - if options.outfile: graph_diff_raw(pop, options.minimize, options.outfile + "." + options.extension) - else: graph_diff_raw(pop, options.minimize) - - if options.all_graphs: - all_graph_functions = [graph_errorbars_raw, graph_errorbars_fitness, graph_maxmin_raw, - graph_maxmin_fitness, graph_diff_raw] - if options.outfile: - parser.error("You can't specify one file to all graphs !") - - dirname = "graphs_" + options.identify - if not os.path.isdir(dirname): - os.mkdir(dirname) - - for graph in all_graph_functions: - filename = dirname + "/" - filename += options.identify + "_" + graph.__name__[6:] - filename += "." + options.extension - graph(pop, options.minimize, filename) - - print "\n\tDone ! The graphs was saved in the directory '%s'" % (dirname) - - if options.compare_raw: - if options.outfile: graph_compare_raw(pop, options.minimize, identify_list, options.outfile + "." + options.extension) - else: graph_compare_raw(pop, options.minimize, identify_list ) - - if options.compare_fitness: - if options.outfile: graph_compare_fitness(pop, options.minimize, identify_list, options.outfile + "." + options.extension) - else: graph_compare_fitness(pop, options.minimize, identify_list ) - - if options.pop_heatmap_raw: - if options.outfile: graph_pop_heatmap_raw(pop, options.minimize, options.colormap, options.outfile + "." + options.extension) - else: graph_pop_heatmap_raw(pop, options.minimize, options.colormap) - - if options.pop_heatmap_fitness: - if options.outfile: graph_pop_heatmap_fitness(pop, options.minimize, options.colormap, options.outfile + "." + options.extension) - else: graph_pop_heatmap_fitness(pop, options.minimize, options.colormap) + ret = c.execute("select * from statistics where identify = ? and generation between ? and ?", + (options.identify, genrange[0], genrange[1])) + else: + ret = c.execute("select * from statistics where identify = ?", (options.identify,)) + + pop = ret.fetchall() + + ret.close() + conn.close() + + if len(pop) <= 0: + print("No statistic data found for the identify '%s' !" % (options.identify,)) + exit() + + print("%d generations found !" % (len(pop),)) + + elif len(identify_list) > 1 and not popGraph: + pop = [] + if (not options.compare_raw) and (not options.compare_fitness): + parser.error("You can't use many ids with this graph type !") + + conn = sqlite3.connect(options.dbfile) + conn.row_factory = sqlite3.Row + c = conn.cursor() + for item in identify_list: + if options.genrange: + genrange = options.genrange.split(":") + ret = c.execute("select * from statistics where identify = ? and generation between ? and ?", + (item, genrange[0], genrange[1])) + else: + ret = c.execute("select * from statistics where identify = ?", (item,)) + fetchall = ret.fetchall() + if len(fetchall) > 0: + pop.append(fetchall) + + ret.close() # TODO try-finally needed + conn.close() + + if len(pop) <= 0: + print("No statistic data found for the identify list '%s' !" % (options.identify,)) + exit() + + print("%d identify found !" % (len(pop),)) + + if options.errorbars_raw: + if options.outfile: + graph_errorbars_raw(pop, options.minimize, options.outfile + "." + options.extension) + else: + graph_errorbars_raw(pop, options.minimize) + + if options.errorbars_fitness: + if options.outfile: + graph_errorbars_fitness(pop, options.minimize, options.outfile + "." + options.extension) + else: + graph_errorbars_fitness(pop, options.minimize) + + if options.maxmin_raw: + if options.outfile: + graph_maxmin_raw(pop, options.minimize, options.outfile + "." + options.extension) + else: + graph_maxmin_raw(pop, options.minimize) + + if options.maxmin_fitness: + if options.outfile: + graph_maxmin_fitness(pop, options.minimize, options.outfile + "." + options.extension) + else: + graph_maxmin_fitness(pop, options.minimize) + + if options.diff_raw: + if options.outfile: + graph_diff_raw(pop, options.minimize, options.outfile + "." + options.extension) + else: + graph_diff_raw(pop, options.minimize) + + if options.all_graphs: + all_graph_functions = [graph_errorbars_raw, graph_errorbars_fitness, graph_maxmin_raw, + graph_maxmin_fitness, graph_diff_raw] + if options.outfile: + parser.error("You can't specify one file to all graphs !") + + dirname = "graphs_" + options.identify + if not os.path.isdir(dirname): + os.mkdir(dirname) + + for graph in all_graph_functions: + filename = dirname + "/" + filename += options.identify + "_" + graph.__name__[6:] + filename += "." + options.extension + graph(pop, options.minimize, filename) + + print("\n\tDone ! The graphs was saved in the directory '%s'" % (dirname)) + + if options.compare_raw: + if options.outfile: + graph_compare_raw(pop, options.minimize, identify_list, options.outfile + "." + options.extension) + else: + graph_compare_raw(pop, options.minimize, identify_list) + + if options.compare_fitness: + if options.outfile: + graph_compare_fitness(pop, options.minimize, identify_list, options.outfile + "." + options.extension) + else: + graph_compare_fitness(pop, options.minimize, identify_list) + + if options.pop_heatmap_raw: + if options.outfile: + graph_pop_heatmap_raw(pop, options.minimize, options.colormap, options.outfile + "." + options.extension) + else: + graph_pop_heatmap_raw(pop, options.minimize, options.colormap) + + if options.pop_heatmap_fitness: + if options.outfile: + graph_pop_heatmap_fitness(pop, options.minimize, options.colormap, + options.outfile + "." + options.extension) + else: + graph_pop_heatmap_fitness(pop, options.minimize, options.colormap) diff --git a/requirements_test.txt b/requirements_test.txt index 2293c98..18ec924 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,3 +1,5 @@ -nose==1.3.0 -coverage==3.7 -mock==1.0.1 +nose +coverage +mock +future +flake8 diff --git a/setup.py b/setup.py index 1080014..58c633e 100644 --- a/setup.py +++ b/setup.py @@ -1,18 +1,21 @@ -from distutils.core import setup #changed to distutils.core for pypy comptibility -from pyevolve import __version__, __author__ +#from distutils.core import setup #changed to distutils.core for pypy comptibility +from setuptools import setup +import sys setup( name = "Pyevolve", - version = __version__, + version = '0.6', packages = ["pyevolve"], scripts = ['pyevolve_graph.py'], + install_requires = ['future'], package_data = { 'pyevolve': ['*.txt'] }, - author = __author__, + test_suite = 'tests', + author = 'Christian S. Perone', author_email = "christian.perone@gmail.com", description = "A complete, free and open-source evolutionary framework written in Python", license = "PSF", keywords = "genetic algorithm genetic programming algorithms framework library python ai evolutionary framework", - url = "http://pyevolve.sourceforge.net/" + url = "http://pyevolve.sourceforge.net/", ) diff --git a/tests/test_allele.py b/tests/test_allele.py new file mode 100644 index 0000000..cb6d0ec --- /dev/null +++ b/tests/test_allele.py @@ -0,0 +1,199 @@ +# 36-39, 47-48, 56, 60, 64-73, 77-79, 83-86, 90-92, 96-107, 125-127, 131, 135, 143, 147, 151, 155, 159, 163, 171, +# 175-178, 198-202, 206-207, 216-220, 223, 226-229, 232, 239, 246, 250-252, 256-262, 270, 274, 278, 282-289 + +# flake8: noqa +from collections import Iterable +from unittest import TestCase + +from pyevolve import G1DBinaryString +from pyevolve import GAllele +from pyevolve import G1DList +from pyevolve import G2DBinaryString +from pyevolve import G2DList +from pyevolve.GenomeBase import G1DBase +from pyevolve.GenomeBase import GTreeBase +from pyevolve.GTree import GTree +from pyevolve.GTree import GTreeNode +from pyevolve.GTree import GTreeNodeBase +from pyevolve.GTree import GTreeNodeGP + + +class GAllelesTestCase(TestCase): + + def test_createAlleles_default(self): + _alleles = GAllele.GAlleles(allele_list=None) + self.assertTrue(hasattr(_alleles, 'allele_list'), True) + self.assertTrue(hasattr(_alleles, 'homogeneous'), True) + self.assertEqual(_alleles.allele_list, []) + _alleles = GAllele.GAlleles(allele_list=[1,2,3]) + self.assertEqual(_alleles.allele_list, [1,2,3]) + _alleles = GAllele.GAlleles(homogeneous=True) + self.assertEqual(_alleles.homogeneous, True) + + def test_Alleles_iadd(self): + _alleles1 = GAllele.GAlleles(allele_list=[1, 2, 3]) + _alleles1 += 4 + self.assertEqual(_alleles1.allele_list, [1, 2, 3, 4]) + + def test_Alleles_add(self): + _alleles1 = GAllele.GAlleles(allele_list=[1, 2, 3]) + _alleles1.add(4) + self.assertEqual(_alleles1.allele_list, [1, 2, 3, 4]) + + def test_Alleles_slicing(self): + # includes slice operation, getitem and setitem + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3]) + self.assertEqual(_alleles[1], 2) + with self.assertRaises(Exception): + _ = _alleles[4] + _alleles[1] = 5 + self.assertEqual(_alleles[1], 5) + self.assertEqual(_alleles[0:2], [1, 5]) + + def test_Alleles_slicing_homogeneous(self): + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3], homogeneous=True) + self.assertEqual(_alleles[2], 1) + _alleles[1] = 5 + self.assertEqual(_alleles[0], 5) + + def test_Alleles_iter(self): + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3]) + self.assertIsInstance(iter(_alleles), Iterable) + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3], homogeneous=True) + self.assertIsInstance(iter(_alleles), Iterable) + + def test_Alleles_len(self): + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3]) + self.assertEqual(len(_alleles), 3) + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3], homogeneous=True) + self.assertEqual(len(_alleles), 1) + + def test_Alleles_repr(self): + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3]) + self.assertIsInstance(repr(_alleles), str) + _alleles = GAllele.GAlleles(allele_list=[1, 2, 3], homogeneous=True) + self.assertIsInstance(repr(_alleles), str) + + +class GAlleleListTestCase(TestCase): + + def test_createAlleleList_default(self): + _allelelist = GAllele.GAlleleList() + self.assertEqual(_allelelist.options, []) + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + self.assertEqual(_allelelist.options, [1, 2, 3]) + + def test_AlleleList_clear(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + _allelelist.clear() + self.assertEqual(_allelelist.options, []) + + def test_AlleleList_getRandomAllele(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + random_allele = _allelelist.getRandomAllele() + self.assertIn(random_allele, _allelelist.options) + + def test_AlleleList_add(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + _allelelist.add(4) + self.assertEqual(_allelelist.options, [1, 2, 3, 4]) + + def test_AlleleList_slicing(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + self.assertEqual(_allelelist[0:2], [1, 2]) + self.assertEqual(_allelelist[1], 2) + _allelelist[1] = 4 + self.assertEqual(_allelelist[1], 4) + + def test_AlleleList_iter(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + self.assertIsInstance(iter(_allelelist), Iterable) + + def test_AlleleList_len(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + self.assertEqual(len(_allelelist), 3) + + def test_AlleleList_remove(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + _allelelist.remove(2) + self.assertEqual(_allelelist.options, [1, 3]) + + def test_AlleleList_repr(self): + _allelelist = GAllele.GAlleleList(options=[1, 2, 3]) + self.assertIsInstance(repr(_allelelist), str) + + +class GAlleleRangeTestCase(TestCase): + + def test_createAlleleRange(self): + _allelerange = GAllele.GAlleleRange(10, 20) + self.assertEqual(_allelerange.beginEnd, [(10, 20)]) + self.assertEqual(_allelerange.minimum, 10) + self.assertEqual(_allelerange.maximum, 20) + _allelerange = GAllele.GAlleleRange(1.0, 2.0, real=True) + self.assertEqual(_allelerange.real, True) + + def test_AlleleRange_add(self): + _allelerange = GAllele.GAlleleRange(10, 20) + _allelerange.add(30, 40) + self.assertEqual(_allelerange.beginEnd, [(10, 20), (30, 40)]) + self.assertEqual(_allelerange.minimum, 10) + self.assertEqual(_allelerange.maximum, 40) + with self.assertRaises(ValueError): + _allelerange.add(40, 30) + + def test_AlleleRange_slicing(self): + _allelerange = GAllele.GAlleleRange(10, 20) + _allelerange.add(30, 40) + self.assertEqual(_allelerange[0], (10, 20)) + _allelerange[1] = (50, 60) + self.assertEqual(_allelerange[1], (50, 60)) + with self.assertRaises(ValueError): + _allelerange[1] = (60, 50) + + def test_AlleleRange_iter(self): + _allelerange = GAllele.GAlleleRange(10, 20) + self.assertIsInstance(iter(_allelerange), Iterable) + + def test_AlleleRange_getMaximum(self): + _allelerange = GAllele.GAlleleRange(10, 20) + self.assertEqual(_allelerange.getMinimum(), 10) + + def test_AlleleRange_getMinimum(self): + _allelerange = GAllele.GAlleleRange(10, 20) + self.assertEqual(_allelerange.getMaximum(), 20) + + def test_AlleleRange_clear(self): + _allelerange = GAllele.GAlleleRange(10, 20) + _allelerange.clear() + self.assertEqual(_allelerange.beginEnd, []) + + def test_AlleleRange_getRandomAllele(self): + _allelerange = GAllele.GAlleleRange(10, 20) + random_allele = _allelerange.getRandomAllele() + self.assertTrue(random_allele, + any([x[0] <= random_allele <= x[1] for x in _allelerange.beginEnd])) + _allelerange.add(30, 40) + random_allele = _allelerange.getRandomAllele() + self.assertTrue(random_allele, + any([x[0] <= random_allele <= x[1] for x in _allelerange.beginEnd])) + _allelerange = GAllele.GAlleleRange(1.0, 2.0, real=True) + random_allele = _allelerange.getRandomAllele() + self.assertTrue(random_allele, + any([x[0] <= random_allele <= x[1] for x in _allelerange.beginEnd])) + + + def test_AlleleRange_real(self): + _allelerange = GAllele.GAlleleRange(10, 20) + self.assertEqual(_allelerange.getReal(), False) + _allelerange.setReal(flag=True) + self.assertEqual(_allelerange.getReal(), True) + + def test_AlleleRange_len(self): + _allelerange = GAllele.GAlleleRange(10, 20) + _allelerange.add(30, 40) + self.assertEqual(len(_allelerange), 2) + + def test_AlleleRange_repr(self): + _allelerange = GAllele.GAlleleRange(10, 20) + self.assertIsInstance(repr(_allelerange), str) \ No newline at end of file diff --git a/tests/test_crossovers.py b/tests/test_crossovers.py index 1f87a50..7c7e9ba 100644 --- a/tests/test_crossovers.py +++ b/tests/test_crossovers.py @@ -1,3 +1,5 @@ + + from itertools import cycle import unittest @@ -22,13 +24,17 @@ def assertCrossoverResultsEqual( genome_attr_name='genomeList', # TODO refactor with Genome getter method assertion_name='assertEqual' ): + def genome_value_getter(g): + if genome_attr_name: + return getattr(g, genome_attr_name) + else: + return g crossover_extra_kwargs = crossover_extra_kwargs or {} kwargs = { 'mom': self.mom, 'dad': self.dad, } kwargs.update(crossover_extra_kwargs) - genome_value_getter = lambda g: getattr(g, genome_attr_name) if genome_attr_name else g actual_sister, actual_brother = [genome_value_getter(g) if g else None for g in crossover(None, **kwargs)] getattr(self, assertion_name)(actual_sister, expected_sister) getattr(self, assertion_name)(actual_brother, expected_brother) @@ -171,7 +177,7 @@ def test_crossfill_crossover(self, rand_mock): ) @patch('pyevolve.Crossovers.rand_random') - def test_crossfill_crossover(self, rand_mock): + def test_crossfill_crossover_sbx(self, rand_mock): rand_mock.return_value = 0.6 self.assertCrossoverResultsEqual( Crossovers.G1DListCrossoverRealSBX, @@ -329,9 +335,9 @@ def assetTreesEqual(self, tree1, tree2): self.assertEqual(root1.node_data, root2.node_data) root1_childs = set([l.node_data for l in root1.getChilds()]) root2_childs = set([l.node_data for l in root2.getChilds()]) - print root1_childs, root2_childs + print(root1_childs, root2_childs) self.assertFalse((root1_childs and not root2_childs) or (not root1_childs and root2_childs)) - print root1_childs, root2_childs + print(root1_childs, root2_childs) self.assertFalse(root1_childs - root2_childs) @patch('pyevolve.Crossovers.rand_choice') diff --git a/tests/test_genomes.py b/tests/test_genomes.py new file mode 100644 index 0000000..5eeb113 --- /dev/null +++ b/tests/test_genomes.py @@ -0,0 +1,215 @@ +# flake8: noqa +from collections import Iterable +from unittest import TestCase + +from pyevolve import G1DBinaryString +from pyevolve import G1DList +from pyevolve import G2DBinaryString +from pyevolve import G2DList +from pyevolve.GenomeBase import G1DBase +from pyevolve.GenomeBase import GTreeBase +from pyevolve.GTree import GTree +from pyevolve.GTree import GTreeNode +from pyevolve.GTree import GTreeNodeBase +from pyevolve.GTree import GTreeNodeGP + + +class G1DBinaryStringTestCase(TestCase): + + # def setUp(self): + # self._stats = Statistics.Statistics() + + def test_create1DBinary_default(self): + _genome = G1DBinaryString.G1DBinaryString() + self.assertTrue(hasattr(_genome, 'genomeList')) + self.assertTrue(hasattr(_genome, 'genomeSize')) + self.assertIsInstance(_genome.genomeSize, int) + self.assertTrue(hasattr(_genome, 'initializator')) + self.assertTrue(hasattr(_genome, 'mutator')) + self.assertTrue(hasattr(_genome, 'crossover')) + + def test_create1DBinary_len(self): + _genome = G1DBinaryString.G1DBinaryString(length=5) + self.assertTrue(hasattr(_genome, 'genomeList')) + self.assertEqual(_genome.genomeSize, 5) + self.assertTrue(hasattr(_genome, 'initializator')) + self.assertTrue(hasattr(_genome, 'mutator')) + self.assertTrue(hasattr(_genome, 'crossover')) + + def test_1DBinarySetitem(self): + _genome = G1DBinaryString.G1DBinaryString(length=5) + _genome.append(0) + _genome.append(1) + _genome.append(1) + self.assertEqual(_genome[0], 0) + with self.assertRaises(ValueError): + _genome[0] = 2 + with self.assertRaises(ValueError): + _genome[0:2] = [0,1,2] + + def test_getBinary(self): + _genome = G1DBinaryString.G1DBinaryString() + self.assertIsInstance(_genome.getBinary(), str) + + def test_getDecimal(self): + _genome = G1DBinaryString.G1DBinaryString(length=3) + _genome.append(0) + _genome.append(1) + _genome.append(1) + self.assertEqual(_genome.getDecimal(), 3) + + def test_append(self): + _genome = G1DBinaryString.G1DBinaryString(length=3) + _genome.append(0) + _genome.append(1) + self.assertEqual(_genome[0], 0) + with self.assertRaises(ValueError): + _genome.append(2) + + def test_repr(self): + _genome = G1DBinaryString.G1DBinaryString() + self.assertIsInstance(repr(_genome), str) + + +class G2DListTestCase(TestCase): + + # def setUp(self): + # self._stats = Statistics.Statistics() + + def test_create2DList_default(self): + _genome = G2DList.G2DList(3, 3) + self.assertTrue(hasattr(_genome, 'width')) + self.assertTrue(hasattr(_genome, 'height')) + self.assertTrue(hasattr(_genome, 'genomeList')) + self.assertTrue(hasattr(_genome, 'initializator')) + self.assertTrue(hasattr(_genome, 'mutator')) + self.assertTrue(hasattr(_genome, 'crossover')) + + def test_2DList_eq(self): + _genome1 = G2DList.G2DList(3, 2) + _genome2 = G2DList.G2DList(3, 3) + self.assertFalse(_genome1 == _genome2) + + _genome1 = G2DList.G2DList(2, 3) + _genome2 = G2DList.G2DList(3, 3) + self.assertFalse(_genome1 == _genome2) + + _genome1 = G2DList.G2DList(3, 3) + _genome2 = G2DList.G2DList(3, 3) + _genome1.setItem(2, 1, 0) + _genome2.setItem(2, 1, 1) + self.assertFalse(_genome1 == _genome2) + + _genome1 = G2DList.G2DList(3, 3) + _genome2 = G2DList.G2DList(3, 3) + self.assertTrue(_genome1 == _genome2) + + def test_2DList_iter(self): + _genome = G2DList.G2DList(3, 3) + self.assertIsInstance(iter(_genome), Iterable) + + def test_repr(self): + _genome = G2DList.G2DList(3, 3) + self.assertIsInstance(repr(_genome), str) + + def test_2DList_resumeString(self): + _genome = G2DList.G2DList(3, 3) + self.assertIsInstance(_genome.resumeString(), str) + + +class G1DListTestCase(TestCase): + + # def setUp(self): + # self._stats = Statistics.Statistics() + + def test_create1DList_default(self): + _genome = G1DList.G1DList() + self.assertTrue(hasattr(_genome, 'genomeSize')) + self.assertTrue(hasattr(_genome, 'initializator')) + self.assertTrue(hasattr(_genome, 'mutator')) + self.assertTrue(hasattr(_genome, 'crossover')) + + def test_1DList_mul(self): + _genome1 = G1DList.G1DList(size=3) + _genome1[:] = [1, 2, 3] + other = 2 + result = _genome1 * other + self.assertEqual(result.genomeList, [2, 4, 6]) + + def test_1DList_add(self): + _genome1 = G1DList.G1DList(size=3) + _genome1[:] = [1, 2, 3] + other = 2 + result = _genome1 + other + self.assertEqual(result.genomeList, [3, 4, 5]) + + def test_1DList_sub(self): + _genome1 = G1DList.G1DList(size=3) + _genome1[:] = [1, 2, 3] + other = 2 + result = _genome1 - other + self.assertEqual(result.genomeList, [-1, 0, 1]) + + def test_repr(self): + _genome = G1DList.G1DList() + self.assertIsInstance(repr(_genome), str) + + +class G2DBinaryStringTestCase(TestCase): + + # def setUp(self): + # self._stats = Statistics.Statistics() + + def test_create2DBinary_default(self): + _genome = G2DBinaryString.G2DBinaryString(3, 3) + self.assertTrue(hasattr(_genome, 'width')) + self.assertTrue(hasattr(_genome, 'height')) + self.assertTrue(hasattr(_genome, 'initializator')) + self.assertTrue(hasattr(_genome, 'mutator')) + self.assertTrue(hasattr(_genome, 'crossover')) + + def test_2DBinary_eq(self): + _genome1 = G2DBinaryString.G2DBinaryString(3, 2) + _genome2 = G2DBinaryString.G2DBinaryString(3, 3) + self.assertFalse(_genome1 == _genome2) + + _genome1 = G2DBinaryString.G2DBinaryString(2, 3) + _genome2 = G2DBinaryString.G2DBinaryString(3, 3) + self.assertFalse(_genome1 == _genome2) + + _genome1 = G2DBinaryString.G2DBinaryString(3, 3) + _genome2 = G2DBinaryString.G2DBinaryString(3, 3) + _genome1.setItem(2, 1, 0) + _genome2.setItem(2, 1, 1) + self.assertFalse(_genome1 == _genome2) + + _genome1 = G2DBinaryString.G2DBinaryString(3, 3) + _genome2 = G2DBinaryString.G2DBinaryString(3, 3) + self.assertTrue(_genome1 == _genome2) + + def test_2DBinary_setitem(self): + _genome = G2DBinaryString.G2DBinaryString(3, 3) + _genome.setItem(1,1,1) + self.assertEqual(_genome.getItem(1,1), 1) + with self.assertRaises(ValueError): + _genome.setItem(1, 1, 2) + + + def test_2DBinary_iter(self): + _genome = G2DBinaryString.G2DBinaryString(3, 3) + self.assertIsInstance(iter(_genome), Iterable) + + def test_repr(self): + _genome = G2DBinaryString.G2DBinaryString(3, 3) + self.assertIsInstance(repr(_genome), str) + + def test_2DBinary_resumeString(self): + _genome = G2DBinaryString.G2DBinaryString(3, 3) + self.assertIsInstance(_genome.resumeString(), str) + + def test_2DBinary_clearString(self): + _genome = G2DBinaryString.G2DBinaryString(2, 4) + _genome.clearString() + self.assertEqual(len(_genome.genomeString), _genome.getHeight()) + self.assertEqual(len(_genome.genomeString[0]), _genome.getWidth()) + self.assertEqual(_genome[1][1], None) \ No newline at end of file diff --git a/tests/test_mutators.py b/tests/test_mutators.py index 65c4d5d..3f90030 100644 --- a/tests/test_mutators.py +++ b/tests/test_mutators.py @@ -28,7 +28,6 @@ def test_swap_mutator_large_pmut(self, rand_mock): Mutators.G1DBinaryStringMutatorSwap(self.genome, pmut=0.5) self.assertEqual(self.genome.genomeList, expected_result) - @patch('pyevolve.Util.randomFlipCoin') def test_flip_mutator_small_pmut(self, coin_flip_mock): coin_flip_mock.return_value = 1 diff --git a/tests/test_simple_ga.py b/tests/test_simple_ga.py index 76df386..cdbe3da 100644 --- a/tests/test_simple_ga.py +++ b/tests/test_simple_ga.py @@ -1,6 +1,6 @@ from unittest import TestCase -from pyevolve import GSimpleGA, G1DList, Consts +from pyevolve import GSimpleGA, G1DList from pyevolve.GTree import GTreeGP @@ -26,7 +26,7 @@ def test_get_different_results_for_different_evaluators(self): self.ga = GSimpleGA.GSimpleGA(self.genome) self.ga.evolve(freq_stats=1) result2 = self.ga.bestIndividual() - self.assertNotEquals(result1, result2) + self.assertNotEqual(result1, result2) def test_fails_with_negative_evaluator(self): self.genome.evaluator.set(lambda _: -1) @@ -51,7 +51,8 @@ def test_exception_on_wrong_multiprocessing_argument(self): self.assertRaises(TypeError, self.ga.setMultiProcessing, {'flag': True, 'full_copy': 'not_bool_argument'}) def test_exception_no_wrong_mutation_rate_size(self): - self.assertRaises(ValueError, self.ga.setMutationRate, [2]) + self.assertRaises(BaseException, self.ga.setMutationRate, [2]) + # self.assertRaises(ValueError, self.ga.setMutationRate, [2]) def test_repr(self): ga = self.ga @@ -67,4 +68,4 @@ def test_repr(self): ga.nElitismReplacement, ga.dbAdapter, ]: - self.assertIn(str(param), ga_repr) \ No newline at end of file + self.assertIn(str(param), ga_repr) diff --git a/tests/test_statistics.py b/tests/test_statistics.py new file mode 100644 index 0000000..65620f9 --- /dev/null +++ b/tests/test_statistics.py @@ -0,0 +1,57 @@ +from unittest import TestCase + +from builtins import int + +from pyevolve import Statistics + + +class StatisticsTestCase(TestCase): + + def setUp(self): + self._stats = Statistics.Statistics() + + def test_lenStatistics(self): + self.assertEqual(len(self._stats), self._stats.internalDict.__len__()) + + def test_reprStatistics(self): + # it should be just successfully generated string + self.assertIsInstance(repr(self._stats), str) + + def test_statisticsAsTuple(self): + # modify to have some probable type diversity + self._stats["rawMax"] = 9223372036854775808 # it will be long on Py 2 + self._stats["rawMin"] = 1.2 # float + stat_tuple = self._stats.asTuple() + self.assertIsInstance(stat_tuple, tuple) + self.assertEqual(len(stat_tuple), len(self._stats)) + self.assertTrue(all(isinstance(x, (int, float)) for x in stat_tuple)) + + def test_clearStatistics(self): + len_before_clear = len(self._stats) + self._stats.clear() + len_after_clear = len(self._stats) + self.assertEqual(len_before_clear, len_after_clear) + clean_stats = Statistics.Statistics() + self.assertEqual(self._stats.internalDict, clean_stats.internalDict) + + def test_statisticsItems(self): + stat_items = self._stats.items() + stat_names = list(self._stats.internalDict.keys()) + self.assertIsInstance(stat_items, list) + self.assertEqual(len(stat_items), len(self._stats)) + self.assertTrue(all(isinstance(x[1], (int, float)) for x in stat_items)) + self.assertTrue(set(stat_names), set([x[0] for x in stat_items])) + + def test_cloneStatistics(self): + clone = self._stats.clone() + self.assertIsNot(clone, self._stats) + self.assertEqual(clone.internalDict, self._stats.internalDict) + self.assertEqual(clone.descriptions, self._stats.descriptions) + + def test_copyStatistics(self): + target = Statistics.Statistics() + self._stats.copy(target) + self.assertEqual(self._stats.internalDict, target.internalDict) + self.assertEqual(self._stats.descriptions, target.descriptions) + self.assertIsNot(self._stats.internalDict, target.internalDict) + self.assertIsNot(self._stats.descriptions, target.descriptions) diff --git a/tests/test_util.py b/tests/test_util.py index dea3a4d..962e5e5 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -16,4 +16,4 @@ def test_randomFlipCoin_border_cases(self): def test_list2DSwapElement(self): _list = [[1, 2, 3], [4, 5, 6]] Util.list2DSwapElement(_list, (0, 1), (1, 1)) - self.assertEqual(_list, [[1, 5, 3], [4, 2, 6]]) \ No newline at end of file + self.assertEqual(_list, [[1, 5, 3], [4, 2, 6]]) diff --git a/tox.ini b/tox.ini index 9db22e2..57bd3a4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,37 @@ [tox] -envlist = py27, pypy +envlist = py27, pypy, py35, py36, pep8, cov [testenv] commands = nosetests {posargs:-v} deps = -r{toxinidir}/requirements_test.txt + +[tox:travis] +2.7 = py27, pep8 +3.6 = py36, pep8 + +[testenv:cov] +setenv = + COVERAGE_FILE = .coverage +deps = -r{toxinidir}/requirements_test.txt +commands = coverage erase + nosetests --with-coverage --cover-inclusive {posargs} + coverage report -m + +[flake8] +ignore = C901, W605 # W605 behaviour is changed since 3.6 +import-order-style = google +exclude = + .git, + .tox, + __pycache__, + docs/source/conf.py, + setup.py, + old, + build, + dist +max-complexity = 10 + +[testenv:pep8] +deps = -r{toxinidir}/requirements_test.txt +commands = + flake8 {posargs} \ No newline at end of file