/* ---------------------------------------------------------------------------- ex16.C mbwall 5may95 Copyright (c) 1995-1996 Massachusetts Institute of Technology DESCRIPTION: Illustration of how to use a non-trivial object in the nodes of a tree genome. This example uses points in the nodes. ---------------------------------------------------------------------------- */ #include #include #include #include #include // This is the object that we're going to put in the nodes. Each point has // three coordinates: x,y,z. class Point { public: Point(float x, float y, float z) { _x = x; _y = y; _z = z; } Point(const Point & p) { _x = p._x; _y = p._y; _z = p._z; } Point & operator=(const Point & p) { _x=p._x;_y=p._y;_z=p._z; return *this; } ~Point() {} float x() const { return _x; } float y() const { return _y; } float z() const { return _z; } float x(float val) { return _x=val; } float y(float val) { return _y=val; } float z(float val) { return _z=val; } friend ostream & operator<<(ostream & os, const Point & p){ os << "(" << p._x << ", " << p._y << ", " << p._z << ")"; return os; } protected: float _x, _y, _z; }; // These are the declarations for the functions defined in this file. The // objective function is pretty standard. The tree initializer generates a // random tree. WriteNode is used in the write method for the tree - we // override (specialize) the write method to print out the contents of the // nodes rather than pointers to the contents. float objective(GAGenome &); void TreeInitializer(GAGenome &); void WriteNode(ostream & os, GANode * n); int main(int argc, char **argv) { cout << "Example 16\n\n"; cout << "This example uses a SteadyState GA and Tree genome. It\n"; cout << "tries to maximize the size of the tree genomes that it\n"; cout << "contains. The genomes contain points in its nodes. Two\n"; cout << "different runs are made: first with the swap subtree mutator,\n"; cout << "second with the destructive mutator.\n\n"; cout.flush(); // See if we've been given a seed to use (for testing purposes). When you // specify a random seed, the evolution will be exactly the same each time // you use that seed number. unsigned int seed = 0; for(int ii=1; ii genome(objective); genome.initializer(TreeInitializer); genome.crossover(GATreeGenome::OnePointCrossover); genome.mutator(GATreeGenome::SwapSubtreeMutator); GAPopulation swappop(genome, 50); genome.mutator(GATreeGenome::DestructiveMutator); GAPopulation destpop(genome, 50); GASteadyStateGA ga(genome); ga.nGenerations(10); // first do evolution with subtree swap mutator. ga.population(swappop); cout << "initializing..."; ga.initialize(seed); cout << "evolving for " << ga.nGenerations() << " generations..."; while(!ga.done()){ ga.step(); cout << "."; cout.flush(); } cout << "\n"; genome = ga.statistics().bestIndividual(); cout << "the ga generated a tree with " << genome.size(); cout << " nodes, " << genome.depth() << " levels deep.\n"; // now do evolution with destructive swap mutator ga.population(destpop); cout << "\ninitializing..."; ga.initialize(); cout << "evolving for " << ga.nGenerations() << " generations..."; while(!ga.done()){ ga.step(); cout << "."; cout.flush(); } cout << "\n"; genome = ga.statistics().bestIndividual(); cout << "the ga generated a tree with " << genome.size(); cout << " nodes, " << genome.depth() << " levels deep.\n"; return 0; } /* ---------------------------------------------------------------------------- All we do in this objective function is try to maximize the size of the tree. Just return the tree size. This means that if you run this objective function for many generations you'll run out of memory! There is no limit to tree or list sizes built-in to the GA library. ---------------------------------------------------------------------------- */ float objective(GAGenome & c) { GATreeGenome & chrom = (GATreeGenome &)c; return chrom.size(); } /* ---------------------------------------------------------------------------- This initializer creates a tree of random size (within limits). The maximum number of children any node can have is limited, so is the maximum depth of the tree. We do it recursively. Each point that is inserted into the tree has random contents. The initializer must first destroy any pre-existing tree or else we have a memory leak (the initializer may be called more than once - for example when you re-run the GA). ---------------------------------------------------------------------------- */ const int MAX_DEPTH = 3; const int MAX_CHILDREN = 2; void DoChild(GATreeGenome & tree, int depth) { if(depth >= MAX_DEPTH) return; int n = GARandomInt(0,MAX_CHILDREN); // maximum of 5 children Point p(GARandomFloat(0,25),GARandomFloat(0,25),GARandomFloat(0,25)); tree.insert(p,GATreeBASE::BELOW); for(int i=0; i &tree=(GATreeGenome &)c; // destroy any pre-existing tree tree.root(); tree.destroy(); // create a root node with coordinates 0,0,0, then do the rest. Point p(0,0,0); tree.insert(p,GATreeBASE::ROOT); int n = GARandomInt(0,MAX_CHILDREN); // maximum of 5 children for(int i=0; i * n) { if(!n) return; GANodeBASE * node = (GANodeBASE *)n; os.width(10); os << ((GANode *)node)->contents << " "; os.width(10); if(node->parent) os << ((GANode *)node->parent)->contents << " "; else os << "." << " "; os.width(10); if(node->child) os << ((GANode *)node->child)->contents << " "; else os << "." << " "; os.width(10); if(node->next) os << ((GANode *)node->next)->contents << " "; else os << "." << " "; os.width(10); if(node->prev) os << ((GANode *)node->prev)->contents << "\n"; else os << ".\n"; WriteNode(os, (GANode *)node->child); for(GANodeBASE * tmp=node->next; tmp && tmp != node; tmp=tmp->next){ os.width(10); os << ((GANode *)tmp)->contents << " "; os.width(10); if(tmp->parent) os << ((GANode *)tmp->parent)->contents << " "; else os << "." << " "; os.width(10); if(tmp->child) os << ((GANode *)tmp->child)->contents << " "; else os << "." << " "; os.width(10); if(tmp->next) os << ((GANode *)tmp->next)->contents << " "; else os << "." << " "; os.width(10); if(tmp->prev) os << ((GANode *)tmp->prev)->contents << "\n"; else os << ".\n"; WriteNode(os, (GANode *)tmp->child); } } int GATreeGenome::write(ostream & os) const { os << " node parent child next prev\n"; WriteNode(os, (GANode *)rt); return os.fail() ? 1 : 0; } // If your compiler does not do automatic instantiation (e.g. g++ 2.6.8), // then define the NO_AUTO_INST directive. #ifdef NO_AUTO_INST #include #include #if defined(__GNUG__) template class GATreeGenome; template class GATree; #else GATreeGenome; GATree; #endif #endif