Height adaptability through a genetic algorithm.

Genetic Algorithm 2.0: Height adaptability

COMPUTATIONAL DESIGN
24.9 – 28.9 2016

This script creates a population of columns with different heights. There is a target height which these columns should reach through a genetic algorithm. It works through a process where its manipulating 16bit binary numbers. Ex: 0000000011011011.

 

Genetic Algorithm 2.0 - It begins with columns with random heights and they evolve to a specific target height.
Genetic Algorithm 2.0 – It begins with columns with random heights and they evolve to a specific target height.

 

/*
    Spatial Experiments I
    27-09-16
    
    Architecture and height
    "This script creates a population of columns with different height.
    There is a target height which these columns should reach through a
    genetic algorithm."
    
    Version 1.0
    
    1. Take out a mean height / choose a height
    2. Convert height number to binary string
    3. Start genetic algorithm, which iterates over the binary string
    5. Display the result
    6. When all columns reach the same height, the goal is achieved
*/

// External libraries
import java.util.ArrayList;

// Setup variables

// Target color
float tempTarget;
String target;

// Building a Mating pool ArrayList
ArrayList matingPool;

// Building a list with heights
ArrayList heightPool;

// Population amount
int totalPopulation = 100;

// Mutation rate
float mutationRate;

// Population of architecture array
Architecture[] population;

void setup() {
    // Background properties
    size(640,640);
    background(255);
    frameRate(4);
    
    // List of heights
    heightPool = new ArrayList();
    
    // Mutation rate
    mutationRate = 0.00025;
    
    //*******************************************************
    // STEP 1: INITIALIZE POPULATION AND CHOOSE TARGET HEIGHT
    //*******************************************************
    population = new Architecture[totalPopulation];
     
    // Create the population
    for (int i = 0; i < population.length; i++) {
        population[i] = new Architecture();  
    }
    
    // Display start heights in console log and add heights to list
    for (int j = 0; j < population.length; j++) {
        population[j].displayHeights();
        heightPool.add(population[j].HEIGHT);
    }
    
    // Find the target height
    tempTarget = displayTarget(heightPool, totalPopulation);
    
    // Convert target height number into a binary number
    target = numberToBinary(tempTarget); // Ex 175 equal binary: 0000000101010001
         
} // End setup

//*******************************************************
// THE LOOP
//*******************************************************
// saving frames for animation
//int x = 0;
void draw() {
    background(255);
    
    // Display the objects
    for (int j = 0, n = population.length; j < n; j++) {
        population[j].display();
    }
    
    //*******************************************************
    // STEP 2. NATURAL SELECTION
    //*******************************************************
    // A) Calculate fitness
    for (int k = 0, n = population.length; k < n; k++) {
         population[k].fitness();
    } // End
    
    // B) Building the mating pool
    // list contains Architecture objects
    ArrayList matingPool = new ArrayList(); 
    
    for (int m = 0, max = population.length; m < max; m++) {
        // Add each member n times according to its fitness score.
        int newFitnessScore = int(population[m].fitness * 100); // EX. 0.25 * 100 = 25
        println(">> " + newFitnessScore + " out of 100 <<");
        for (int n = 0; n < newFitnessScore; n++) {
            matingPool.add(population[m]);
        }
    }
    
    //*******************************************************
    // STEP 3: REPRODUCTION   
    //*******************************************************
    for (int i = 0, n = population.length; i < n; i++) {
        int a = int(random(matingPool.size()));
        int b = int(random(matingPool.size()));
        // Pick parents from matingPool
        Architecture partnerA = matingPool.get(a);    // potential to improve here
        Architecture partnerB = matingPool.get(b);    // it can pick the same parents!
        
        // A) Crossover
        Architecture child = partnerA.crossover(partnerB);
        
        // B) Mutation
        // The population dies out after a while when you use mutation
        //child.mutate(mutationRate);
        //child.mutateSimple(mutationRate);
    
        // Note that we are overwriting the population with the new
        // children.  When draw() loops, we will perform all the same
        // steps with the new population of children.
        population[i] = child;
    }
    // for saving frames for animation
    /*if ( (x % 2) == 0 ) {
        saveFrame("GA-####.png");
    }
    x++; */
} // End draw


// Functions

