/* ---------------------------------------------------------------------------- ex5.C mbwall 10dec94 Copyright 1995-1996 Massachusetts Institute of Technology DESCRIPTION: Example program for a composite genome derived from the GAGenome and containing a 2DBinStr genome and a Bin2Dec genome. This program uses a steady-state GA to match a 2D pattern in the 2DBinStr part of the genome and a sequence of numbers in the Bin2Dec part. In this example we derive a new genome and a new crossover object to be used with it. All of the operators (initialization, mutation, crossover, comparison, and objective) are defined as member functions but are also override-able on any instance of the new genome. ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include // This is the class definition for the new genome. The default operators are // defined as static member functions. They can be overridden if necessary by // anyone making an instance of this class - you don't need to derive a new // class to change the behaviour of one or two of its operators. class CompositeGenome : public GAGenome { public: GADefineIdentity("CompositeGenome", 201); static void CompositeInitializer(GAGenome&); static int CompositeMutator(GAGenome&, float); static float CompositeComparator(const GAGenome&, const GAGenome&); static int CompositeCrossover(const GAGenome&, const GAGenome&, GAGenome*, GAGenome*); public: CompositeGenome(int, int, GABin2DecPhenotype&, GAGenome::Evaluator f=NULL, void* u=NULL); CompositeGenome(const CompositeGenome & orig); CompositeGenome& operator=(const GAGenome& g); virtual ~CompositeGenome(); 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; GA2DBinaryStringGenome & binstr() const {return *str;} GABin2DecGenome & bin2dec() const {return *b2d;} protected: GA2DBinaryStringGenome *str; GABin2DecGenome *b2d; }; // Member functions for the composite genome object CompositeGenome:: CompositeGenome(int element, int bond, GABin2DecPhenotype& p, GAGenome::Evaluator f, void* u) : GAGenome( CompositeInitializer, CompositeMutator, CompositeComparator) { evaluator(f); userData(u); crossover(CompositeCrossover); str = new GA2DBinaryStringGenome(element, bond, f, u); b2d = new GABin2DecGenome(p, f, u); } CompositeGenome::CompositeGenome(const CompositeGenome & orig) { str = new GA2DBinaryStringGenome(orig.binstr()); b2d = new GABin2DecGenome(orig.bin2dec()); copy(orig); } CompositeGenome& CompositeGenome::operator=(const GAGenome& g) { copy(g); return *this; } CompositeGenome::~CompositeGenome() { delete str; delete b2d; } GAGenome* CompositeGenome::clone(GAGenome::CloneMethod) const { return new CompositeGenome(*this); } void CompositeGenome::copy(const GAGenome & c){ if(&c != this && sameClass(c)){ GAGenome::copy(c); CompositeGenome & bc = (CompositeGenome &)c; str->copy(*(bc.str)); b2d->copy(*(bc.b2d)); } } int CompositeGenome::equal(const GAGenome& g) const { CompositeGenome& genome = (CompositeGenome&)g; return ((*str == *genome.str) && (*b2d == *genome.b2d)); } int CompositeGenome::read(istream & is) { is >> *str >> *b2d; return is.fail() ? 1 : 0; } int CompositeGenome::write(ostream & os) const { int i,j; for(j=0; jheight(); j++){ for(i=0; iwidth(); i++) os << (str->gene(i,j) == 1 ? '*' : ' ') << " "; os << "\n"; } os << "\n" << *b2d << "\n"; return os.fail() ? 1 : 0; } // These are the default initialization, mutation, and comparator operators for // this genome class. They are defined as static functions of the composite // genome class and they're defaults for the class. But they can be overridden // on any instance of the genome. // The initializer just calls the initializer for each of the genomes that are // in the composite genome. // I would have used simply 'Initializer', 'Mutator', etc rather than // 'CompositeInitializer' but old versions of g++ are brain-dead and don't // get the encapsulation properly. void CompositeGenome::CompositeInitializer(GAGenome & c) { CompositeGenome & child = (CompositeGenome &)c; child.binstr().initialize(); child.bin2dec().initialize(); child._evaluated = gaFalse; } // The mutator just calls the mutator for each of the component genomes. int CompositeGenome::CompositeMutator(GAGenome & c, float pmut) { CompositeGenome & child = (CompositeGenome &)c; int nmut = child.binstr().mutate(pmut) + child.bin2dec().mutate(pmut); if(nmut) child._evaluated = gaFalse; return nmut; } // The comparator just calls the comparators for each of the component genomes, // then averages the score. float CompositeGenome::CompositeComparator(const GAGenome& a, const GAGenome& b) { CompositeGenome& sis = (CompositeGenome &)a; CompositeGenome& bro = (CompositeGenome &)b; return 0.5 * (sis.binstr().compare(bro) + sis.bin2dec().compare(bro)); } // The crossover operator invokes the crossover for each of the genomes in the // composite genome. We use sexual crossover only, and we do not test to see // if no crossover has been assigned. int CompositeGenome:: CompositeCrossover(const GAGenome& a, const GAGenome& b, GAGenome* c, GAGenome* d){ CompositeGenome& mom = (CompositeGenome&)a; CompositeGenome& dad = (CompositeGenome&)b; int n=0; GAGenome::SexualCrossover strcross = mom.str->sexual(); GAGenome::SexualCrossover b2dcross = mom.b2d->sexual(); if(c && d){ CompositeGenome& sis = (CompositeGenome&)*c; CompositeGenome& bro = (CompositeGenome&)*d; (*strcross)(mom.binstr(), dad.binstr(), &sis.binstr(), &bro.binstr()); (*b2dcross)(mom.bin2dec(),dad.bin2dec(), &sis.bin2dec(), &bro.bin2dec()); sis._evaluated = gaFalse; bro._evaluated = gaFalse; n = 2; } else if(c){ CompositeGenome& sis = (CompositeGenome&)*c; (*strcross)(mom.binstr(), dad.binstr(), &sis.binstr(), 0); (*b2dcross)(mom.bin2dec(), dad.bin2dec(), &sis.bin2dec(), 0); sis._evaluated = gaFalse; n = 1; } else if(d){ CompositeGenome& bro = (CompositeGenome&)*d; (*strcross)(mom.binstr(), dad.binstr(), 0, &bro.binstr()); (*b2dcross)(mom.bin2dec(), dad.bin2dec(), 0, &bro.bin2dec()); bro._evaluated = gaFalse; n = 1; } return n; } // This object is a container for the data that we are supposed to match in // our objective function. typedef struct _CompositeData { short ** str; float * b2d; } CompositeData; // In this objective function we try to match the pattern in the 2D part of the // genome and match the sequence of values in the binary-to-decimal part of the // genome. The overall score is the sum of both parts. float Objective(GAGenome & g) { CompositeGenome & genome = (CompositeGenome &)g; GA2DBinaryStringGenome & str = genome.binstr(); GABin2DecGenome & b2d = genome.bin2dec(); int i; short **pattern = ((CompositeData *)g.userData())->str; float val1=0.0; for(i=0; ib2d; float val2=b2d.nPhenotypes(); for(i=0; i= argc){ cerr << argv[0] << ": you must specify a filename.\n"; exit(1); } else{ sprintf(filename1, argv[i]); continue; } } else if(strcmp("values", argv[i]) == 0){ if(++i >= argc){ cerr << argv[0] << ": you must specify a filename.\n"; exit(1); } else{ sprintf(filename2, argv[i]); continue; } } else if(strcmp("seed", argv[i]) == 0){ if(++i < argc) continue; continue; } else { cerr << argv[0] << ": unrecognized arguement: " << argv[i] << "\n\n"; cerr << "valid arguements include standard GAlib flags plus:\n"; cerr << " graph\tname of graph filename (" << filename1 << ")\n"; cerr << " values\tname of values filename (" << filename2 << ")\n"; cerr << "\n"; exit(1); } } ifstream infile; // First we read in the pattern for the 2DBinStr genome. // File format is pretty simple: // two integers that give the height then width of the matrix, // then the matrix of 1's and 0's (with whitespace inbetween). infile.open(filename1, ios :: in); if(!infile){ cerr << "Cannot open " << filename1 << " for input.\n"; exit(1); } int height, width; infile >> height; infile >> width; short **target = new short*[width]; for(i=0; i> target[i][j]; infile.close(); // Now we read in a sequence of numbers that the Bin2Dec genome is supposed // to match for its objective. File format is // pretty simple: a single integer that tells how many numbers will follow, // then the sequence of numbers. infile.open(filename2, ios :: in); if(!infile){ cerr << "Cannot open " << filename2 << " for input.\n"; exit(1); } infile >> n; float *sequence = new float[n]; for(i=0; i> sequence[i]; infile.close(); // Print out the pattern and sequence. cout << "input pattern:\n"; for(j=0; j