############################################################################### # Evolution optimization strategy, based on genes frequency in genotype. # # Suitable for solving NP-complete problems such as generating sudoku game, # # tasks scheduling, designing networks and etc. # # Algorithm idea is taken from: # # http://www.cad.polito.it/FullDB/exact/sac98.html # # Implemented by: vasiliauskas.agnius@gmail.com # ############################################################################### import random class sgaLocus : """ Class for defining allele position (locus) in genome """ def __init__ ( self , Alleles ): self . Genes = [ None , None , None ] # [FirstGenome, SecondGenome, BestGenome] self . Alleles = dict ([( x , 1.0 / len ( Alleles )) for x in Alleles ]) class sgaGenotype : """ Class for defining population genotype. Main class for evolving problem solutions """ def __init__ ( self , alleleGroups , Amount , FitnessFunc , NotifyFunc = None , Minimize = True ): self . FitnessFunc = FitnessFunc self . NotifyFunc = NotifyFunc self . Minimize = Minimize self . AlleleAffectValue = 0.005 self . Fitness = [ None , None , None ] # [FirstGenome, SecondGenome, BestGenome] self . MutationProbability = 1.0 / ( Amount * len ( alleleGroups )) self . Lgroups = [[ sgaLocus ( algr ) for algr in alleleGroups ] for x in range ( Amount )] # Initializing random generators self . randmut = random . Random () self . randsel = random . Random () self . randfrq = random . Random () # >>> private methods starts def __GenerateIndividual ( self , GenomeNo ): assert GenomeNo in [ 0 , 1 ] for group in self . Lgroups : for locus in group : al = None # Does mutation occur if self . randmut . random () < self . MutationProbability : al = self . randsel . choice ( locus . Alleles . keys ()) # select allele by it`s frequency in genotype else : while al == None : for allele in locus . Alleles . keys (): if self . randfrq . random () < locus . Alleles [ allele ]: al = allele break # allele is selected, now setting genome locus . Genes [ GenomeNo ] = al if locus . Genes [ 2 ] == None : locus . Genes [ 2 ] = al def __AffectAlleles ( self , BetterGenome , UpdateBest ): assert BetterGenome in [ 0 , 1 ] afirst = self . AlleleAffectValue if BetterGenome == 0 else - self . AlleleAffectValue asecond = self . AlleleAffectValue if BetterGenome == 1 else - self . AlleleAffectValue for group in self . Lgroups : for locus in group : pfirst = locus . Alleles [ locus . Genes [ 0 ]] + afirst psecond = locus . Alleles [ locus . Genes [ 1 ]] + asecond pfirst = 1.0 if pfirst > 1.0 else 0.0 if pfirst < 0.0 else pfirst psecond = 1.0 if psecond > 1.0 else 0.0 if psecond < 0.0 else psecond locus . Alleles [ locus . Genes [ 0 ]] = pfirst locus . Alleles [ locus . Genes [ 1 ]] = psecond # check do we need to update best genome if UpdateBest : locus . Genes [ 2 ] = locus . Genes [ BetterGenome ] # <<< private methods ends def Evolve ( self , cycles ): for iter in range ( cycles ): BetterGenome , UpdateBest = None , False self . __GenerateIndividual ( 0 ) self . __GenerateIndividual ( 1 ) self . Fitness [ 0 ] = self . FitnessFunc ( self , 0 ) self . Fitness [ 1 ] = self . FitnessFunc ( self , 1 ) if self . Fitness [ 2 ] == None : self . Fitness [ 2 ] = self . FitnessFunc ( self , 2 ) if self . Fitness [ 0 ] > self . Fitness [ 1 ]: BetterGenome = 0 if not self . Minimize else 1 elif self . Fitness [ 0 ] < self . Fitness [ 1 ]: BetterGenome = 1 if not self . Minimize else 0 if BetterGenome != None : if ( self . Fitness [ BetterGenome ] > self . Fitness [ 2 ] and not self . Minimize ) or \ ( self . Fitness [ BetterGenome ] < self . Fitness [ 2 ] and self . Minimize ): UpdateBest = True self . Fitness [ 2 ] = self . Fitness [ BetterGenome ] self . __AffectAlleles ( BetterGenome , UpdateBest ) if self . NotifyFunc and UpdateBest : self . NotifyFunc ( self , iter ) def DumpGenotype ( self ): for ixg , group in enumerate ( self . Lgroups ): for ixl , locus in enumerate ( group ): for ixa , allele in enumerate ( locus . Alleles ): print "Grp_" + str ( ixg ), "|" , "Loc_" + str ( ixl ), "|" , allele , "=>" , locus . Alleles [ allele ], "prob." def testFitness ( gen , n ): # Try to find x,y,z such that equation x^2 - y^2 - z^2 - 27 = 0 return abs ( gen . Lgroups [ 0 ][ 0 ] . Genes [ n ] ** 2 - gen . Lgroups [ 0 ][ 1 ] . Genes [ n ] ** 2 - gen . Lgroups [ 0 ][ 2 ] . Genes [ n ] ** 2 - 27 ) def testNotify ( gen , iter ): print 'iteration_' + str ( iter ) + ': ' , '|' + str ( gen . Lgroups [ 0 ][ 0 ] . Genes [ 2 ]) + '^2 - ' + \ str ( gen . Lgroups [ 0 ][ 1 ] . Genes [ 2 ]) + '^2 - ' + \ str ( gen . Lgroups [ 0 ][ 2 ] . Genes [ 2 ]) + '^2 - 27| =' , gen . Fitness [ 2 ] if __name__ == "__main__" : print 'Solving equation x^2 - y^2 - z^2 - 27 = 0' print '--------------------------------------------' gen = sgaGenotype ([ range ( 2 , 61 ), range ( 2 , 61 ), range ( 2 , 61 )], 1 , testFitness , testNotify , Minimize = True ) gen . Evolve ( 1000 )