153 lines
5.0 KiB
Python
Executable File
153 lines
5.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import sys
|
|
import parser
|
|
import random
|
|
import numpy as np
|
|
import ea
|
|
|
|
def parsingArguments():
|
|
# parsing args:
|
|
parser = argparse.ArgumentParser(description="evolutionary algorithm simulation", formatter_class=argparse.RawTextHelpFormatter)
|
|
parser.add_argument('--offspringSize', dest='offspringSize', default = 1 , help='size for new offspring')
|
|
parser.add_argument('--P', dest='P', default = 2, help = 'start population size')
|
|
parser.add_argument('--L', dest='L', default=3, help = 'genome length')
|
|
parser.add_argument('--f', dest='f', default="- (x[0] - 5)**2 + 10", help='fitness function in python syntax. x[0] - x[L] are the arguments')
|
|
parser.add_argument('--epsilon', dest='epsilon', default=0.25, help='epsilon for random mutation')
|
|
parser.add_argument('--cycles', dest='cycles', default=100, help='cycles to calculate')
|
|
|
|
if (len(sys.argv) == 1):
|
|
|
|
# no parameters given. Print help and ask user at runtime for options:
|
|
|
|
settings = {}
|
|
settings["offspringSize"] = 1
|
|
settings["P"] = 2
|
|
settings["L"] = 1
|
|
settings["f"] = "- (x[0] - 5)**2 + 10"
|
|
settings["epsilon"] = 0.25
|
|
settings["cycles"] = 100
|
|
|
|
|
|
while True:
|
|
parser.print_help()
|
|
print("\ncurrent settings:")
|
|
for key in settings.keys():
|
|
print(str(key) + " = " + str(settings[key]))
|
|
a = input("enter parameter to change. press enter to continue: ")
|
|
if len(a) == 0:
|
|
break
|
|
val = input("enter new value: ")
|
|
settings[a] = val
|
|
# passing settings to parser:
|
|
parser.set_defaults(offspringSize=settings["offspringSize"])
|
|
parser.set_defaults(P=settings["P"])
|
|
parser.set_defaults(L=settings["L"])
|
|
parser.set_defaults(f=settings["f"])
|
|
parser.set_defaults(epsilon=settings["epsilon"])
|
|
parser.set_defaults(cycles=settings["cycles"])
|
|
return parser.parse_args()
|
|
|
|
# easy adjustable functions for the ea-cycle. Will be packed in lambda objects in main()
|
|
def inheritance(parents):
|
|
'''
|
|
|
|
:param parents: list of Individuals. Their genome can be accessed by parents[i].genome
|
|
:return: genome for new offspring individual
|
|
'''
|
|
|
|
# just copy genome from first parent:
|
|
return parents[0].genome
|
|
|
|
def mutation(genome, e):
|
|
'''
|
|
|
|
:param genome: list of length L of real values: the genome to mutate
|
|
:param e: epsilon value (for random range)
|
|
:return: the mutated genome
|
|
'''
|
|
|
|
# mutate new genome by equally distributed random value in range [-e:e]
|
|
newGenome = []
|
|
|
|
for i in range(len(genome)):
|
|
newGenome.append(genome[i] + random.random() * 2 * e - e)
|
|
|
|
return newGenome
|
|
|
|
def externalSelection(fitness):
|
|
'''
|
|
|
|
:param fitness: list with fitness values
|
|
:return: list of indices of surviving individuals
|
|
'''
|
|
# only keep the fittest
|
|
return [np.argmax(fitness)]
|
|
|
|
def parentSelection(population, fitness):
|
|
'''
|
|
parent selection method for one individual
|
|
:param population: list of individuals which survived external selection
|
|
:param fitness: list of fitness values for given population
|
|
:return: list of parents for one new offspring individual
|
|
'''
|
|
|
|
# only first (and only individual so far) is parent
|
|
return [0]
|
|
|
|
|
|
def main():
|
|
|
|
# at first, get arguments
|
|
args = parsingArguments()
|
|
offspringSize = int(args.offspringSize)
|
|
P = int(args.P)
|
|
L = int(args.L)
|
|
f = args.f
|
|
epsilon = float(args.epsilon)
|
|
cycles = int(args.cycles)
|
|
|
|
# parse and compile fitness function to evaluateable code
|
|
functionCode = parser.expr(f).compile()
|
|
|
|
# build easy adjustable lambda functions for each step of the EA-cycle:
|
|
|
|
# fitness function:
|
|
fitnessFunctionLambda = lambda x: eval(functionCode)
|
|
|
|
# inheritance function: just copy genome from first parent
|
|
inheritanceFunctionLambda = lambda parents: inheritance(parents)
|
|
|
|
# mutation function:
|
|
mutationFunctionLambda = lambda genome: mutation(genome, epsilon)
|
|
|
|
# external selection:
|
|
externalSelectionLambda = lambda fitness: externalSelection(fitness)
|
|
|
|
# parentSelection:
|
|
parentSelectionLambda = lambda population, fitness: parentSelection(population, fitness)
|
|
|
|
ep = ea.EvolutionaryPopulation(L,
|
|
offspringSize,
|
|
fitnessFunctionLambda,
|
|
inheritanceFunctionLambda,
|
|
mutationFunctionLambda,
|
|
externalSelectionLambda,
|
|
parentSelectionLambda)
|
|
|
|
#start with random population:
|
|
for i in range(P):
|
|
genome = []
|
|
for i in range(L):
|
|
genome.append(random.random() * 10 - 5) # random value in range [-5:5]. TODO: make adjustable
|
|
ep.addIndividual(genome)
|
|
|
|
ep.evaluateFitness() # called manually, because population is modified
|
|
ep.printPopulation()
|
|
ep.performCycle(cycles)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|