main.py 9.71 KB
# -*- coding: utf-8 -*-
"""
Created on Mon May  7 12:46:27 2018

@author: Olav
"""
import numpy as np
from math import floor, ceil, sqrt
import random
from chromsome import Chromosome
#pip install bitstring in anaconda prompt, remember to update pip: pip install --upgrade pip
from bitstring import BitArray

def fitnessCheckBounds(fitness, corners):
    reward = 10
    rewardAll = 100
    allCornersInBound = True
    
    #print(corners)
    for corner in range(len(corners)):
        if corner >= 7:
            break
        
        if corners[corner] >= xTo:
            fitness += reward
        else:
            allCornersInBound = False
        
        if corners[corner+1] >= yHeight:
            fitness += reward*2
        else:
            allCornersInBound = False
        
    
    if allCornersInBound:
        fitness += rewardAll
    else:
        fitness -= ceil(rewardAll/2)
        
    return fitness

def fitnessCheckLeastEnergyJump(fitness, corners):
    reward = 10
    heights = np.array([corners[1], corners[3], corners[5], corners[7]])
       
    avgHeights = np.average(heights)
    avgHeights = avgHeights - yHeight
    
    fitness += (1/avgHeights) * reward
    
    return fitness

def fitnessCheckQuadrant(fitness, corners):
    reward = 100
    
    #Check length of all corners
    sideLen1 = getSideLength(corners[0], corners[1], corners[2], corners[3])
    sideLen2 = getSideLength(corners[2], corners[3], corners[4], corners[5])
    sideLen3 = getSideLength(corners[4], corners[5], corners[6], corners[7])
    sideLen4 = getSideLength(corners[6], corners[7], corners[0], corners[1])
    allSides = np.array([sideLen1, sideLen2, sideLen3, sideLen4])
    
    #How far away is the sides from chromosome original height
    for i in range(0, 4):
        diff = getDifferenceSide(sideLen1, agentHeight)
        diff = round(diff, 1)
        if diff == 0.0:
            diff = 0.1
        fitness += int((1/diff) * reward)
        
    #Are the sides of same length?
    allSidesEqual = True
    for i in range(0, 4):
        if i != 3:
            diff = round(getDifferenceSide(allSides[i], allSides[i+1]), 1)
        else:
            diff = round(getDifferenceSide(allSides[i], allSides[0]), 1)
        
        if diff == 0.0:
            diff = 0.1
        else:
            allSidesEqual = False
        
        fitness += (1/diff) * reward
        
    if allSidesEqual:
        fitness += reward * 1000
  
    return fitness

def getSideLength(x1, y1, x2, y2):
    return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))

def getDifferenceSide(side1, side2):
    return abs(side1 - side2)

def fitnessValue(chromosome):
    chromosomeAgent = chromosome[0]
    chromosome = chromosomeAgent.gene
    
    fitness = 0
    
    x1 = float(BitArray(bin=''.join(map(str, chromosome[0:chromosomeBitsValue]))).uint)
    y1 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue:chromosomeBitsValue*2]))).uint)
    
    x2 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue*2:chromosomeBitsValue*3]))).uint)
    y2 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue*3:chromosomeBitsValue*4]))).uint)
    
    x3 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue*4:chromosomeBitsValue*5]))).uint)
    y3 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue*5:chromosomeBitsValue*6]))).uint)
    
    x4 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue*6:chromosomeBitsValue*7]))).uint)
    y4 = float(BitArray(bin=''.join(map(str, chromosome[chromosomeBitsValue*7:chromosomeBitsValue*8]))).uint)
    
    
    dx1 = x1 + upperLCorner[0]
    dy1 = y1 + upperLCorner[1]
    
    dx2 = x2 + upperRCorner[0]
    dy2 = y2 + upperRCorner[1]
    
    dx3 = x3 + lowerLCorner[0]
    dy3 = y3 + lowerLCorner[1]
    
    dx4 = x4 + lowerRCorner[0]
    dy4 = y4 + lowerRCorner[1]
    
    corners = np.array([dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4])
    
    fitness = fitnessCheckBounds(fitness, corners)
    fitness = fitnessCheckLeastEnergyJump(fitness, corners)
    fitness = fitnessCheckQuadrant(fitness, corners)
    
    fitness = int(fitness)
    chromosomeAgent.fitness = fitness
    return fitness
    
def fitness(population):
    l = list(population)
    l.sort(key=lambda chromosome: fitnessValue(chromosome), reverse=True)
    population = np.array(l)
    
    return population

