/********************************************************************
*
* Copyright (c) 2006-2007 Berlin Brown and botnode.com All Rights Reserved
*
* http://www.opensource.org/licenses/bsd-license.php
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the Botnode.com (Berlin Brown) nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Date:
* Main Description:
* Contact: Berlin Brown <berlin dot brown at gmail.com>
*********************************************************************/
package org.adder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
*/
public class Population {
/*
* Based on
* http://www.c-sharpcorner.com/UploadFile/mgold/GAAdderDesign09242005053429AM/GAAdderDesign.aspx\
*
* "Using Genetic Algorithms to Design Logic Circuits in C# By Mike Gold February 05, 2003"
*
*/
public static final int kLength = 20;
public static final int kInitialPopulation = 500;
public static final int kPopulationLimit = 500;
public static final int kMin = 0;
public static final int kMax = 8;
public static final double kMutationFrequency = 0.33f;
public static final double kDeathFitness = 0.00f;
public static final double kReproductionFitness = 0.0f;
private List<Genome> genomes = new ArrayList<Genome>(20);
private final List<Double> scores = new ArrayList<Double>(20);
private final List<Genome> genomeReproducers = new ArrayList<Genome>(20);
private final List<Genome> genomeResults = new ArrayList<Genome>(20);
private final List<Genome> genomeFamily = new ArrayList<Genome>(20);
private int currentPopulation = kInitialPopulation;
private int generation = 1;
private final boolean best2 = true;
/////////////////////////////////////////////////////////////////
public Population() {
for (int i = 0; i < kInitialPopulation; i++) {
final EquationGenome aGenome = new EquationGenome(kLength, kMin, kMax);
aGenome.setCrossoverPoint(EquationGenome.nextInt(EquationGenome.TheSeed, 1, (int) aGenome.length));
aGenome.calculateFitness();
genomes.add(aGenome);
}
}
/**
* Method mutate.
* @param aGene Genome
*/
private void mutate(final Genome aGene) {
final double nint = EquationGenome.TheSeed.nextInt(100);
if (nint < (kMutationFrequency * 100.0)) {
aGene.mutate();
}
}
/**
* Method removeRange.
* @param elements List
* @param startIndex int
* @param endIndex int
*/
public static void removeRange(final List<Genome> elements, final int startIndex, final int endIndex) {
int index;
if (startIndex > endIndex) {
throw new IllegalArgumentException();
}
for (index = endIndex; index >= startIndex; index--) {
elements.remove(index);
}
}
/**
* Method clone.
* @param list List
* @return List
*/
public static final List<Genome> clonePopulation(final List<Genome> list) {
final List<Genome> newList = new ArrayList<Genome>(80);
for (final Iterator<Genome> it = list.iterator(); it.hasNext();) {
newList.add((Genome) it.next());
}
return newList;
}
public void nextGeneration() {
// increment the generation;
generation++;
// check who can die
for (int i = 0; i < genomes.size(); i++) {
if (((Genome) genomes.get(i)).canDie(kDeathFitness)) {
genomes.remove(i);
i--;
}
}
// determine who can reproduce
genomeReproducers.clear();
genomeResults.clear();
for (int i = 0; i < genomes.size(); i++) {
if (((Genome) genomes.get(i)).canReproduce(kReproductionFitness)) {
genomeReproducers.add(genomes.get(i));
}
}
// do the crossover of the genes and add them to the population
doCrossover(genomeReproducers);
genomes = (List<Genome>) clonePopulation(genomeResults);
// mutate a few genes in the new population
for (int i = 0; i < genomes.size(); i++) {
mutate((Genome) genomes.get(i));
}
// calculate fitness of all the genes
for (int i = 0; i < genomes.size(); i++) {
((Genome) genomes.get(i)).calculateFitness();
}
// kill all the genes above the population limit
if (genomes.size() > kPopulationLimit) {
removeRange(genomes, kPopulationLimit, genomes.size() - kPopulationLimit);
}
currentPopulation = genomes.size();
}
/**
* Method calculateFitnessForAll.
* @param genes List<EquationGenome>
*/
public void calculateFitnessForAll(final List<Genome> genes) {
for (Genome lg : genes) {
lg.calculateFitness();
}
}
/**
* Method doCrossover.
* @param genes List
*/
public void doCrossover(final List<Genome> genes) {
final List<Genome> geneMoms = new ArrayList<Genome>(80);
final List<Genome> geneDads = new ArrayList<Genome>(80);
for (int i = 0; i < genes.size(); i++) {
// randomly pick the moms and dad's
if ((EquationGenome.TheSeed.nextInt(100) % 2) > 20) {
geneMoms.add(genes.get(i));
} else {
geneDads.add(genes.get(i));
}
}
// now make them equal
if (geneMoms.size() > geneDads.size()) {
while (geneMoms.size() > geneDads.size()) {
geneDads.add(geneMoms.get(geneMoms.size() - 1));
geneMoms.remove(geneMoms.size() - 1);
}
if (geneDads.size() > geneMoms.size()) {
geneDads.remove(geneDads.size() - 1); // make sure they are
// equal
}
} else {
while (geneDads.size() > geneMoms.size()) {
geneMoms.add(geneDads.get(geneDads.size() - 1));
geneDads.remove(geneDads.size() - 1);
}
if (geneMoms.size() > geneDads.size()) {
geneMoms.remove(geneMoms.size() - 1); // make sure they are
// equal
}
}
// now cross them over and add them according to fitness
for (int i = 0; i < geneDads.size(); i++) {
// pick best 2 from parent and children
EquationGenome babyGene1 = null;
EquationGenome babyGene2 = null;
final int randomnum = EquationGenome.TheSeed.nextInt(100) % 3;
System.out.println("<Dad> ########$$$$ " + randomnum);
if (randomnum == 0) {
babyGene1 = (EquationGenome) ((EquationGenome) geneDads.get(i)).crossover((EquationGenome) geneMoms.get(i));
babyGene2 = (EquationGenome) ((EquationGenome) geneMoms.get(i)).crossover((EquationGenome) geneDads.get(i));
} else if (randomnum == 1) {
babyGene1 = (EquationGenome) ((EquationGenome) geneDads.get(i)).crossover2Point((EquationGenome) geneMoms.get(i));
babyGene2 = (EquationGenome) ((EquationGenome) geneMoms.get(i)).crossover2Point((EquationGenome) geneDads.get(i));
} else {
babyGene1 = (EquationGenome) ((EquationGenome) geneDads.get(i)).uniformCrossover((EquationGenome) geneMoms.get(i));
babyGene2 = (EquationGenome) ((EquationGenome) geneMoms.get(i)).uniformCrossover((EquationGenome) geneDads.get(i));
} // End of if - else
genomeFamily.clear();
genomeFamily.add(geneDads.get(i));
genomeFamily.add(geneMoms.get(i));
genomeFamily.add(babyGene1);
genomeFamily.add(babyGene2);
calculateFitnessForAll(genomeFamily);
for (int j = 0; j < 4; j++) {
checkForUndefinedFitness((Genome) genomeFamily.get(j));
}
Collections.sort((List<Genome>) genomeFamily);
if (best2) {
// if Best2 is true, add top fitness genes
genomeResults.add(genomeFamily.get(0));
genomeResults.add(genomeFamily.get(1));
} else {
genomeResults.add(babyGene1);
genomeResults.add(babyGene2);
}
// if population shrunk, you can add rest of genes
if (Population.kPopulationLimit < genomes.size()) {
genomeResults.add(genomeFamily.get(3));
genomeResults.add(genomeFamily.get(4));
}
}
}
/**
* Method checkForUndefinedFitness.
* @param g Genome
*/
public void checkForUndefinedFitness(final Genome g) {
if (Double.isNaN(g.currentFitness)) {
g.currentFitness = 0.01f;
}
}
public void writeNextGeneration() {
// just write the top 20
System.out.println("generation " + generation);
for (int i = 0; i < currentPopulation; i++) {
System.out.println(((Genome) genomes.get(i)).toString());
}
System.out.println("generation #" + generation + ", Hit the enter key to continue...\n");
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
try {
System.in.read();
} catch (Exception e) {
e.printStackTrace();
}
*/
}
/**
* Method converged.
* @return boolean
*/
public boolean converged() {
int histogram = 0;
for (int i = 1; i < scores.size(); i++) {
final Double ad = (Double) scores.get(i);
final Double bd = (Double) scores.get(i - 1);
final double a = ad.doubleValue();
final double b = bd.doubleValue();
if ((int) (a * 10000) == (int) (b * 10000)) {
histogram++;
} else {
histogram = 0;
}
}
if (histogram > 5) {
return true;
}
return false;
}
public void writeNextGenerationBest() {
Collections.sort(genomes);
if (genomes.size() > 0) {
System.out.println(((Genome) genomes.get(0)));
scores.add(((EquationGenome) genomes.get(0)).currentFitness);
}
} // End of the Method
} // End of Class