/* ---------------------------------------------------------------------------- ex14.C mbwall 29apr95 Copyright (c) 1995-1996 Massachusetts Institute of Technology DESCRIPTION: Example program for a composite genome derived from the GAGenome and containing a list of lists. This example shows how to derive your own genome class and illustrates the use of one of the template genomes (GAListGenome) from the GAlib. ---------------------------------------------------------------------------- */ #include #include #include #include #include // Here we specify how big the lists will be and how many lists will be in each // composite genome. These are the default values - you can change them from // the command line. Beware that this program will break if nrobots is bigger // than the size of the lists. int listsize = 10; int nrobots = 6; // This is the class definition. We override the methods of the base class and // define a few methods of our own to access the protected members. The list // genomes in the composite genome are assigned the 'List' operators // by default (they can be overridden by using the 'Operator' members on the // lists explicitly). // The ID can be any number over 200 (IDs under 200 are reserved for use by // GAlib objects). class RobotPathGenome : public GAGenome { public: GADefineIdentity("RobotPathGenome", 251); static void Initializer(GAGenome&); static int Mutator(GAGenome&, float); static float Comparator(const GAGenome&, const GAGenome&); static float Evaluator(GAGenome&); static void PathInitializer(GAGenome&); static int Crossover(const GAGenome&, const GAGenome&, GAGenome*, GAGenome*); public: RobotPathGenome(int nrobots, int pathlength); RobotPathGenome(const RobotPathGenome & orig) { n=l=0; list=0; copy(orig); } RobotPathGenome operator=(const GAGenome & arg) { copy(arg); return *this; } virtual ~RobotPathGenome(); virtual GAGenome *clone(GAGenome::CloneMethod) const ; virtual void copy(const GAGenome & c); virtual int equal(const GAGenome& g) const; virtual int read(istream & is); virtual int write(ostream & os) const ; GAListGenome & path(const int i){return *list[i];} int npaths() const { return n; } int length() const { return l; } protected: int n, l; GAListGenome **list; }; RobotPathGenome::RobotPathGenome(int nrobots, int pathlength) : GAGenome(Initializer, Mutator, Comparator){ evaluator(Evaluator); crossover(Crossover); n = nrobots; l = pathlength; list = (n ? new GAListGenome * [n] : (GAListGenome **)0); for(int i=0; i; list[i]->initializer(PathInitializer); list[i]->mutator(GAListGenome::SwapMutator); } } void RobotPathGenome::copy(const GAGenome& g) { if(&g != this && sameClass(g)){ GAGenome::copy(g); // copy the base class part RobotPathGenome & genome = (RobotPathGenome &)g; if(n == genome.n){ for(int i=0; icopy(*genome.list[i]); } else{ int i; for(i=0; i * [n]; for(i=0; i *)genome.list[i]->clone(); } } } RobotPathGenome::~RobotPathGenome(){ for(int i=0; iequal(*genome.list[i]); return flag; } int RobotPathGenome::read(istream & is) { for(int i=0; i> *(list[i]); return is.fail() ? 1 : 0; } int RobotPathGenome::write(ostream & os) const { for(int i=0; i::PartialMatchCrossover(mom.path(i), dad.path(i), &sis.path(i), &bro.path(i)); sis._evaluated = gaFalse; bro._evaluated = gaFalse; n=2; } else if(c) { RobotPathGenome& sis = (RobotPathGenome &)*c; for(int i=0; i::PartialMatchCrossover(mom.path(i), dad.path(i), &sis.path(i), 0); sis._evaluated = gaFalse; n=1; } else if(d) { RobotPathGenome& bro = (RobotPathGenome &)*d; for(int i=0; i::PartialMatchCrossover(mom.path(i), dad.path(i), 0, &bro.path(i)); bro._evaluated = gaFalse; n=1; } return n; } // This is the initialization operator for our lists. We create a list that is // n long and whose nodes contain numbers in sequence. // The first thing to do in the initializer is to clear out any old // contents - we do not want to build our new list on a previously existing // one! Notice that we have to cast the genome into the type of // genome we're using (in this case a list). The GA always operates on // generic genomes. // All of our lists must be the same length since we're going to use the // ordered crossover operators. void RobotPathGenome::PathInitializer(GAGenome & c){ GAListGenome & list = (GAListGenome &)c; // We must first destroy any pre-existing list. while(list.head()) list.destroy(); // Insert the head of the list. This node has a random number in it, but the // number is in a range different than those in the rest of the list. This // way we'll be able to see how the lists get scrambled up. Since we're using // ordered crossover (see the header file) we should never end up with more // than one node in each list that has a given value. list.insert(0, GAListBASE::HEAD); // Loop through n times and append nodes onto the end of the list. int i; for(i=0; i::write(ostream & os) const { int *cur, *head; GAListIter iter(*this); if((head=iter.head()) != 0) os << *head << " "; for(cur=iter.next(); cur && cur != head; cur=iter.next()) os << *cur << " "; return os.fail() ? 1 : 0; } int main(int argc, char *argv[]) { cout << "Example 14\n\n"; cout << "This example shows how to create a genome that contains\n"; cout << "a list of lists. We create a composite genome that has\n"; cout << "lists in it. Each list has some nodes, only one of which\n"; cout << "contains the number 0. The objective is to move the node with\n"; cout << "number 0 in it to the nth position where n is the number of the\n"; cout << "list within the composite genome.\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. int i; for(i=1; i= argc){ cerr << argv[0] << ": number of robots needs a value.\n"; exit(1); } else{ nrobots = atoi(argv[i]); continue; } } else if(strcmp("pl", argv[i]) == 0){ if(++i >= argc){ cerr << argv[0] << ": path length needs a value.\n"; exit(1); } else{ listsize = atoi(argv[i]); continue; } } else if(strcmp(argv[i],"seed") == 0) { if(++i >= argc){ cerr << argv[0] << ": seed needs a value.\n"; exit(1); } else { GARandomSeed((unsigned int)atoi(argv[i])); } } else if(strcmp("help",argv[i]) == 0){ cerr << "valid arguements include standard GAlib arguments plus:\n"; cerr << " nr\tnumber of robots (" << nrobots << ")\n"; cerr << " pl\tlength of the paths (" << listsize << ")\n"; cerr << "\n"; exit(1); } } if(listsize < nrobots) { cerr << "path length must be greater than the number of robots.\n"; exit(1); } RobotPathGenome genome(nrobots, listsize); GASteadyStateGA ga(genome); ga.parameters(argc,argv); ga.evolve(); genome.initialize(); cout << "a randomly-generated set of paths:\n" << genome << endl; cout << "the ga generated:\n" << ga.statistics().bestIndividual() << "\n"; return 0; } // If your compiler does not do automatic instantiation (e.g. g++ 2.6.8), // then define the NO_AUTO_INST directive. This will force the instantiation // of the template classes that we use. For some compilers (e.g. metrowerks) // this must come after any specializations or you'll get 'multiply-defined' // errors when you compile. #ifdef NO_AUTO_INST #include #include #if defined(__GNUG__) template class GAList; template class GAListGenome; #else GAList; GAListGenome; #endif #endif