Height adaptability through a genetic algorithm in 3D-space.

Genetic Algorithm 3.0: Height adaptability in 3D

COMPUTATIONAL DESIGN
28.9 – 30.9 2016

This script creates a population of columns in three-dimensional space with different heights. There is a target height which these columns should reach through a genetic algorithm and the columns turn red when the height is reached. It works through a process where it’s manipulating 16bit binary numbers. Ex: 0000000011011011.

Genetic Algorithm 3.0 - Adaptability to a specific height in 3D-space.
Genetic Algorithm 3.0 – Adaptability to a specific height in 3D-space.
/*
    Spatial Experiments I
    30-09-16
    
    Architecture and height
    "This script creates a population of columns with different height in 3D space.
    There is a target height which these columns should reach through a
    genetic algorithm. They turn red when they have reached the target height."
    
    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 in 3D
    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 = 30;

// Mutation rate
float mutationRate;

// Population of architecture array
Architecture[] population;

// 2D grid
int cols, rows;
int scl = 25;
int w = 5000;
int h = 5000;
    

void setup() {
    // Background properties
    size(640,640,P3D);
    background(0);
    frameRate(7);
    cols = w / scl;
    rows = h/ scl;
    
    // 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(0);
    //midpoint of coordinatesystem
    translate(width/2, height/2);
    rotateX(radians(80));
    // 2D grid
    noFill();
    //translate(-width/2, -height/2);
    for (int y = -25; y < rows; y++) {
        for (int x = -25; x < cols; x++) {
            rect(x*scl, y*scl, scl, scl);
        }
    }
    
    // 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 % 5) == 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;
}

The second file, Architecture class.

class Architecture {
    // Class variables 
    color c;
    float x, y;
    float size, HEIGHT;
    String binaryHEIGHT;
    
    // Genes adressed as heights in binary numbers
    char[] binaryHEIGHTList;
    
    // Counting fitness
    float fitness;
     
    // Class constructor 
    Architecture()
    {
         c = color( random(10,245), random(10,245), random(10,245) );
         x = random(-(width/2),(width/2));
         y = random(-(height/2),(height/2));
         size = 25;
 
         HEIGHT = (random(10,300));
         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;
        }
        
        // Give structurs with right height a specific color
        color finished = #FF0000;
        
        // Display    
        try {
            // Protected code
            println("Convert me >> " + convertMe +" and TARGET " + target);
            int finalHEIGHT = unbinary(convertMe);
            float HEIGHT = (finalHEIGHT);
            //println("final height >> " + finalHEIGHT + " >> " + WIDTH);
            pushMatrix();
                stroke(255);
                translate(x, y, HEIGHT/2);
                //rotateY(PI/rotation);
                if ( target.equals(convertMe) ) {
                    fill(finished);
                } else {
                    fill( random(10,245) );
                }
                box(size, size, HEIGHT);
            popMatrix();
            
        } 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