def mutation(population):
    """
    for i in range(len(population)):
        for j in range(len(population[i])):
            if random.uniform(0.0, 1.0) <= mutationRatio:
                if population[i][j] != 0:
                    population[i][j] = 0
                else:
                    population[i][j] = 1
    """
    chromosomePick = np.random.randint(len(population)) 
    for j in range(populationSize):
        if random.uniform(0.0, 1.0) <= mutationRatio:
            chromosome = population[chromosomePick][0]
            chromosomeBit = chromosome.geneAt(j)
            
            if chromosomeBit != 0:
                chromosome.geneSet(j, 0)
            else:
                chromosome.geneSet(j, 1)
            
    
    return population

def roulette(population):
    populationSize = len(population)
    
    return population

def crossover(population):
    #print("Before: \n" + str(population))
    offspring = np.array
    splitSize = 2
    for i in range(populationProduction):
        if i >= populationSize-1:
            break
        
        parent1 = population.item(i).gene
        parent2 = population.item(i+1).gene

        #print('paren1' + str(parent1))
        #print('paren2' + str(parent2))
        
        child1 = np.array([], dtype=int)
        child2 = np.array([], dtype=int)
        
        itr = 0
        count = 0
        while (count < chromosomeSize):
            if itr%2 != 0:
                child1 = np.concatenate((
                        child1,
                        parent1[count:count+splitSize], #p1 split x/y
                        parent2[count+splitSize:count+4] #p2 split x/y
                        ), axis=0)
                
                child2 = np.concatenate((
                        child2,
                        parent2[count:count+splitSize], #p1 split x/y
                        parent1[count+splitSize:count+4] #p2 split x/y
                        ), axis=0)
                
            else:
                child1 = np.concatenate((
                        child1,
                        parent1[count:count+4], #p1 take the whole x/y
                        ), axis=0)
                child2 = np.concatenate((
                        child2,
                        parent2[count:count+4], #p1 take the whole x/y
                        ), axis=0)
            
            count += chromosomeBitsValue
            itr += 1
            
        
        #print('child1' + str(child1))
        #print('child2' + str(child2))
        if i == 0:
            offspring = np.vstack([Chromosome(child1), Chromosome(child2)])
        else:
            offspring = np.vstack([offspring, Chromosome(child1), Chromosome(child2)])
        
    #Remove
    #population = np.delete(population, np.s_[len(population)-len(offspring):len(population)], axis = 0)
    
    #Insert
    population = np.vstack([offspring, population])
    
    #print("After: \n" + str(population))
    return population
        
    

# Init agent values
yHeight = 4.0
xStart = 3.0
xTo = 0.0
agentHeight = 0.2
upperLCorner = np.array([xStart-(agentHeight/2), agentHeight])
upperRCorner = np.array([xStart+(agentHeight/2), agentHeight])
lowerLCorner = np.array([xStart-(agentHeight/2), 0.0])
lowerRCorner = np.array([xStart+(agentHeight/2), 0.0])


# Size of population
populationSize = 12

# percentage of parents which will produce two childrens
populationProduce = 0.5

# WARNING: OLD below, not that relevant
# Population production will determine how many chromosomes will be able to mate.
# The chromosomes are "cheating" on eachother so that we pick chromosome 0 - 1 to mate
# then 1 - 2, 2 - 3 and so on. Hence 100% populationProduce (see above) will yield a 
# populationProduction of 12 chromosomes, although it says 6. Check crossover function.
populationProduction = floor(ceil(populationSize * populationProduce)/2)
print('populationProduction ' + str(populationProduction))

# How often a bit will flip during a mutation
mutationRatio = 0.3
print('mutationRatio ' + str(mutationRatio))

# How many interation of generations
generationSize = 100

# A value eg. x coordinate for a corner on a rectangle represents 4 bit value
chromosomeBitsValue = 4

# A rectangle has 4 points x,y where one point x is equal to 4 binaries.
# eg. 15 decimal is the highest point
# Times two since a corner has two values and times 4 since a rect has 4 corners
chromosomeSize = chromosomeBitsValue*2*4

# Init random population
#population = np.random.randint(2, size=(populationSize, chromosomeSize))
for i in range(populationSize):
    chromosomeGene = np.random.randint(2, size=(chromosomeSize)) 
    if i == 0:
        population = np.array(Chromosome(chromosomeGene))
    else:
        population = np.vstack([population, Chromosome(chromosomeGene)])


# How many cycles until a mutation
mutationCycle = 2

threshold = False
for generation in range(generationSize):
    
    print ('Generation: ' + str(generation))
    #population = roulette(population) #roulette wheel
    population = crossover(population) #crossing 
    if generation % mutationCycle == 0:
        population = mutation(population)
        
    population = fitness(population) #sort by fitness
    
    #test numb
    numb = 0
    for c in population:
        numb += 1
        print(c[0].fitness)
        if numb == populationSize:
            break
        
        #if fit >= 260:
        #    threshold = True
    
    if threshold:
        break
    

    
print(population)