Last active
January 23, 2017 20:57
-
-
Save kolergy/d488d20294374f7157c3977db1ca84f4 to your computer and use it in GitHub Desktop.
Stupid Life: it's stupid but it moves, NN & GA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/freeware/bin/python | |
## #!/home/QtPalmtop/bin/python | |
#------------------------------------------------------------------------------------------- | |
#------------------------------------------------------------------------------------------- | |
# Ustructured NN | |
# Kolergy | |
# Written in Python. See http://www.python.org/ | |
#------------------------------------------------------------------------------------------- | |
#------------------------------------------------------------------------------------------- | |
import math | |
import random | |
import string | |
import time | |
from Tkinter import * | |
Version = "0.5.4" | |
#------------------------------------------------------------------------------------------- | |
# A Recursive Neural Network | |
class RNeurNet: | |
""" The unstructured network class""" | |
def __init__(self, layersNeurons, memNeurons): | |
self.layersNeurons = layersNeurons # Number of neurons on each layers | |
self.nLayers = len(layersNeurons) # Number of layers | |
self.nOutputs = self.layersNeurons[-1] # Number of outputs | |
self.layer = [] # Layers Storage | |
self.memNeurons = memNeurons # Number of memory neurons | |
self.layersNeurons[0] = self.layersNeurons[0] + 1 # Adding the biais Neuron | |
# Adding Mem Neuron on First & Last Layers | |
self.layersNeurons[ 0] = self.layersNeurons[ 0] + self.memNeurons | |
self.layersNeurons[-1] = self.layersNeurons[-1] + self.memNeurons | |
# Creating the network | |
for nl in range(self.nLayers): # Create the layer filed with neurons | |
lay = [] | |
for nn in range(self.layersNeurons[nl]): | |
Nname = "Neur" + str(nl) + "-" + str(nn) | |
if nl==0: lay.append(Neuron(Nname, [] ) ) | |
else: lay.append(Neuron(Nname, self.layer[nl-1]) ) | |
self.layer.append(lay) # Add the new layer to the network | |
#print "Network created with Architecture: %s" % self.layersNeurons | |
#creating the memory link | |
inputs = [] | |
for i in range(self.memNeurons): inputs = self.layer[-1][self.memNeurons:] | |
for i in range(self.memNeurons): self.layer[0][-1-i].addInputs(inputs) | |
# Runs an evaluation calculation of the network | |
def calc(self): | |
#print "Network evaluation calculation" | |
for l in self.layer: # Calc all layers | |
for n in l: n.calc() # Calc all neurons | |
# Evaluate a single pattern | |
def evaluateNet(self, inputPattern): | |
#print "Evaluating Pattern:%s" % inputPattern | |
for i in range(len(inputPattern)): self.layer[0][i].activation = inputPattern[i] # Application of the input pattern | |
self.calc() # Network evaluation calculation | |
#self.display() | |
output = [] | |
for i in range(self.nOutputs): output.append(self.layer[-1][i].activation*2.0 -1) # corrected to be in the range [-1, 1] | |
return(output) # Return the output Neurons | |
# Display the total network | |
def display(self): | |
print "" | |
print "Displaying of the complete network layer by layer" | |
for l in self.layer: | |
for n in l: n.display() | |
print "" | |
# Exporting the total network | |
def exportLinks(self): | |
#print "Exporting the links" | |
links = [] | |
for la in self.layer: | |
for n in la: | |
for li in n.inputsVector: | |
links.append(li.weight) | |
#print links | |
return(links) | |
# Importing the total network | |
def importLinks(self, links): | |
#print "Importing the links" | |
#print links | |
i = 0 | |
for la in self.layer: | |
for n in la: | |
for li in n.inputsVector: | |
li.weight = links[i] | |
i += 1 | |
#------------------------------------------------------------------------------------------- | |
# A Neuron | |
class Neuron: | |
' The Neuron Class' | |
def __init__(self, name, inputs): | |
self.activation = 0.0 | |
self.sum = 0.0 | |
self.inputsVector = [] | |
self.outputsVector = [] | |
self.name = name | |
self.addInputs(inputs) | |
# Create the input vector list filed with links to an other layer | |
def addInputs(self, inputs): | |
#print inputs | |
if len(inputs) != 0: | |
for i in inputs: self.inputsVector.append( Link(i, self) ) | |
# Calculate activation of neuron output from inputs sum | |
def calc(self): | |
self.sum = 0 | |
if len(self.inputsVector) != 0: # If not in input neuron | |
for c in self.inputsVector: # Sum the input values | |
c.calc() | |
self.sum += c.act | |
self.activation = 1/(1+math.exp(-self.sum)) # Sigmoid function | |
# Display the neuon characteristics : Name, Sum of inputs, Activation | |
def display(self): print "N:%s" % self.name + " S:%6.3f" % self.sum + " A:%6.3f" % self.activation | |
#------------------------------------------------------------------------------------------- | |
# A Link between a source neuron and a destination Neuron | |
class Link: | |
' A Link between two Neuron and its functions' | |
def __init__(self, source, destination, weightInit = -9999): | |
self.source = source | |
self.destination = destination | |
self.acti = 0.0 | |
# Set the output vector of the source neuron | |
self.source.outputsVector.append(self) | |
# initialise the weight to the given value or randomly if no value is given | |
if weightInit == -9999: self.weight = random.uniform(-2, 2) | |
else: self.weight = weightInit | |
# Calculate the link activation | |
def calc(self): | |
self.act = self.source.activation * self.weight | |
#print"L Sa:%5.3f" % self.source.activation + " W%5.3f" % self.weight + " A:%5.3f" %self.act | |
#------------------------------------------------------------------------------------------- | |
# The Beast | |
class Beast: | |
def __init__(self, area, position=[0.0,0.0] ): | |
self.position = position # The X & Y position of the beast | |
self.HP = 100 # Beast Health points | |
self.SP = 100 # Beast Stamina points | |
self.area = area # Reference to the GameArea | |
self.net = RNeurNet([3,8,8,8,2], 8) # The Recursive Neural Net | |
self.geneMxRange = 10.0 | |
self.repr = 10 | |
self.rect = [] | |
self.movment = [10, 10] | |
self.movmentPrev = [10, 10] | |
#print "initpos=%s" % self.position | |
def heartBeat(self): | |
#print "before:%s" % self.position | |
self.movmentPrev = self.movment | |
self.movment = self.net.evaluateNet(self.position) # Run the network with its inputs & get new pos | |
self.area.updatePos(self) # Ask the area to update the position | |
self.repr += 1 | |
self.HP -= 1 | |
#print "New Pos:%s" % self.position | |
def getGenes(self): | |
genes = self.net.exportLinks() | |
#print "Get GN" | |
#print genes | |
for i in range(len(genes)):genes[i] = 0.5 + genes[i] / self.geneMxRange # addimentioning | |
#print genes | |
return(genes) | |
def setGenes(self, genes): | |
for i in range(len(genes)):genes[i] = (genes[i]-0.5) * self.geneMxRange # redimentioning | |
self.net.importLinks(genes) # Set the network | |
self.repr = 0 # Child can not reproduce yet | |
#------------------------------------------------------------------------------------------- | |
# The Game Area | |
class GameArea: | |
def __init__(self, size=[20.0,10.0] ): | |
self.size = size # Size of the area in the X & Y directions origin is [0,0] | |
self.population = [] # Initialization of the empty population | |
self.TargetCorner1 = [0,0] # Initialization of the target zone lower corner | |
self.TargetCorner2 = [0,0] # Initialization of the target zone upper corner | |
self.heartBeat = 0.1 # Heart Beat in seconds | |
self.inTarget = [] # List of Beast in target | |
self.targetSize = 5 | |
self.targetCenter = [3,4] | |
self.gen = 0 # Current generation | |
self.displayArea = 1000 | |
self.zoomfact = self.displayArea / self.size[0] | |
self.root = Tk() | |
self.frame = Frame(self.root, bd=2, relief=SUNKEN) | |
self.deltaPop = 0 | |
#self.canvas = [] | |
self.frame.grid_rowconfigure(0, weight=1) | |
self.frame.grid_columnconfigure(0, weight=1) | |
self.xscrollbar = Scrollbar(self.frame, orient=HORIZONTAL) | |
self.xscrollbar.grid(row=1, column=0, sticky=E+W) | |
self.yscrollbar = Scrollbar(self.frame) | |
self.yscrollbar.grid(row=0, column=1, sticky=N+S) | |
self.canvas = Canvas(self.frame, bd=0, scrollregion=(0, 0, 1000, 1000), xscrollcommand = self.xscrollbar.set, yscrollcommand = self.yscrollbar.set) | |
self.button = Button(self.canvas, text="Quit", fg="red", command=self.quitArea ) | |
self.run = Button(self.canvas, text="Run", command=self.setAndRun ) | |
self.save = Button(self.canvas, text="Save", command=self.savePop ) | |
self.load = Button(self.canvas, text="Load", command=self.loadPop ) | |
self.button.pack(side=LEFT) | |
self.run.pack( side=LEFT) | |
self.save.pack( side=LEFT) | |
self.load.pack( side=LEFT) | |
self.canvas.grid(row=0, column=0, sticky=N+S+E+W) | |
#print self.zoomfact | |
self.area = self.canvas.create_rectangle(0, 0, self.displayArea, self.displayArea, fill="green") | |
self.xscrollbar.config(command=self.canvas.xview) | |
self.yscrollbar.config(command=self.canvas.yview) | |
self.frame.pack(fill=BOTH, expand=1) | |
print"B4 Display" | |
self.root.mainloop() | |
self.root.destroy() | |
print"After Display" | |
def quitArea(self): | |
self.population[:] | |
self.frame.quit | |
def setTargetZone(self): # Definition of a rectangular target zone | |
self.TargetCorner1 = [max(0, self.targetCenter[0]-self.targetSize/2), max(0, self.targetCenter[1]-self.targetSize/2)] | |
self.TargetCorner2 = [min(self.size[0], self.targetCenter[0]+self.targetSize/2), min(self.size[0], self.targetCenter[1]+self.targetSize/2)] | |
self.canvas.coords(self.target, | |
self.TargetCorner1[0] * self.zoomfact, | |
self.TargetCorner1[1] * self.zoomfact, | |
self.TargetCorner2[0] * self.zoomfact, | |
self.TargetCorner2[1] * self.zoomfact) | |
#self.canvas.update() | |
def updateTargetZone(self): | |
delta = 0.1 | |
fact = 1 | |
randDist= 0.5 | |
borderMargin = 0.5 | |
self.sizeOld = self.targetSize | |
if len(self.population) > 200: fact = 0.5 | |
elif len(self.population) < 50: fact = 2 | |
if self.deltaPop > 2 : | |
self.targetSize *= 1-(delta/fact) | |
if self.deltaPop > 0.2*len(self.population) and len(self.population) > 80 : | |
self.targetSize *= 1-(2/delta*fact) | |
if self.deltaPop < 0 : | |
self.targetSize *= 1+(delta*fact) | |
if self.deltaPop < 0.2*len(self.population) and len(self.population) < 100: | |
self.targetSize *= 1+(2*delta*fact) | |
if self.targetSize > self.size[0]*1.5: self.targetSize = self.size[0]*1.5 | |
elif self.targetSize < 0.3: self.targetSize = 0.3 | |
#if self.gen % 50 == 0: self.targetCenter = [random.uniform(1, self.size[0]-1),random.uniform(1, self.size[1]-1)] | |
#if self.gen % 50 == 0: | |
self.targetCenter = [min(self.size[0]-borderMargin, max(borderMargin, self.targetCenter[0] + random.uniform(-randDist, randDist))), \ | |
min(self.size[1]-borderMargin, max(borderMargin, self.targetCenter[1] + random.uniform(-randDist, randDist)))] | |
#print "targetCenter" 71 83 | |
#print self.targetCenter | |
self.setTargetZone() | |
def addBeast(self, position = [0, 0]): # Add a beast to the game area | |
if position == [0, 0]: position = [random.uniform(0, self.size[0]/2),random.uniform(0, self.size[1]/2)] | |
position.append(0.0) # Add the color value | |
beast = Beast(self, position ) | |
beast.rect = self.canvas.create_line(position[0] * self.zoomfact, position[1] * self.zoomfact, | |
position[0] * self.zoomfact +2, position[1] * self.zoomfact +2, arrow=LAST, arrowshape=(2,3,2)) | |
self.population.append(beast) | |
def addChild(self, genes): # Add a beast to the game area | |
if self.targetCenter[0] > 5: x = 1 | |
else: x = self.size[0]-1 | |
if self.targetCenter[1] > 5: y = 1 | |
else: y = self.size[1]-1 | |
beast = Beast(self, [random.uniform(x-1, x+1),random.uniform(y-1, y+1), 0.0] ) | |
beast.setGenes(genes) | |
beast.rect = self.canvas.create_line(beast.position[0] * self.zoomfact, beast.position[1] * self.zoomfact, | |
beast.position[0] * self.zoomfact +2, beast.position[1] * self.zoomfact +2, arrow=LAST, arrowshape=(2,3,2)) | |
self.population.append(beast) | |
def runAllBeasts(self): # Tick heart beat | |
while len(self.population) > 1: | |
self.gen += 1 | |
self.inTarget = [] | |
pop0 = len(self.population) | |
self.updateTargetZone() | |
for b in self.population: b.heartBeat() | |
self.mate() | |
pop1 = len(self.population) | |
self.deltaPop = pop1-pop0 | |
dSize = self.targetSize / self.sizeOld | |
print "%6d" % self.gen +" :: Pop:%5d" %len(self.population) + " Dpop:%5d" % self.deltaPop + " DSize:%6.2f" % dSize | |
self.canvas.update() | |
#time.sleep(self.heartBeat) | |
def setAndRun(self): | |
self.target = self.canvas.create_rectangle(self.TargetCorner1[0] * self.zoomfact, | |
self.TargetCorner1[1] * self.zoomfact, | |
self.TargetCorner2[0] * self.zoomfact, | |
self.TargetCorner2[1] * self.zoomfact, fill="blue") | |
print "Create Networks" | |
for i in range(200): self.addBeast() | |
print "Go" | |
self.runAllBeasts() | |
def savePop(self): | |
path = "c:\\Users\\toto\\My Documents" | |
oFileName = 'StupidLife.sav' | |
oFile = open(path + "\\" + oFileName, "w+") | |
oFile.write("StupidLife V%s\n" % Version) | |
oFile.write(" Pos_X Pos_Y ") | |
for i in range(len(self.population[0].getGenes())): oFile.write(" Gen%2d " % i) | |
oFile.write("\n") | |
for b in self.population: | |
oFile.write("%6.4f" % b.position[0] + " %6.4f" % b.position[1]) | |
for g in b.getGenes(): oFile.write(" %6.4f" % g) | |
oFile.write("\n") | |
#oFile.write("%8.0f" %dataTable['alt'][j] + "\t%8.2f" %dataTable['time'][j] + "\t%8.1f" %dataTable['dist'][j] + "\t%8.4f" %dataTable['fuel'][j] + "\n") | |
oFile.close() | |
del(self.population[:]) # empty population | |
#crash = 0.0 / 0.0 | |
def loadPop(self): | |
position = [0.0, 0.0] | |
path = "c:\\Users\\toto\\My Documents" | |
iFileName = 'StupidLife.sav' | |
iFile = open(path + "\\" + iFileName, "r") # | |
rLines = iFile.read().splitlines() # Read the file & close it | |
iFile.close() # | |
for l in rLines[2:]: # population is all lines except the 2 first | |
l = map(float, l.split(" ")) # split the line string & transform to float | |
position[0] = l[0] # Set X Position | |
position[1] = l[1] # Set Y Position | |
self.addBeast(position) # Add the new memeber to the population | |
self.population[-1].setGenes(l[2:]) # Set Beast Genes (memory is not saved) | |
#crash = 0.0 / 0.0 | |
self.runAllBeasts() | |
def mate(self): # reproduce beast in Target Zone | |
while len(self.inTarget) >= 2: # 2 parents needed | |
b1 = self.inTarget[0] # Set parents 1 & 2 | |
b2 = self.inTarget[1] | |
b1.repr = 0 # re-init reproduction counter | |
b2.repr = 0 | |
childs = self.modifyChrom(b1.getGenes(), b2.getGenes()) # Perform the crossover & get the childs genes | |
for genes in childs: self.addChild(genes) # Cheate the childs with the new genes | |
del self.inTarget[:2] # Parents removed from current reproduction pool | |
def isTargetZone(self, beast): # Check if Beast in Target Zone | |
position = beast.position | |
if ( (min(self.TargetCorner1[0], self.TargetCorner2[0]) < position[0] < max(self.TargetCorner1[0], self.TargetCorner2[0])) \ | |
& (min(self.TargetCorner1[1], self.TargetCorner2[1]) < position[1] < max(self.TargetCorner1[1], self.TargetCorner2[1])) ): | |
beast.position.append(1.0) | |
if beast not in self.inTarget and beast.repr >= 4: | |
self.inTarget.append(beast) | |
self.canvas.itemconfig(beast.rect, fill="green") | |
#print "In target" | |
else: | |
if 1 < position[0] or 1 < position[1] or (self.size[0]-1) < position[0] or (self.size[1]-1) < position[1]: | |
beast.position.append(-1.0) # Warning approaching edge | |
self.canvas.itemconfig(beast.rect, fill="red") | |
#self.canvas.update() | |
else: | |
beast.position.append(0.0) | |
self.canvas.itemconfig(beast.rect, fill="blue") | |
#self.canvas.update() | |
if beast in self.inTarget: | |
self.inTarget.remove(beast) | |
#print "Out of target" | |
def updatePos(self, beast): # Update the beast position | |
oldPosition = beast.position | |
#print "OldPos:%s" % oldPosition | |
newPosition =[] | |
totMove = math.sqrt(math.pow(beast.movment[0],2)+math.pow(beast.movment[1],2)) + math.sqrt(math.pow(beast.movmentPrev[0],2)+math.pow(beast.movmentPrev[1],2)) | |
for i in range(len(oldPosition)-1): | |
newPosition.append(oldPosition[i] + beast.movment[i]/5) # Update position | |
# if newPosition[i] < 0: newPosition[i] = 0 # Ensure Beast stays in range | |
# elif newPosition[i] > self.size[i]: newPosition[i] = self.size[i] | |
if newPosition[i] < 0 or newPosition[i] > self.size[i] or beast.HP <= 0 or totMove < 0.2: | |
#print "one killed" | |
#print len(self.population) | |
self.population.remove(beast) # Eliminate Beast that go out | |
self.canvas.delete(beast.rect) | |
self.canvas.update() | |
#print len(self.population) | |
return | |
beast.position = newPosition[:] # Set the new position | |
self.canvas.coords(beast.rect, | |
oldPosition[0] * self.zoomfact, | |
oldPosition[1] * self.zoomfact, | |
beast.position[0] * self.zoomfact, | |
beast.position[1] * self.zoomfact) | |
#print "NewPos:%s" % newPosition | |
#print "BeastPos:%s" % beast.position | |
self.isTargetZone(beast) # Check if in target zone | |
#print "BeastPos:%s" % beast.position | |
#--------------------------------------------------------------------------- | |
# The BoundedSBXCrossover | |
# ref: Deb Agrawal, Simulated binary crossover for continous search space | |
# complex systems 9, 1995, pp115-148. | |
#--------------------------------------------------------------------------- | |
def modifyChrom(self, parent1Gene, parent2Gene): | |
#print "*** Start BoundedSBXCrossover() ***" | |
# Settings ------------------------------------------------------------------------------- | |
distibutionIndex = 1.0 | |
geneCrossoverRate = 0.5 | |
child1Gene = [0,]*len(parent1Gene) | |
child2Gene = [0,]*len(parent1Gene) | |
tempChroms = [] | |
#print parent1Gene | |
#print parent2Gene | |
#print child1Gene | |
for i in range(len(parent1Gene)): | |
if(random.uniform(0.0, 1.0) <= geneCrossoverRate and parent1Gene[i] != parent2Gene[i]): # selecting only 1/2 genes | |
if(parent2Gene[i] > parent1Gene[i]): # Sort the parents | |
pGeneBig = parent2Gene[i] | |
pGeneSml = parent1Gene[i] | |
else: | |
pGeneBig = parent1Gene[i] | |
pGeneSml = parent2Gene[i] | |
if((pGeneSml - 0) > (1 - pGeneBig)): beta = 1 + (2*(1 - pGeneBig) / (pGeneBig - pGeneSml)) # find the shortest distance to the | |
else: beta = 1 + (2*(pGeneSml - 0) / (pGeneBig - pGeneSml)) # boundary & adapt the distribution | |
alpha = 2 - math.pow(beta, -(distibutionIndex + 1)) | |
r = random.uniform(0.0, 1.0) | |
if (r <= 1/alpha): betaq = math.pow( alpha * r , 1 / (distibutionIndex + 1)) | |
else: betaq = math.pow( 1 / (2 - alpha * r), 1 / (distibutionIndex + 1)) | |
child1Gene[i] = 0.5*((pGeneSml+pGeneBig) - betaq*(pGeneBig-pGeneSml)) | |
child2Gene[i] = 0.5*((pGeneSml+pGeneBig) + betaq*(pGeneBig-pGeneSml)) | |
#print "alpha:%f" % alpha + "\beta:%f" % beta + "\betaq:%f" % betaq | |
else: | |
child1Gene[i] = parent1Gene[i] # if no mod copy the parents genes | |
child2Gene[i] = parent2Gene[i] | |
# make sure every thing is in bounds | |
if (child1Gene[i] < 0): | |
print "Cg1-%d" % i +" Low:%f" % child1Gene[i] | |
child1Gene[i] = 0 | |
if (child2Gene[i] < 0): | |
print "Cg2-%d" % i +" Low:%f" % child1Gene[i] | |
child2Gene[i] = 0 | |
if (child1Gene[i] > 1): | |
print "Cg1-%d" % i +" Hig:%f" % child1Gene[i] | |
child1Gene[i] = 1 | |
if (child2Gene[i] > 1): | |
print "Cg2-%d" % i +" Hig:%f" % child1Gene[i] | |
child2Gene[i] = 1 | |
#print child1Gene | |
#print child2Gene | |
tempChroms.append(child1Gene) | |
tempChroms.append(child2Gene) | |
return(tempChroms) | |
#------------------------------------------------------------------------------------------- | |
#------------------------------------------------------------------------------------------- | |
if __name__ == '__main__': # Testing Main | |
# Seed initialisation | |
random.seed(time.time()) | |
print "NeuronLearn V0.5.4" | |
# Network Creation | |
area = GameArea([10.0,10.0]) | |
#area.setTargetZone() | |
#print "Create Networks" | |
#for i in range(200): area.addBeast() | |
#print "Go" | |
#area.runAllBeasts() | |
print "Finished" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment