machine_learning/geneticalgorithm.cpp
/*******************************************************
* Copyright (c) 2014, ArrayFire
* All rights reserved.
*
* This file is distributed under 3-clause BSD license.
* The complete license agreement can be obtained at:
* http://arrayfire.com/licenses/BSD-3-Clause
********************************************************/
#include <arrayfire.h>
#include <climits>
#include <cstring>
#include <ctime>
#include <cstdio>
using namespace af;
static const float DefaultTopFittest = 0.5;
array update(const array& searchSpace, const array& sampleX, const array& sampleY, const int n)
{
return searchSpace(sampleY*n + sampleX);
}
array selectFittest(const array& sampleZ, const int nSamples,
const float topFit = DefaultTopFittest)
{
//pick top fittest
array indices, values;
sort(values, indices, sampleZ);
int topFitElem = topFit*nSamples;
int n = indices.elements();
return (n > topFitElem) ? indices(seq(n - topFitElem, n-1)) : indices;
}
void reproduce(array& searchSpace, array& sampleX, array& sampleY, array& sampleZ, const int nSamples, const int n)
{
//Get fittest parents
array selection = selectFittest(sampleZ, nSamples);
array parentsX = sampleX(selection);
array parentsY = sampleY(selection);
int bits = (int)log2(n);
//Divide selection in two
array parentsX1 = parentsX.rows(0, parentsX.elements() / 2 - 1);
array parentsX2 = parentsX.rows(parentsX.elements() / 2, parentsX.elements() - 1);
array parentsY1 = parentsY.rows(0, parentsY.elements() / 2 - 1);
array parentsY2 = parentsY.rows(parentsY.elements() / 2, parentsY.elements() - 1);
//Get crossover points (at which bit to crossover) and construct bit masks from them
array crossover = randu(nSamples / 4, u32) % bits;
array lowermask = (1 << crossover) - 1;
array uppermask = INT_MAX - lowermask;
//Create children as the cross between two parents
array childrenX1 = (parentsX1 & uppermask) + (parentsX2 & lowermask);
array childrenY1 = (parentsY1 & uppermask) + (parentsY2 & lowermask);
array childrenX2 = (parentsX2 & uppermask) + (parentsX1 & lowermask);
array childrenY2 = (parentsY2 & uppermask) + (parentsY1 & lowermask);
//Join two new sets
sampleX = join(0, childrenX1, childrenX2);
sampleY = join(0, childrenY1, childrenY2);
//Create mutant children
array mutantX = sampleX;
array mutantY = sampleY;
//Flip a random bit to vary the gene pool
mutantX = mutantX ^ (1 << (randu(nSamples / 2, u32) % bits));
mutantY = mutantY ^ (1 << (randu(nSamples / 2, u32) % bits));
sampleX = join(0, sampleX, mutantX);
sampleY = join(0, sampleY, mutantY);
//Update the value of each sample with the new coordinates
sampleZ = update(searchSpace, sampleX, sampleY, n);
}
void initSamples(array& searchSpace, array& sampleX, array& sampleY, array& sampleZ, const int nSamples, const int n)
{
setSeed(time(NULL));
sampleX = randu(nSamples, u32) % n;
sampleY = randu(nSamples, u32) % n;
sampleZ = update(searchSpace, sampleX, sampleY, n);
}
void init(array& searchSpace, array& searchSpaceXDisplay, array& searchSpaceYDisplay, array& sampleX, array& sampleY, array& sampleZ, const int nSamples, const int n)
{
//initialize space
searchSpace = range(dim4(n/2, n/2), 0) + range(dim4(n/2, n/2), 1);
searchSpace = join(0, searchSpace, flip(searchSpace, 0));
searchSpace = join(1, searchSpace, flip(searchSpace, 1));
//initialize display data
searchSpaceXDisplay = iota(dim4(n, 1), dim4(1, n));
searchSpaceYDisplay = iota(dim4(1, n), dim4(n, 1));
//initalize searchers
initSamples(searchSpace, sampleX, sampleY, sampleZ, nSamples, n);
}
void reproducePrint(float& currentMax,
array& searchSpace, array& sampleX, array& sampleY, array& sampleZ,
const float trueMax, const int nSamples, const int n)
{
if (currentMax < trueMax * 0.99) {
float maximum = max<float>(sampleZ);
array whereM = where(sampleZ == maximum);
if (maximum < trueMax * 0.99) {
printf("Current max at ");
} else {
printf("\nMax found at ");
}
printf("(%d,%d): %f (trueMax %f)\n",
sampleX(whereM).scalar<unsigned int>(),
sampleY(whereM).scalar<unsigned int>(), maximum, trueMax);
currentMax = maximum;
reproduce(searchSpace, sampleX, sampleY, sampleZ, nSamples, n);
}
}
void geneticSearch(bool console, const int nSamples, const int n)
{
array searchSpaceXDisplay = 0;
array searchSpaceYDisplay = 0;
array searchSpace;
array sampleX;
array sampleY;
array sampleZ;
init(searchSpace, searchSpaceXDisplay, searchSpaceYDisplay,
sampleX, sampleY, sampleZ, nSamples, n);
float trueMax = max<float>(searchSpace);
float maximum = -trueMax;
if (!console) {
af::Window win(1600, 800, "Arrayfire Genetic Algorithm Search Demo");
win.grid(1, 2);
do {
reproducePrint(maximum, searchSpace, sampleX, sampleY, sampleZ,
trueMax, nSamples, n);
win(0,0).setAxesTitles("IdX", "IdY", "Search Space");
win(0,1).setAxesTitles("IdX", "IdY", "Search Space");
win(0,0).surface(searchSpaceXDisplay, searchSpaceYDisplay, searchSpace);
win(0,1).scatter(sampleX.as(f32), sampleY.as(f32), sampleZ.as(f32), AF_MARKER_CIRCLE);
win.show();
} while (!win.close());
} else {
do {
reproducePrint(maximum, searchSpace, sampleX, sampleY, sampleZ,
trueMax, nSamples, n);
} while (maximum < trueMax * 0.99);
}
}
int main(int argc, char** argv)
{
bool console = false;
const int n = 32;
const int nSamples = 16;
if (argc > 2 || (argc == 2 && strcmp(argv[1], "-"))) {
printf("usage: %s [-]\n", argv[0]);
return -1;
} else if (argc == 2 && argv[1][0] == '-') {
console = true;
}
try {
printf("** ArrayFire Genetic Algorithm Search Demo **\n\n");
printf("Search for trueMax in a search space where the objective function is defined as :\n\n");
printf("SS(x ,y) = min(x, n - (x + 1)) + min(y, n - (y + 1))\n\n");
printf("(x, y) belongs to RxR; R = [0, n); n = %d\n\n", n);
if (!console) {
printf("The left figure shows the objective function.\n");
printf("The figure on the right shows current generation's parameters and function values.\n\n");
}
geneticSearch(console, nSamples, n);
} catch (af::exception& e) {
fprintf(stderr, "%s\n", e.what());
}
return 0;
}