// Calculates the target height for every call
float displayTarget( ArrayList listOfHeights, int sizeOfPopulation ) {
      float sum = 0;       
      for (float heights : listOfHeights) {
          sum = sum + heights;
      }
      float fakeTarget = (sum / sizeOfPopulation);
      int target = int(fakeTarget);
      println("This is the target height >> " + target);
      //println("This is the size of poplulation " + sizeOfPopulation);
      //println("This ist he sum of the list " + sum);
      return target;
}

// Convert a number into a binary number/String
String numberToBinary(float number) {
    int convert = int(number);
    String result = binary(convert,16);
    println("Number: " + convert + " equal binary: " + result);
    return result;
}

And the Architecture class file.

 
class Architecture {
    // Class variables 
    color c;
    float x, y;
    float WIDTH, HEIGHT;
    String binaryHEIGHT;
    
    // Genes adressed as heights in binary numbers
    char[] binaryHEIGHTList;
    
    // Counting fitness
    float fitness;
     
    // Class constructor 
    Architecture()
    {
         c = color( random(55, 200), random(55, 200), random(55, 200) );
         x = random(width);
         y = (height);
         WIDTH = 20;
 
         HEIGHT = (random(100,640));
         binaryHEIGHT = binary(int(HEIGHT),16); // Ex. 0000000010101011
         println("binaryHEIGHT >> " + binaryHEIGHT);
         // Convert string into a char list
         binaryHEIGHTList = binaryHEIGHT.toCharArray(); 

    } // End constructor
    
    // Methods
    void display() {
      
        // Create a String out of char list
        // We can then convert the string into int datatype
        String convertMe = new String();
        for ( char i : binaryHEIGHTList ) {
            convertMe = convertMe + i;
        }
        
        // Display    
        try {
            // Protected code
            println("Convert me >> " + convertMe);
            int finalHEIGHT = unbinary(convertMe);
            rect(x, y, WIDTH, -(finalHEIGHT)); // (-) the columns should be directed upwards
            println("final height >> " + finalHEIGHT + " >> " + WIDTH);
            fill(c);
            noStroke();
            
        } catch(NumberFormatException e1) {
            // Catch block
            //color def = color(255,255,255);
            println("It was not a number >> Mutation 'failed' <<");
            //rect(x, y, HEIGHT, WIDTH);
            //fill(def);
            //noStroke();
            
        } // End throw error                
    } // End display
    
    // Displaying the height numerically in the console log (debugging)
    void displayHeights() {
        println( "<< This is the height " + HEIGHT + " >>" );
    }  // End displayHeights
    
    // Calculate fitness
    void fitness() {
         int score = 0;
         // Looping through char list and target color list
         // comparing each letter in the two of them and 
         // giving it a fitness score if they are equal
         for ( int i = 6; i < binaryHEIGHTList.length; i++ ) {
              if (binaryHEIGHTList[i] == target.charAt(i)) { 
                  score++;
              }
         }
         // Set the fitness score
         // the target list is 16-6 ( 10 numbers )
         fitness = float(score)/(target.length() - 6); 
     } // End fitness
    
    // Crossover
    // method crossover has datatype Architecture
    // and parameter of object Architecture
    Architecture crossover(Architecture partner) {
        Architecture child = new Architecture();
        int midpoint = int( random(binaryHEIGHTList.length) );
        for (int i = 0; i < binaryHEIGHTList.length; i++) {
            if (i > midpoint) {
                child.binaryHEIGHTList[i] = binaryHEIGHTList[i]; 
            } else {              
                child.binaryHEIGHTList[i] = partner.binaryHEIGHTList[i]; 
            }
        }
        return child;
    } // End crossover
    
    // Mutation 1.0
    // Goes through the whole binary sequence
    void mutate(float mutationRate) {
        for (int i = 0; i < binaryHEIGHTList.length; i++) {
            if (random(1) < mutationRate) {
                // random 1 or 0
                int answer = int(( random(1)+0.5 ));
                binaryHEIGHTList[i] = char(answer);
                    
            } // End if
        } // End for loop through charColorList length
    } // End mutation 1.0
    
    // Mutation 2.0
    // Chooses only one string in the binary sequence
    void mutateSimple(float mutationRate) {
        int pointToMutate = int( random(binaryHEIGHTList.length) );
            if (random(1) < mutationRate) {
                // random 1 or 0
                int answer = int(( random(1)+0.5 ));
                binaryHEIGHTList[pointToMutate] = char(answer);
                    
            } // End if
    } // End mutation 2.0
    
    
} // End Architecture Class