home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 01 Manslow / GPExample / CGP.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-10  |  10.1 KB  |  333 lines

  1. //GPExample
  2. //Copyright John Manslow
  3. //29/09/2001
  4.  
  5. ////////////////////////////////////////////////////////////
  6. //Only when compiling under Windows
  7. #include "stdafx.h"
  8. #define new DEBUG_NEW
  9. ////////////////////////////////////////////////////////////
  10.  
  11. #include "CGP.h"
  12. #include "CGPNode.h"
  13. #include "math.h"
  14. #include "assert.h"
  15. #include "fstream.h"
  16. #include "time.h"
  17.  
  18. //This is a list of prototype "instructions" or nodes that can be used in the construction of genetic "programs"
  19. //or trees. Nodes are declared as automatic variables in their implementation files and register themselves in 
  20. //the prototype list in their constructors. The prorotype list provides a convenient way of copying and creating
  21. //nodes
  22. CGPNode **pPrototypeList=NULL;
  23. unsigned long ulNumberOfPrototypes=0;
  24.  
  25. //A log file is useful for debugging
  26. extern ofstream *pLogFile;
  27.  
  28. CGP::CGP(unsigned long ulNewPopulationSize)
  29. {
  30.     //The constructor allocates memory required for the population and its statistics. It also fills the population
  31.     //with random programs
  32.     unsigned long i;
  33.  
  34.     //Check parameter
  35.     assert(!(ulNewPopulationSize<1));
  36.     ulPopulationSize=ulNewPopulationSize;
  37.  
  38.     //Storage for the programs and their fitness statistics
  39.     pProgram=new CGPNode*[ulPopulationSize];
  40.     pdFitnesses=new double[ulPopulationSize];
  41.  
  42.     //Fill the population with random programs and set their fitnesses to deault values
  43.     for(i=0;i<ulPopulationSize;i++)
  44.     {
  45.         pProgram[i]=pGetRandomSubtree();
  46.         pdFitnesses[i]=0.0;
  47.     }
  48.  
  49.     //Mutation and crossover probabilities need to be much higher in GP than GA
  50.     dMutationProbability=0.1;
  51.     dCrossoverProbability=0.5;
  52.  
  53.     //Reset the count of the number of fitness evaluations
  54.     ulIteration=0;
  55.  
  56.     //Set the fitness statistic of the best program to a large negative number so that it'll be overwritten immediately
  57.     dBestFitness=-1e+100;
  58.     pFittestTree=NULL;
  59. }
  60.  
  61. CGP::~CGP()
  62. {
  63.     //Deallocate memory
  64.     unsigned long i;
  65.     for(i=0;i<ulPopulationSize;i++)
  66.     {
  67.         delete pProgram[i];
  68.     }
  69.     delete []pProgram;
  70.     delete []pdFitnesses;
  71.     delete pFittestTree;
  72. }
  73.  
  74. CGPNode *CGP::pGetRandomSubtree(void)
  75. {
  76.     //This function creates a random program (i.e. tree) 
  77.     unsigned long i;
  78.     double dTotalProbability=0.0;
  79.  
  80.     //Nodes are sampled from the prototype list with probability proportional to their dPrior member varibles.
  81.     //Since we don't know how many elements there'll be in the list, we need to normalise the priors. Obviously,
  82.     //this could be made more efficient by performing the normalisation once in the GP constructor but, since
  83.     //the normalisation is negligible compared to the fitness evalutations, its okay to do it repeatedly here
  84.     double *pdProbabilities=new double[ulNumberOfPrototypes];
  85.     for(i=0;i<ulNumberOfPrototypes;i++)
  86.     {
  87.         dTotalProbability+=pPrototypeList[i]->dPrior;
  88.     }
  89.     for(i=0;i<ulNumberOfPrototypes;i++)
  90.     {
  91.         pdProbabilities[i]=pPrototypeList[i]->dPrior/dTotalProbability;
  92.     }
  93.  
  94.     //Select a node using a "roulette wheel". We'll choose the node that contains this cumulative probability
  95.     double dTargetProbability=double(rand())/double(RAND_MAX);
  96.  
  97.     //The prototype that will be chosen
  98.     unsigned long ulPrototype=0;
  99.  
  100.     //The cumulative probability
  101.     double dAccumulator=pdProbabilities[ulPrototype];
  102.  
  103.     //Repeat until the target cumulative probability lies within the current node
  104.     while(dTargetProbability>dAccumulator+1e-14)
  105.     {
  106.         ulPrototype++;
  107.         dAccumulator+=pdProbabilities[ulPrototype];
  108.     }
  109.     delete []pdProbabilities;
  110.  
  111.     //Return a copy of the selected node
  112.     return pPrototypeList[ulPrototype]->pGetCopy(this);
  113. }
  114.  
  115. CGPNode* CGP::pMutate(CGPNode *pTreeToMutate)
  116. {
  117.     //This function mutates the progarm (tree) passed as the argument. 
  118.     assert(pTreeToMutate);
  119.  
  120.     //First, find out how many nodes there are in the program tree so that we can pick one as the target 
  121.     //of the mutation
  122.     unsigned long ulNumberOfNodesInTree=pTreeToMutate->ulGetNumberOfNodesInSubtree(0);
  123.  
  124.     //Decide whether we're actually going to mutate this program or not
  125.     if(double(rand())/double(RAND_MAX)>dMutationProbability)
  126.     {
  127.         //If not, return the program unmodified
  128.         return pTreeToMutate;
  129.     }
  130.  
  131.     //If there are no nodes in the tree below the top level,
  132.     if(ulNumberOfNodesInTree==0)
  133.     {
  134.         //Delete the entire tree
  135.         delete pTreeToMutate;
  136.  
  137.         //And create a new random one and return it
  138.         pTreeToMutate=pGetRandomSubtree();
  139.         return pTreeToMutate;
  140.     }
  141.     else
  142.     {
  143.         //Otherwise,
  144.         CGPNode **pNode=NULL;
  145.         unsigned long ulNodeCounter=0;
  146.  
  147.         //Pick one of the nodes to mutate
  148.         unsigned long ulNodeToMutate=rand()%ulNumberOfNodesInTree;
  149.  
  150.         //Get a pointer to ut
  151.         pTreeToMutate->GetnthNode(ulNodeCounter,ulNodeToMutate,pNode);
  152.  
  153.         //Delete the existing subtree
  154.         delete *pNode;
  155.  
  156.         //And create a new one
  157.         *pNode=pGetRandomSubtree();
  158.     }
  159.     return pTreeToMutate;
  160. }
  161.  
  162. CGPNode *CGP::pCross(unsigned long ulParentA,unsigned long ulParentB)
  163. {
  164.     //This function produces a child program by crossing the programs at the locations in the population 
  165.     //indicated by the function's arguments. Crossover is performed by randomly selecting crossover
  166.     //points in each of the parent programs trees and swapping the subtrees associated with them
  167.  
  168.     //First, find out how many nodes there are in each of the parents (excluding the root node)
  169.     unsigned long ulNumberOfNodesInParentA=pProgram[ulParentA]->ulGetNumberOfNodesInSubtree(0);
  170.     unsigned long ulNumberOfNodesInParentB=pProgram[ulParentB]->ulGetNumberOfNodesInSubtree(0);
  171.  
  172.     unsigned long ulSourceNode,ulDestinationNode;
  173.  
  174.     //Parent B will form the basis of the child, so take a copy of it
  175.     CGPNode *pChildTree=pProgram[ulParentB]->pGetCopy(this);
  176.  
  177.     //Decide randomly whether crossover will be performed at all
  178.     if(double(rand())/double(RAND_MAX)>dCrossoverProbability)
  179.     {
  180.         //If not, return the child tree unmodified
  181.         return pChildTree;
  182.     }
  183.  
  184.     //If there are enough nodes in parent A to select a crossover point
  185.     if(ulNumberOfNodesInParentA>0)
  186.     {
  187.         //Select a crossover site in parent A
  188.         ulSourceNode=rand()%(ulNumberOfNodesInParentA);
  189.     }
  190.     else
  191.     {
  192.         //If not, crossover will not be performed, so return the child tree unmodified
  193.         return pChildTree;
  194.     }
  195.  
  196.     //If there are enough nodes in parent B to perform crossover
  197.     if(ulNumberOfNodesInParentB>0)
  198.     {
  199.         //Select a crossover site
  200.         ulDestinationNode=rand()%(ulNumberOfNodesInParentB);
  201.     }
  202.     else
  203.     {
  204.         //Otherwise, crossover cannot be performed, so return the child tree unmodified 
  205.         return pChildTree;
  206.     }
  207.  
  208.     CGPNode **pNode=NULL;
  209.     unsigned long ulNodeCounter;
  210.  
  211.     //Get a pointer to the node at the child tree's crossover site
  212.     ulNodeCounter=0;
  213.     pChildTree->GetnthNode(ulNodeCounter,ulDestinationNode,pNode);
  214.  
  215.     //Delete the existing subtree
  216.     delete *pNode;
  217.     CGPNode **pNewSubtree=NULL;
  218.  
  219.     //Get a pointer to the node at parent A's crossover site
  220.     ulNodeCounter=0;
  221.     pProgram[ulParentA]->GetnthNode(ulNodeCounter,ulSourceNode,pNewSubtree);
  222.  
  223.     //Set the node at the child's crossover site to point to a copy of the subtree at parent A's
  224.     //crossover site
  225.     *pNode=(*pNewSubtree)->pGetCopy(this);
  226.     return pChildTree;
  227. }
  228.  
  229.  
  230. CGPNode *CGP::pGetChromosomeForEvaluation(void)
  231. {
  232.     //Only this function and SetFitness need to be called to make this GP class work. This function
  233.     //selects two parents from the population, mates them to produce a child program by crossover,
  234.     //mutates the child and places it in the population in place of the least fit parent. A pointer to the 
  235.     //child is returned so that its fitness can be evaluated
  236.  
  237.     //If we've not yet evaluated the fitness of every program in the population, select the next one in line.
  238.     //At this stage we don't need to create any new programs because we've not yet tried all the ones
  239.     //we have
  240.     if(ulIteration<ulPopulationSize)
  241.     {
  242.         ulWorkingTree=ulIteration;
  243.     }
  244.     else
  245.     {
  246.         //If we have measured the fitness of all programs in the population, we need to create new ones
  247.         //by applying the crossover and mutation operators
  248.         CGPNode *pChild;
  249.         unsigned long ulParentA,ulParentB;
  250.  
  251.         //The crossover operator requires two different parents, so select them at random from the 
  252.         //population
  253.         ulParentA=rand()%ulPopulationSize;
  254.         do
  255.         {
  256.             ulParentB=rand()%ulPopulationSize;
  257.         }
  258.         while(ulParentA==ulParentB);
  259.         ulWorkingTree=ulParentA;
  260.  
  261.         //Produce a child program by crossing the parents
  262.         pChild=pCross(ulParentA,ulParentB);
  263.  
  264.         //Mutate the child
  265.         pChild=pMutate(pChild);
  266.  
  267.         //If parent A was fittest
  268.         if(pdFitnesses[ulParentA]>pdFitnesses[ulParentB])
  269.         {
  270.             //the new program will replace parent B
  271.             delete pProgram[ulParentB];
  272.             pProgram[ulParentB]=pChild;
  273.             ulWorkingTree=ulParentB;
  274.         }
  275.         else
  276.         {
  277.             //otherwise, itwill replace parent A
  278.             delete pProgram[ulParentA];
  279.             pProgram[ulParentA]=pChild;
  280.             ulWorkingTree=ulParentA;
  281.         }
  282.     }
  283.     //Return a pointer to the newly created program so that its fitness can be evaluated
  284.     return pProgram[ulWorkingTree];
  285. }
  286.  
  287. void CGP::SetFitness(double dNewFitness)
  288. {
  289.     //Keep track of the number of fitness evaluations
  290.     ulIteration++;
  291.  
  292.     //Record the fitness of the rpogram that was just evaluated
  293.     pdFitnesses[ulWorkingTree]=dNewFitness;
  294.  
  295.     //If the program performed bettwe than the best so far
  296.     if(dNewFitness-dBestFitness>0)
  297.     {
  298.         //Record its fitness and take a copy of the program
  299.         dBestFitness=dNewFitness;
  300.         if(pFittestTree!=NULL)
  301.         {
  302.             delete pFittestTree;
  303.         }
  304.         pFittestTree=pProgram[ulWorkingTree]->pGetCopy(this);
  305.         TRACE("New fittest:\t%+16.3lf\n",dBestFitness);
  306.  
  307.         //Also, dump some info to the log file        
  308.         char pString[10000];
  309.         sprintf(pString,"CGP::NewBestFitness:    Chromosome %5u has fitness %+18.3lf on iteration %u",ulWorkingTree,dNewFitness,ulIteration);
  310.         *pLogFile<<pString;
  311.         *pLogFile<<"\n";
  312.         sprintf(pString,"");
  313.         pProgram[ulWorkingTree]->psGetString(pString);
  314.         *pLogFile<<pString;
  315.         *pLogFile<<"\n";
  316.         sprintf(pString,"");
  317.         _strtime(pString);
  318.         *pLogFile<<pString;
  319.         *pLogFile<<"\n";
  320.     }
  321. }
  322.  
  323. CGPNode *CGP::pGetBestChromosome()
  324. {
  325.     //Returns a pointer to the best program found so far. It is not a copy, so don't delete it!
  326.     return pFittestTree;
  327. }
  328.  
  329. double CGP::dGetBestPerformance(void)
  330. {
  331.     //Returns the best performance so far achieved
  332.     return dBestFitness;
  333. }