Browse files

refactored population and accuracy modules

  • Loading branch information...
1 parent 3d4ad0a commit 0429698471a11c92da84f5591efbf937bbfb9a6a @Eyjafjallajokull committed Jan 13, 2013
Showing with 153 additions and 51 deletions.
  1. +13 −6 genetics/accuracy.py
  2. +25 −24 genetics/population.py
  3. +14 −20 gengen.py
  4. +99 −0 lib/event.py
  5. +2 −1 tests/test_population.py
View
19 genetics/accuracy.py
@@ -75,10 +75,17 @@ def increase(self):
log.info('genomeSize change triggered resetting of the other params')
break
- def checkPopulation(self, population):
- if population.getBestGenome().generation >= self.genomeGenerationTrigger:
- return self.canIncrease()
- return False
+ def onCalculatedFitness(self, event):
+ pop = event.data['population']
+ if pop.getBestGenome().generation >= self.genomeGenerationTrigger and self.canIncrease():
+ self.increase()
+ pop.resetGeneration()
+
+ def onLastGeneration(self, event):
+ pop = event.data['population']
+ if self.canIncrease():
+ self.increase()
+ pop.resetGeneration()
+
+
- def checkBeforeEnd(self):
- return self.canIncrease()
View
49 genetics/population.py
@@ -3,8 +3,8 @@
from time import time
import pickle
import random
-from genetics.accuracy import AccuracyMachine
from lib.common import do
+from lib.event import Event, EventDispatcher
import lib.log as log
import lib.config as config
@@ -17,15 +17,17 @@ def _poolFitnessCalculation(params):
return genome
-class Population():
- def __init__(self, genomeType):
+class Population(EventDispatcher):
+ def __init__(self, genomeType, fitnessMachine):
+ super(Population, self).__init__()
self.pool = Pool(processes=4, maxtasksperchild=100)
self.generation = 0
self.genomeType = genomeType
self.genomes = []
- self.fitnessMachine = None
- # todo: AccuracyMachine should listen for Population events
- self.accuracyMachine = AccuracyMachine()
+ self.fitnessMachine = fitnessMachine
+
+ def _dispatch_event(self, type):
+ self.dispatch_event(Event(type, data={'population':self}))
def load(self):
genomeFiles = glob.glob(config.config['main']['populationRamPath'] + '*_genome.obj')
@@ -47,25 +49,22 @@ def initialize(self):
log.info('initialized population of %d genomes' % config.config['ga']['populationSize'])
def step(self):
+ self._dispatch_event('stepStart')
log.info('SUNRISE %d generation' % self.generation)
start = time()
self.calculateFitness()
+ self._dispatch_event('calculatedFitness')
- self.genomes = sorted(self.genomes, cmp=lambda a, b: cmp(a.fitness, b.fitness))
- if self.accuracyMachine.checkPopulation(self):
- self.accuracyMachine.increase()
- self.resetGeneration()
-
- bestGenome = self.genomes[0]
- log.debug(
- 'current population ' + ', '.join(map(lambda a: a.serial + '-' + str(a.fitness), self.genomes)))
+ bestGenome = self.getBestGenome()
+ log.debug('current population ' + ', '.join(map(lambda a: a.serial + '-' + str(a.fitness), self.genomes)))
log.info('best genome %s-%d; average fitness %d'
- % (bestGenome.serial, bestGenome.fitness,
- reduce(lambda avg, g: avg + g.fitness, self.genomes, 0) / len(self.genomes)))
+ % (bestGenome.serial, bestGenome.fitness,reduce(lambda avg, g: avg + g.fitness, self.genomes, 0) / len(self.genomes)))
log.debug('genomeSize=%d' % config.config['ga']['genomeSize'])
+
parents = self.selection()
self.crossover(parents)
log.info('%d s' % int(time() - start))
+ self._dispatch_event('stepEnd')
def calculateFitness(self):
log.debug('calculateFitness')
@@ -75,9 +74,9 @@ def calculateFitness(self):
def selection(self):
selectedGenomes = self._selectionTournament()
- log.debug('selected %d genomes' % (len(selectedGenomes)))
- selectedGenomes = sorted(selectedGenomes, cmp=lambda a, b: cmp(a.fitness, b.fitness))
- log.debug(', '.join(map(lambda a: a.serial + '-' + str(a.fitness), selectedGenomes)))
+ #selectedGenomes = sorted(selectedGenomes, cmp=lambda a, b: cmp(a.fitness, b.fitness))
+ log.debug('selected %d genomes: %s' %
+ (len(selectedGenomes), ', '.join(map(lambda a: a.serial + '-' + str(a.fitness), selectedGenomes))))
return selectedGenomes
def _selectionTournament(self):
@@ -135,8 +134,11 @@ def crossover(self, parents):
self.genomes.append(genomeC)
def getBestGenome(self):
- self.genomes = sorted(self.genomes, cmp=lambda a, b: cmp(a.fitness, b.fitness))
- return self.genomes[0]
+ best = self.genomes[0]
+ for genome in self.genomes:
+ if genome.fitness < best.fitness:
+ best = genome
+ return best
def resetGeneration(self):
log.info('resetGeneration')
@@ -151,6 +153,5 @@ def evolve(self):
genome.generation += 1
genome.save()
self.generation += 1
- if self.generation == config.config['ga']['generations'] and self.accuracyMachine.checkBeforeEnd():
- self.accuracyMachine.increase()
- self.resetGeneration()
+ if self.generation == config.config['ga']['generations']:
+ self._dispatch_event('lastGeneration')
View
34 gengen.py
@@ -1,13 +1,12 @@
import argparse
from unittest import TestLoader, TextTestRunner
+from genetics.accuracy import AccuracyMachine
from genetics.population import Population
from genetics.genome import *
from lib.renderer.blender import BlenderRenderer
from lib.config import readConfig
from lib.log import initLogger
-
-
def initRamDir(basePath, ramPath):
tmp = ramPath
if tmp[-1]=='/':
@@ -25,42 +24,37 @@ def closeRamDir(basePath, ramPath):
parser = argparse.ArgumentParser()
parser.add_argument('command', metavar='COMMAND', help='command: init, evolve, tests')
parser.add_argument('-c','--config', metavar='CONFIG', help='config file')
- #todo: add debug mode
-# parser.add_argument('-v', '--verbose', action='store_true', help='verbose mode')
args = parser.parse_args()
if args.command=='tests':
suite = TestLoader().discover('tests', pattern='*.py')
- TextTestRunner(verbosity=2).run(suite)
- exit(0)
+ result = TextTestRunner(verbosity=2).run(suite)
+ result = 0 if result.wasSuccessful() else 1
+ exit(result)
cfg = readConfig(args.config)
logger = initLogger()
initRamDir(cfg['main']['populationPath'], cfg['main']['populationRamPath'])
-# g = pickle.load(open('population_ram/1249448_genome.obj'))
-# br = BlenderRenderer(cfg['main']['baseBlendPath'])
-# br.renderToFile(g)
-# ogr = OpenglRenderer()
-# ogr.renderToScreen(g)
-# exit()
+ renderer = BlenderRenderer()
+ fitnessMachine = MeshFitnessMachine(cfg['main']['baseImage'], renderer)
+ pop = Population(MeshGenome, fitnessMachine)
- br = BlenderRenderer()
- fm = MeshFitnessMachine(cfg['main']['baseImage'], br)
- p = Population(MeshGenome)
- p.fitnessMachine = fm
+ accuracyMachine = AccuracyMachine()
+ pop.add_event_listener('calculatedFitness', accuracyMachine.onCalculatedFitness)
+ pop.add_event_listener('lastGeneration', accuracyMachine.onLastGeneration)
if args.command=='init':
- p.initialize()
+ pop.initialize()
elif args.command=='evolve':
- p.load()
+ pop.load()
try:
- p.evolve()
+ pop.evolve()
except KeyboardInterrupt as ki:
pass
closeRamDir(cfg['main']['populationPath'], cfg['main']['populationRamPath'])
- best = p.getBestGenome()
+ best = pop.getBestGenome()
print(best.serial, best.fitness)
View
99 lib/event.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+from lib import log
+
+# -----------------------------------------------------------------------------
+# Event and EventDispatcher classes
+# -----------------------------------------------------------------------------
+
+class Event(object):
+ """
+ Generic event to use with EventDispatcher.
+ """
+
+ def __init__(self, event_type, data=None):
+ """
+ The constructor accepts an event type as string and a custom data
+ """
+ self._type = event_type
+ self._data = data
+
+ @property
+ def type(self):
+ """
+ Returns the event type
+ """
+ return self._type
+
+ @property
+ def data(self):
+ """
+ Returns the data associated to the event
+ """
+ return self._data
+
+
+class EventDispatcher(object):
+ """
+ Generic event dispatcher which listen and dispatch events
+ """
+
+ def __init__(self):
+ self._events = dict()
+
+ def __del__(self):
+ """
+ Remove all listener references at destruction time
+ """
+ self._events = None
+
+ def has_listener(self, event_type, listener):
+ """
+ Return true if listener is register to event_type
+ """
+ # Check for event type and for the listener
+ if event_type in self._events.keys():
+ return listener in self._events[event_type]
+ else:
+ return False
+
+ def dispatch_event(self, event):
+ """
+ Dispatch an instance of Event class
+ """
+ # Dispatch the event to all the associated listeners
+ if event.type in self._events.keys():
+ listeners = self._events[event.type]
+ if len(listeners)>0:
+ log.debug('dispatch event "%s" to %d listener(s)' % (event.type, len(listeners)))
+ for listener in listeners:
+ listener(event)
+
+ def add_event_listener(self, event_type, listener):
+ """
+ Add an event listener for an event type
+ """
+ # Add listener to the event type
+ if not self.has_listener(event_type, listener):
+ listeners = self._events.get(event_type, [])
+
+ listeners.append(listener)
+
+ self._events[event_type] = listeners
+
+ def remove_event_listener(self, event_type, listener):
+ """
+ Remove event listener.
+ """
+ # Remove the listener from the event type
+ if self.has_listener(event_type, listener):
+ listeners = self._events[event_type]
+
+ if len(listeners) == 1:
+ # Only this listener remains so remove the key
+ del self._events[event_type]
+
+ else:
+ # Update listeners chain
+ listeners.remove(listener)
+
+ self._events[event_type] = listeners
View
3 tests/test_population.py
@@ -1,3 +1,4 @@
+from genetics.fitness import MeshFitnessMachine, BaseFitnessMachine
from genetics.population import Population
from genetics.genome import TestGenome
from tests.test_base import TestBase
@@ -8,7 +9,7 @@ class TestPopulation(TestBase):
def setUp(self):
self.initConfig('basic')
self.initLog()
- self.population = Population(self.genomeType)
+ self.population = Population(self.genomeType, BaseFitnessMachine(None))
self.population.pool._processes = 1
def test_load_init(self):

0 comments on commit 0429698

Please sign in to comment.