/* * li-toolkit.cc -- Lithium Genetic Programming Toolkit * # Copyright (c) 2006 Henry Strickland -- in the domain # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # (* http://www.opensource.org/licenses/mit-license.php *) * */ #include "li.h" #include namespace Li { int trace; int verbose; int quiet; int noTest; int extendPrecisionPrintingDoubles; uint randomSeed= 1; void FATAL(const char* fmt, const char* a, const char* b, const char* c) { fprintf(stderr, "\n***** FATAL: "); fprintf(stderr, fmt, a, b, c ); fprintf(stderr, " *****\n"); abort(); } void FATAL(const char* fmt, string a, string b, string c) { FATAL(fmt, a.c_str(), b.c_str(), c.c_str()); } SAYER *Say; int DontSay(FILE* stream, const char* format, ...) { return 0; } //////////////////////////////// // // Logger Logger::Logger(string _banner, FILE* fd) : fd(fd) , enc_banner( BraceEncodeStr(_banner) ) { } Logger::Logger(string _banner, string filename, string mode) : fd( fopen( filename.c_str(), mode.c_str() ) ) , enc_banner( BraceEncodeStr(_banner) ) { if (not this->fd) FATAL("cannot fopen filename: %s mode: %s banner: %s", filename, mode, enc_banner ); } Logger::~Logger() { if (fd) fflush(fd); } void Logger::writeAccumulatedItems(string s) { if (fd) { string b= LabelScope::Decorate(enc_banner); int e= fprintf( fd, "<:\t%s\t%s\t:>\n", b.c_str(), s.c_str() ); if (e<0) FATAL( "ERROR DURING WRITING OUTPUT" ); e= fflush(fd); if (e<0) FATAL( "ERROR DURING WRITING OUTPUT" ); } } //////////////////////////////// // // PartialLoggerLine PartialLoggerLine::PartialLoggerLine( Logger& _logger, string _label ) : logger(_logger) , accum( BraceEncodeStr(_label) ) { } PartialLoggerLine& PartialLoggerLine::operator<< (string item) { accum = accum + "\t" + BraceEncodeStr(item); return *this; } PartialLoggerLine::~PartialLoggerLine( ) { logger.writeAccumulatedItems(accum); } /// LabelScope LabelScope::LabelScope(string _label) { saved= Current; Current += BraceEncodeStr(_label) + ","; } LabelScope::~LabelScope() { Current= saved; } string LabelScope::Decorate(string _suffix) { return Current.size() ? Current + _suffix : string(",") + _suffix; } string LabelScope::Current; TEST(LabelScope) { AssertStrEq( "", LabelScope::Current ); AssertStrEq( ",abc", LabelScope::Decorate("abc") ); { auto LabelScope object1( "one" ); AssertStrEq( "one,", LabelScope::Current ); AssertStrEq( "one,suf", LabelScope::Decorate("suf") ); { auto LabelScope object2( "two" ); AssertStrEq( "one,two,", LabelScope::Current ); AssertStrEq( "one,two,suf", LabelScope::Decorate("suf") ); } AssertStrEq( "one,suf", LabelScope::Decorate("suf") ); } AssertStrEq( ",xyz", LabelScope::Decorate("xyz") ); } /// ParamScope ParamScope::ParamScope(string _pname, string _pvalue) { saved= Current; Current.push_back( _pname + "=" + _pvalue ); } ParamScope::~ParamScope() { Current= saved; } vector ParamScope::Decorate(vector _suffix) { vector z= Current; for (int i=0; i<_suffix.size(); i++) { z.push_back(_suffix.at(i)); } return z; } vector ParamScope::Current; TEST(ParamScope) { AssertEq( 0, ParamScope::Current.size() ); { auto ParamScope object1( "Color", "Red" ); AssertEq( 1, ParamScope::Current.size() ); AssertStrEq( "Color=Red", ParamScope::Current.at(0) ); { auto ParamScope object2( "Size", "4096" ); AssertEq( 2, ParamScope::Current.size() ); AssertStrEq( "Color=Red", ParamScope::Current.at(0) ); AssertStrEq( "Size=4096", ParamScope::Current.at(1) ); } AssertEq( 1, ParamScope::Current.size() ); AssertStrEq( "Color=Red", ParamScope::Current.at(0) ); } AssertEq( 0, ParamScope::Current.size() ); } /// SubstScope SubstScope::SubstScope(char _pname, string _pvalue) { saved= Current; Current.set( _pname, _pvalue ); } SubstScope::~SubstScope() { Current= saved; } vector SubstScope::Subst(vector words) { vector z; for (int i=0; i 0 ); return s_sum / s_count; } double Sampling::stdDev() const { Assert( s_count > 0 ); // Wikipedia, Standard_deviation, "Rapid Calculation methods", // Sample standard deviation "s" from running sums. return sqrt( ( s_count * s_sum_squares - s_sum*s_sum ) / s_count / (s_count-1) ); } double Sampling::stdErr() const { Assert( s_count > 0 ); // Wikipedia, Standard_error_(Statistics), // "standard error of a sample from a population" return stdDev() / sqrt(s_count); } double Sampling::sumSquares() const { return s_sum_squares; } uint Sampling::count() const { return s_count; } #if 0 ------ not needed? -------- Sampling::operator double() const { Assert( s_count==1 ); return s_sum; } #endif template double Double(const T& x) { return (double)x; } template <> double Double(const Sampling& x) { return x.mean(); } string Sampling::toString() const { if ( s_ok_to_add ) { return ToString(mean()) // + "~" + ToString( sqrt(s_sum_squares/s_count) ) + "~" + ToString(s_count) + "~" + ToString( stdDev() ) // + "~" + ToString( stdErr() ) ; } else { Assert( s_count == 1 ); return ToString(s_sum) + "~~"; } } TEST(Sampling) { Sampling x; AssertEq( 0.0, x.mean() ); x= 3.14; AssertEq( 3.14, x.mean() ); Sampling xx= x; AssertEq( 3.14, xx.mean() ); Sampling xxx; xxx= x; AssertEq( 3.14, xxx.mean() ); Sampling z; z.clearForInsertions(); z.insertSample( 9 ); z.insertSample( 10 ); z.insertSample( 11 ); AssertEq( 10.0, z.mean() ); AssertEq( 9*9+10*10+11*11, z.sumSquares() ); AssertEq( 3, z.count() ); // Assert( z.stdDev() < 1.1 ); Sampling zz= z; AssertEq( 10.0, zz.mean() ); AssertEq( 9*9+10*10+11*11, zz.sumSquares() ); AssertEq( 3, zz.count() ); Sampling zzz; zzz= z; AssertEq( 10.0, zzz.mean() ); AssertEq( 9*9+10*10+11*11, zzz.sumSquares() ); AssertEq( 3, zzz.count() ); } //////////////////////////////// // // Logger string Trim(string x) { while ( x.size() && x.at(0)==' ' ) { x= x.substr(1); } while ( x.size() && x.at( x.size()-1 )==' ' ) { x= x.substr(0, x.size()-1 ); } return x; } string ReplaceCharWithString(string src, char c, string replace) { string z; for (uint i=0; i vector SplitWordsOnChar(string s) { vector z; int n= s.size(); int i= 0; while (i SplitWords(string s) { vector z; int n= s.size(); int i= 0; while (i'9') ) { // allow '{' to pass z.push_back( c ); } else if ( c=='}') { // allow '}' to pass z.push_back( c ); } else { // encode the char unsigned ones= byte(c) % 10; unsigned tens= ( byte(c) / 10 ) % 10; unsigned huns= byte(c) / 100; z.push_back( '{' ); if (huns) z.push_back( '0' + huns ); if (tens) z.push_back( '0' + tens ); z.push_back( '0' + ones ); z.push_back( '}' ); } } return z; } TEST(BraceEncodeStr) { string s= BraceEncodeStr( "" ); AssertStrEq( "", s ); s= BraceEncodeStr( string("\0", 1) ); AssertStrEq( "{0}", s ); s= BraceEncodeStr( string("\0\xff\0", 3) ); AssertStrEq( "{0}{255}{0}", s ); s= BraceEncodeStr( string("[Hello}") ); AssertStrEq( "[Hello}", s ); s= BraceEncodeStr( string("{Hello}") ); AssertStrEq( "{Hello}", s ); s= BraceEncodeStr( string("{1}{2}{3}") ); AssertStrEq( "{123}1}{123}2}{123}3}", s ); } string BraceDecodeStr( string s ) { const char* t= s.c_str(); string z; while (*t) { if (*t=='{' && '0'<=t[1] && t[1]<='9') { int x= 0; ++t; while ( *t && *t != '}' ) { x = 10*x + *t-'0'; ++t; } if (*t) ++t; z.push_back( char(x) ); } else { z.push_back( *t++ ); } } return z; } TEST(BraceDecodeStr) { string s= BraceEncodeStr( "" ); AssertStrEq( "", s ); string z= BraceDecodeStr( s ); AssertStrEq( "", z ); s= BraceEncodeStr( "\3" ); AssertStrEq( "{3}", s ); z= BraceDecodeStr( s ); AssertStrEq( "\3", z ); s= BraceEncodeStr( "\7\xff\6" ); AssertStrEq( "{7}{255}{6}", s ); z= BraceDecodeStr( s ); AssertStrEq( "\7\xff\6", z ); s= BraceEncodeStr( "[Hello}" ); AssertStrEq( "[Hello}", s ); z= BraceDecodeStr( s ); AssertStrEq( "[Hello}", z ); const char* hard = "{{{1}{}{2}{\7}{3}}}"; s= BraceEncodeStr( hard ); z= BraceDecodeStr( s ); AssertStrEq( hard, z ); } /////////////////////////////////////////////////////////////////////////////////////// Linked::~Linked() { assert( linkCount == 0 ); linkCount= -666; } #ifndef NDEBUG void Linked::check() const { assert( linkCount>=0 ); } #endif Error::~Error() throw() { if (why) free( (void*) why); } const char* Error::what() const throw() { return why? why: "(NULL)"; } const char* Stopped::what() const throw() { return because==OUT_OF_NODES ? "OUT_OF_NODES" : because==EVAL_TOO_DEEP ? "EVAL_TOO_DEEP" : because==EVAL_TOO_MANY_STEPS ? "EVAL_TOO_MANY_STEPS" : because==ENOUGH_OUTPUT ? "ENOUGH_OUTPUT" : "(?Stopped?)"; } int Stopped::Count_OUT_OF_NODES; int Stopped::Count_EVAL_TOO_DEEP; int Stopped::Count_EVAL_TOO_MANY_STEPS; int Stopped::Count_ENOUGH_OUTPUT; Stopped::Stopped(Because _because) throw () : because(_because) { switch (because) { case OUT_OF_NODES: ++Count_OUT_OF_NODES; break; case EVAL_TOO_DEEP: ++Count_EVAL_TOO_DEEP; break; case EVAL_TOO_MANY_STEPS: ++Count_EVAL_TOO_MANY_STEPS; break; case ENOUGH_OUTPUT: ++Count_ENOUGH_OUTPUT; break; } br( what() ); } Stopped::~Stopped() throw() {} template T AsciiTo(string s, string forWhat); template <> uint AsciiTo(string s, string forWhat) { uint z; s= Trim(s); int n= sscanf( s.c_str(), "%d", &z ); if ( n!=1 ) FATAL("cannot convert string to unsigned int for %s: `%s'", forWhat, s ); return z; } template <> double AsciiTo(string s, string forWhat) { double z; s= Trim(s); int n= sscanf( s.c_str(), "%lf", &z ); if ( n!=1 ) FATAL("cannot convert string to double for %s: `%s'", forWhat, s ); return z; } // For defining command-line Params struct ParamInfoBase { static ParamInfoBase* root; const char * name; const char * comment; ParamInfoBase * next; ParamInfoBase(const char * _name, const char * _comment); virtual ~ParamInfoBase(); virtual void setValue(Params*, string valueStr) = 0; virtual string getValue(Params*) = 0; virtual void setDefault(Params*) = 0; virtual string usage() = 0; static string List(); } *ParamInfoBase::root; ParamInfoBase::ParamInfoBase(const char * _name, const char * _comment) : name(_name) , comment(_comment) { next= root; // add to linked list root= this; } ParamInfoBase::~ParamInfoBase() { } /// template struct ParamInfo : public ParamInfoBase { typedef T Params::*DOUBLE_MEMBER; DOUBLE_MEMBER member; T dflt; ParamInfo(DOUBLE_MEMBER _member, const char* _name, const char* _comment, T _dflt); virtual ~ParamInfo(); virtual void setValue(Params*, string valueStr); virtual string getValue(Params*); virtual void setDefault(Params*); virtual string usage(); }; template ParamInfo::ParamInfo(DOUBLE_MEMBER _member, const char* _name, const char* _comment, T _dflt) : ParamInfoBase(_name, _comment) , member(_member) , dflt(_dflt) { } template ParamInfo::~ParamInfo() { } template void ParamInfo::setValue(Params* params, string valueStr) { string which = string() + name + " : " + comment; T value= AsciiTo( valueStr, which.c_str() ); (params ->* member)= value; } template string ParamInfo::getValue(Params* params) { return ToString(params ->* member); } template void ParamInfo::setDefault(Params* params) { (params ->* member)= dflt; } template string ParamInfo::usage() { return string(name) + "=" + ToString(dflt) + " : " + comment; } /// ParamInfo ParamP( (& Params::P), "P", "population size", 100); ParamInfo ParamG( (& Params::G), "G", "max number of generations", 100); ParamInfo ParamMutateTimes( (& Params::mutateTimes), "MutateTimes", "repeat mutate up to how many times", 1); ParamInfo ParamPercentMutate( (& Params::percentMutate), "PercentMutate", "percentage to mutate", 10.0); ParamInfo ParamPercentCross( (& Params::percentCross), "PercentCross", "percentage to cross", 10.0); ParamInfo ParamElitist( (& Params::elitist), "Elitist", "how many best creatures get free pass", 0); ParamInfo ParamShowBeaconFrequency( (& Params::showBeaconFrequency), "Beacon", "how often to print progress to stdout", 0); ParamInfo ParamFinalBest( (& Params::finalBestToShow), "FinalBest", "how many final best to show", 1); ParamInfo ParamRogers( (& Params::mrRogersNeighborhoodSize), "Rogers", "Mr Rogers neighborhood size", 100); ParamInfo ParamRogersDepth( (& Params::mrRogersNeighborhoodDepth), "RogersDepth", "depth of Mr Rogers neighborhood", 3); ParamInfo ParamInitLen( (& Params::creatureInitialLength), "InitLen", "initial creature length", 64); ParamInfo ParamMaxLen( (& Params::creatureMaxLength), "MaxLen", "maximum creature length", 128); ParamInfo ParamGoalLen( (& Params::creatureGoalLength), "GoalLen", "creature length goal", 64); ParamInfo ParamLenWeight( (& Params::creatureGoalLengthWeight), "LenWeight", "weight for creature length goal (perhaps 0 or 0.01)", 0.0); ParamInfo ParamWedge( (& Params::wedge), "Wedge", "wedge in distance metric (perhaps 0.0 or 1.0 or 2.0)", 1.0); ParamInfo ParamLimitDepth( (& Params::limitDepth), "LimitDepth", "maximum eval recursion depth", 999); ParamInfo ParamLimitEvals( (& Params::limitEvals), "LimitEvals", "maximum eval steps", 9999); ParamInfo ParamStopPercent( (& Params::percentageUnderTwoToStop), "StopPercent", "stop when this many percent are under two", 0); ParamInfo ParamShortPenalty( (& Params::shortPenalty), "ShortPenalty", "log of penalty for output too short", 10.0); Params::Params() { for (ParamInfoBase* info= ParamInfoBase::root; info; info= info->next) { info->setDefault(this); } creatureAlphabet = "?"; maxNumOutputs = 256; } Params::~Params() {} string Params::toString() { string z; for (ParamInfoBase* info= ParamInfoBase::root; info; info= info->next) { if (z.size()) z += " "; z += info->name; z += "="; z += info->getValue( this ); } return z; } string ParamInfoBase::List() { string z; for (ParamInfoBase* info= ParamInfoBase::root; info; info= info->next) { string tmp= info->usage(); z += string(" ... ") + tmp + "\n"; } return z; } Creature::~Creature() {} Generation::~Generation() {} MachineIO::~MachineIO() {} Machine::~Machine() {} string ToString(class Sampling x) { return x.toString(); } int CompareByStdFitness(const void* ap, const void* bp) { CreatureLink& al= *(CreatureLink*)ap; CreatureLink& bl= *(CreatureLink*)bp; double aa = al->stdFitness; double bb = bl->stdFitness; return (aabb) ? 1 : 0; } void Generation::sortByFitness() { CreatureLink* b= &vec[0]; int count = vec.size(); int size = sizeof(CreatureLink*); qsort( (void*)b, count, size, CompareByStdFitness ); } ////////////////////////////////////////////////////////////////////////////////// Language* Language::root; Language::Language(const char* _name) : name(_name) , next(root) { root= this; } Language::~Language() { } Link Language::defaultParams() { return new Params(); } Language* Language::Find(const char* _name) { for (Language* p= root; p; p= p->next) { if ( streq( _name, p->name ) ) return p; } throw Error( string() + "Language not found: " + _name ); } ////////////////////////////////////////////////////////////////////////////////// Target* Target::root; Target::~Target() { } Target::Target(const char* _name) : name(_name) , next(root) { root= this; } Target* Target::Find(const char* _name) { for (Target* p= root; p; p= p->next) { if ( streq( _name, p->name ) ) return p; } throw Error( string() + "Target not found: " + _name ); } TargetNumbers::TargetNumbers(const char* _name) : Target(_name) , numbers(new Numbers()) { } TargetNumbers::~TargetNumbers() { } ////////////////////////////////////////////////////////////////////////////////// EvalCounter::EvalCounter(Machine* _mach) : mach(_mach) { ++mach->depthCounter; if ( mach->depthCounter > mach->m_params->limitDepth ) { throw Stopped(Stopped::EVAL_TOO_DEEP); } ++mach->evalCounter; if ( mach->evalCounter > mach->m_params->limitEvals ) { throw Stopped(Stopped::EVAL_TOO_MANY_STEPS); } } MachineIO::MachineIO(Link p ) : m_params(p) , m_outputs( new Numbers() ) { } Machine::Machine(Link p, Link c ) : MachineIO(p) , m_creature(c) , depthCounter(0) , evalCounter(0) { } int Stopped_NONE; int Stopped_Error; int Stopped_exception; void Machine::eval() throw (Error) { // The language-invarient part of eval. try { this->eval_virtual(); ++ Stopped_NONE; } catch (Stopped& of) { return; } catch (Error&) { ++ Stopped_Error; throw; } catch (exception& ex) { ++ Stopped_exception; throw Error( ex.what() );; } } #define NO_READABLE_CODE "?????" string Machine::readableCode() { return NO_READABLE_CODE; // means we dont really have anything better to say, than sourceCode } /////////////////////////////////////////////////////////////////////////////////////// node TaggedMachine::cons(node p, node q) { if ( nextNode >= NUM_NODES ) { throw Stopped(Stopped::OUT_OF_NODES); } node z= &nodes[nextNode]; ++nextNode; z->n_hd= p; z->n_tl= q; return z; } int TaggedMachine::llen(node p) { int z= 0; while ( isPair(p) ) { p= tl(p); ++z; } return z; } TaggedMachine::~TaggedMachine() { assert(nodes); free(nodes); nodes= 0; } TaggedMachine::TaggedMachine( Link p, Link c ) : Machine(p,c) , nodes( (Node*) malloc( NUM_NODES * sizeof(Node) ) ) , nextNode( NUM_CHARS ) { assert(nodes); #ifndef NDEBUG memset( nodes, 0xFF, NUM_CHARS * sizeof(Node) ); #endif } void TaggedMachine::eval_virtual() { Assert( ! "subclassResponsiblity" ); } ////////////////// Tagged Mock Language //////////////////////////////// class TaggedMockLanguage : public Language { private: // private singleton, only used via Find() TaggedMockLanguage() : Language("__tagged") {} virtual ~TaggedMockLanguage(); static TaggedMockLanguage Singleton; public: virtual Link defaultParams( ); virtual Link createMachine( Link p, Link c ); } TaggedMockLanguage::Singleton; TaggedMockLanguage::~TaggedMockLanguage() { } Link TaggedMockLanguage::defaultParams( ) { Link p= Language::defaultParams(); p->creatureAlphabet = "-.? "; p->creatureInitialLength = 128; return p; } Link TaggedMockLanguage::createMachine( Link p, Link c ) { return new TaggedMachine( p, c ); } //////////////////////// TESTING ///////////////////////////////////// static TaggedMachine* testTaggedMachine( string programString ) { Language* lang= Language::Find("__tagged"); Link params= lang->defaultParams(); Link prog= new Creature(programString, lang); return new TaggedMachine( params, prog ); } TEST(tagged_basic_nodes) { Link m= testTaggedMachine( "???" ); Assert( m->isNum( m->numNode( 0 ) ) ); Assert( m->isNum( m->numNode( 999 ) ) ); Assert( m->isNum( m->numNode( -999 ) ) ); Assert( not m->isNum( m->charNode( 'a' ) ) ); Assert( not m->isNum( m->cons( m->numNode(0), m->charNode('a') ) ) ); Assert( m->isNum( m->hd( m->cons( m->numNode(0), m->charNode('a') ) ) ) ); Assert( not m->isNum( m->tl( m->cons( m->numNode(0), m->charNode('a') ) ) ) ); Assert( not m->isChar( m->numNode( 0 ) ) ); Assert( not m->isChar( m->numNode( 999 ) ) ); Assert( not m->isChar( m->numNode( -999 ) ) ); Assert( m->isChar( m->charNode( 'a' ) ) ); Assert( not m->isChar( m->cons( m->numNode(0), m->charNode('a') ) ) ); Assert( not m->isChar( m->hd( m->cons( m->numNode(0), m->charNode('a') ) ) ) ); Assert( m->isChar( m->tl( m->cons( m->numNode(0), m->charNode('a') ) ) ) ); Assert( not m->isPair( m->numNode( 0 ) ) ); Assert( not m->isPair( m->numNode( 999 ) ) ); Assert( not m->isPair( m->numNode( -999 ) ) ); Assert( not m->isPair( m->charNode( 'a' ) ) ); Assert( m->isPair( m->cons( m->numNode(0), m->charNode('a') ) ) ); Assert( not m->isPair( m->hd( m->cons( m->numNode(0), m->charNode('a') ) ) ) ); Assert( not m->isPair( m->tl( m->cons( m->numNode(0), m->charNode('a') ) ) ) ); AssertEq( 0, m->asNum( m->numNode( 0 ) ) ); AssertEq( 999, m->asNum( m->numNode( 999 ) ) ); AssertEq( -999, m->asNum( m->numNode( -999 ) ) ); AssertEq( (word)'a', m->asChar( m->charNode( 'a' ) ) ); } TEST(tagged_just_enough_nodes) { Link m= testTaggedMachine( "???" ); bool caught= false; try { // There are NUM_NODES - NUM_CHARS nodes available. for (int i=0; icons( m->charNode('a'), m->charNode('b') ); } } catch (Error& e) { caught= false; } Assert( not caught ); } TEST(tagged_too_many_nodes) { Link m= testTaggedMachine( "???" ); bool caught= false; try { // There are NUM_NODES - NUM_CHARS nodes available. // Allocating one more should break it. for (int i=0; icons( m->charNode('a'), m->charNode('b') ); } } catch (Error& e) { caught= true; } Assert( caught ); } /////////////////////////////////////////////////////////////////////////////////////// Link Language::createRandomCreature( Params* p ) { int alphalen= p->creatureAlphabet.size(); int clen= p->creatureInitialLength; string s; for (int i=0; icreatureAlphabet.at(r); s.push_back(ch); } return new Creature( s, this ); } Link Language::mutate( Params* params, Creature* c ) { uint times= 1; if (params->mutateTimes > 1) { times= 1 + Random::choose(params->mutateTimes); } for (uint t=0; tcreatureAlphabet.size(); uint len= c->sourceCode.length(); uint k= Random::choose(len); uint r= Random::choose(alphalen); char ch= params->creatureAlphabet.at(r); string s; for (uint i=0; isourceCode[i] ); } } c= new Creature(s, this); } return c; } int NumberOfDifferencesInStrings( string a, string b ) { int z= 0; AssertEq( a.size(), b.size() ); for (uint i=0; i params= lang->defaultParams(); int z= 0; for (uint i=0; i<10; i++) { Link prog= lang->createRandomCreature( params ); Link mutant= lang->mutate( params, prog ); int d= NumberOfDifferencesInStrings( prog->sourceCode, mutant->sourceCode ); z += NumberOfDifferencesInStrings( prog->sourceCode, mutant->sourceCode ); Assert( d <= 1 ); } Assert( z>5 ); // well more than half the time, should be a difference } void Language::cross( Params* params, Creature* c1, Creature* c2, CreatureLink& newc1, CreatureLink& newc2 ) { string a1= c1->sourceCode; string a2= c2->sourceCode; uint a1len= a1.length(); uint a2len= a2.length(); uint j1= Random::choose(a1len); uint k1= Random::choose(a1len); uint j2= Random::choose(a2len); uint k2= Random::choose(a2len); if ( k1 < j1 ) { uint tmp= k1; k1= j1; j1= tmp; } if ( k2 < j2 ) { uint tmp= k2; k2= j2; j2= tmp; } string s1 = a1.substr(0,j1) + a2.substr(j2, k2-j2) + a1.substr(k1); string s2 = a2.substr(0,j2) + a1.substr(j1, k1-j1) + a2.substr(k2); uint maxLen= params->creatureMaxLength; if ( s1.length() > maxLen ) { s1= s1.substr(0, maxLen); // crude truncation, if too big } if ( s2.length() > maxLen ) { s2= s1.substr(0, maxLen); // crude truncation, if too big } // Hopefully this can't happen, but if it does... if (not s1.size()) { s1= c1->sourceCode; } if (not s2.size()) { s2= c2->sourceCode; } newc1= new Creature(s1, this); newc2= new Creature(s2, this); } static uint NumberOfOccurancesOfCharInString( char ch, string s ) { uint z= 0; for (uint i=0; i params= lang->defaultParams(); for (uint i=0; i<10; i++) { CreatureLink c1= new Creature( "12345678901234567890", lang ); CreatureLink c2= new Creature( "abcdefghijabcdefghij", lang ); CreatureLink z1, z2; lang->cross( params, c1, c2, z1, z2 ); string all= z1->sourceCode + z2->sourceCode; //Say(stderr, "\n %s -- %s\n", z1->sourceCode.c_str(), z2->sourceCode.c_str() ); for (char ch='0'; ch<='9'; ch++) { AssertEq( 2U, NumberOfOccurancesOfCharInString( ch, all ) ); } for (char ch='a'; ch<='j'; ch++) { AssertEq( 2U, NumberOfOccurancesOfCharInString( ch, all ) ); } } } /////////////////////////////////////////////////////////////////////////////////////// double LoggyDistance( Numbers* targ, Numbers* outs, double wedge, double shortPenalty) { vector& t= targ->vec; vector& o= outs->vec; uint tsiz = t.size(); uint osiz = o.size(); double sum= 0.0; if ( wedge==0.0 ) { for (uint i=0; i targ = new Numbers(); Link outs = new Numbers(); targ->vec.push_back(t0); targ->vec.push_back(t1); targ->vec.push_back(t2); outs->vec.push_back(o0); outs->vec.push_back(o1); if (o2!=0) outs->vec.push_back(o2); double dist= LoggyDistance( targ, outs, wedge, shortPenalty ); return dist; } int Millionths( double x ) { return (int)(x*1000000.0); } // for double comparisons TEST(LoggyDistance0) { double dist= ToTestLoggyDistanceLength3( 1, 100, 333, 1, 100, 333, 2.0, 10.0 ); Assert( dist == 0.0 ); } TEST(LoggyDistanceDiff1) { double dist= ToTestLoggyDistanceLength3( 1, 100, 333, 1, 100, 334, 2.0, 10.0 ); AssertEq( Millionths(log(1 + 1.0)), Millionths(dist) ); } TEST(LoggyDistanceDiff1Wedge0) { double dist= ToTestLoggyDistanceLength3( 1, 100, 333, 1, 101, 333, 0.0, 10.0 ); AssertEq( Millionths(log(1 + 1.0)), Millionths(dist) ); } TEST(LoggyDistanceDiff1Wedge1) { double dist= ToTestLoggyDistanceLength3( 1, 100, 333, 1, 101, 333, 1.0, 10.0 ); AssertEq( Millionths(2.0*log(1 + 1.0)), Millionths(dist) ); } TEST(LoggyDistanceDiff1Wedge2) { double dist= ToTestLoggyDistanceLength3( 1, 100, 333, 1, 101, 333, 2.0, 10.0 ); AssertEq( Millionths(4.0*log(1 + 1.0)), Millionths(dist) ); } TEST(LoggyDistanceShort1) { double dist= ToTestLoggyDistanceLength3( 1, 100, 333, 1, 101, 0/* 0 means omit */, 0.0, 23.0 ); AssertEq( Millionths( log(2) + 23.0 ), Millionths(dist) ); } static double codeLengthPenalty( Machine* mach, Params* params) { if (not params->creatureGoalLengthWeight) return 0.0; // shortcut int lenDiff= abs( (int)mach->m_creature->sourceCode.size() - (int)params->creatureGoalLength ); return lenDiff * params->creatureGoalLengthWeight; } string TargetNumbers::explainTarget() { return ToString( numbers->vec ); } double TargetNumbers::computeRawFitness( Machine* mach, Params* params) { double dist= LoggyDistance( this->numbers, mach->m_outputs, params->wedge, params->shortPenalty ); double penalty= codeLengthPenalty( mach, params) ; return dist + penalty; } Link TargetNumbers::playAndRate( Creature *prog, Params *params, Language* lang ) { Link mach= lang->createMachine( params, prog ); mach->eval(); prog->stdFitness= this->computeRawFitness( mach, params ); return mach; } uint TournamentOfTwo( uint n ) { uint a= Random::choose(n); uint b= Random::choose(n); return a results, rather than Machine*" inline void Generation::potentialBestYet( Creature* prog, Machine* results ) { // may set bestYet and bestYetOutputs //==TODO: If prog can have stdFitness, why not also m_outputs if ( not (Creature*) bestYet || prog->stdFitness < bestYet->stdFitness ) { bestYet= prog; bestYetOutputs= results->m_outputs; bestYetReadableCode= results->readableCode(); Logger progress( "progress", stdout ); progress << "Generation" << genNum; progress << "BestYetFitness" << bestYet->stdFitness; progress << "BestYetCode" << bestYet->sourceCode; progress << "BestYetOutput" << bestYetOutputs->vec; if ( bestYetReadableCode != NO_READABLE_CODE ) { progress << "BestYetReadable" << bestYetReadableCode; } } } //==TODO: "Link results, rather than Machine*" void Generation::addRatedCreature( Creature* prog, Machine* mach ) { this->vec.push_back( prog ); if ( prog->stdFitness < 2.0 ) { ++numUnderFitnessTwo; } if ( mach ) { this->potentialBestYet( prog, mach ); } } Link BasicGP::createInitialBasicGen( ) { Link z= new Generation(); z->genNum= 0; for (uint p=0; pP; p++) { // add P random creatures to the generation, and rate their fitness Link prog= lang->createRandomCreature( params ); Link mach= targ->playAndRate( prog, params, lang ); z->addRatedCreature( prog, mach ); } return z; } Link BasicGP::nextBasicGen() { uint P = currentGen->vec.size(); Link z= new Generation(); z->genNum= 1 + currentGen->genNum; z->bestYet = currentGen->bestYet; z->bestYetOutputs = currentGen->bestYetOutputs; z->bestYetReadableCode = currentGen->bestYetReadableCode; double mutateFence= params->percentMutate; double crossFence= mutateFence + params->percentCross; for ( uint p=0; pelitist ) { // Previous Best Creatures gets free ticket CreatureLink prog= currentGen->vec.at(p); z->addRatedCreature( prog, NULL ); } else if ( r < mutateFence ) { Trace2( stderr, "." ); CreatureLink prog= lang->mutate(params, currentGen->vec.at(t)); Link mach= targ->playAndRate( prog, params, lang ); z->addRatedCreature( prog, mach ); } else if ( r < crossFence && p+1

vec.at(t); uint t2= TournamentOfTwo(P); CreatureLink prog2= currentGen->vec.at(t2); lang->cross( params, prog, prog2, /*out*/prog, /*out*/prog2 ); Link mach= targ->playAndRate( prog, params, lang ); z->addRatedCreature( prog, mach ); Link mach2= targ->playAndRate( prog2, params, lang ); z->addRatedCreature( prog2, mach2 ); ++p; // a second creature was inserted } else { // 60% Single Copy CreatureLink prog= currentGen->vec.at(t); z->addRatedCreature( prog, NULL ); } } return z; } void BasicGP::saySummary( Logger& lg, Generation* gen, uint genNum) { size_t sz= gen->vec.size(); uint ten= min ( (size_t)10, sz ); // how many top scores to show lg << "GENERATION" << genNum; string tops; for (uint j=0; jvec.at(j)->stdFitness ) ) + " "; } lg << "LogTops: " << tops; uint num_under_two = 0; for (uint j=0; jvec.at(j)->stdFitness; if (fit < 2.0) ++num_under_two; } lg << "num_under_two" << num_under_two ; lg << "min" << gen->vec.at( 0 )->stdFitness << "25%" << gen->vec.at( (sz-1)/4 )->stdFitness << "median" << gen->vec.at( (sz-1)/2 )->stdFitness << "75%" << gen->vec.at( (sz-1)*3/4 )->stdFitness << "max" << gen->vec.at( (sz-1) )->stdFitness ; lg << "BEST_FIT" << gen->bestYet->stdFitness; lg << "BEST_CODE" << gen->bestYet->sourceCode.c_str(); lg << "BEST_OUT" << ToString( gen->bestYetOutputs->vec, 256 ); if ( gen->bestYetReadableCode != NO_READABLE_CODE ) { lg << "BEST_READABLE" << gen->bestYetReadableCode; } } template void BasicGP::displayBuckets( Link buckets, string label, uint n ) { // Abbreviate output vector by chopping off trailing 0 elements uint num_used_buckets= RogersScore::NUM_BUCKETS; // tentative while ( num_used_buckets > 2 ) { if ( 0 == Double( buckets->vec[ num_used_buckets-1 ] ) ) --num_used_buckets; else break; } string s; for (uint i=0; ivec[i] ); } gp_answer << label << s; } Link BasicGP::histogramOfExploration( Link scores, double parentScore, string label ) { Link buckets= new Doubles(); // buckets for histogram for (uint i=0; ivec.push_back(0); } uint n= scores->vec.size(); double nth= 1.0/n; // build histograms in buckets for (uint i=0; ivec.at(i) - parentScore; uint buck= 0; if ( gap > 0.0 ) { buck= (uint) (log(gap+1.0)); } //if ( buck >= RogersScore::NUM_BUCKETS ) buck= RogersScore::NUM_BUCKETS-1; buck = min( buck, RogersScore::NUM_BUCKETS-1 ); buckets->vec[buck] += nth; } displayBuckets( buckets, label, n ); return buckets; } Link BasicGP::exploreRogersAtOneDepth( Link prog, int depth, /*double&*/ Sampling& neutralityOut ) { neutralityOut= 0.0; uint neutralCount= 0; uint n= params->mrRogersNeighborhoodSize; Link z= new Doubles(); for (uint i=0; i mutant= prog; for (int d=0; dmutate( params, mutant ); } targ->playAndRate( mutant, params, lang ); z->vec.push_back( mutant->stdFitness ); if ( mutant->stdFitness <= prog->stdFitness ) ++neutralCount; } if (n>0) { neutralityOut=(double)neutralCount/(double)n; } return z; } void Params::setParam( string key, string value ) { for (ParamInfoBase* info= ParamInfoBase::root; info; info= info->next) { //#if ( streq( info->name, k ) ) if ( key == info->name ) { info->setValue(this, value); return; } } FATAL("(use -h for help) Unknown parameter key: '%s'", key ); } void Params::setParams( const vector &args ) { for (uint i=0; iexplainTarget(); // continue without new seed for (uint i=0; ivec.size(); i++) { Link prog= old.currentGen->vec.at(i); Link mach= targ->playAndRate( prog, params, lang ); initialGen->addRatedCreature( prog, mach ); } currentGen= initialGen; } /* * This is the primary constructor, * leaving the initial Generation NULL. */ BasicGP::BasicGP( string lang_s, string targ_s, vector _args ) : gp_note ("note", verbose ? stderr : NULL ) , gp_answer ("answer", stdout ) , args( _args ) , lang( Language::Find( lang_s.c_str() ) ) , targ( Target::Find( targ_s.c_str() ) ) , params( lang->defaultParams() ) , whenFirstCreatureIsUnderTwo(0) , whenFirstQuarterAreEachUnderTwo(0) { Assert(lang); Assert(params); Assert(targ); params->setParams(_args); gp_note << "Target" << targ->explainTarget(); nextSeed(); initialGen= createInitialBasicGen( ); initialGen->sortByFitness(); currentGen= initialGen; } BasicGP::~BasicGP() { } void BasicGP::nextSeed() { gp_answer << "seed" << randomSeed; Li::Random::setSeed( randomSeed ); ++randomSeed; } void BasicGP::showFinalBest() { for (uint i=0; ifinalBestToShow; i++) { gp_answer << ( ToString("Final,") + ToString(i) ) << currentGen->vec[i]->stdFitness << currentGen->vec[i]->sourceCode.c_str(); } } void BasicGP::go() { uint g; gp_answer << "Params" << lang->getName() << targ->getName() << params->toString(); for (g=0; gG; g++) { if ( not gp_note.isNull() ) saySummary( gp_note, currentGen, g ); if ( params->percentageUnderTwoToStop ) { if ( currentGen->numUnderFitnessTwo * 100.0 / params->P >= params->percentageUnderTwoToStop ) { gp_answer << "StopBecausePercentageUnderTwo" << currentGen->numUnderFitnessTwo * 100.0 / params->P; break; } } currentGen= nextBasicGen( ); currentGen->sortByFitness(); if (params->showBeaconFrequency && g % params->showBeaconFrequency == 0 ) { Logger beacon("beacon", stdout); saySummary( beacon, currentGen, g ); } } saySummary( gp_answer, currentGen, g ); showFinalBest(); doRogersCalculationsOnManyCreatures(); gp_score.s_label= LabelScope::Decorate( "FINAL" ); gp_score.s_best_fitness= currentGen->bestYet->stdFitness; gp_score.s_num_generations= g; if ( params->finalBestToShow > 1 ) { gp_score.s_final_fitness.clearForInsertions(); for ( uint fb=0; fbfinalBestToShow; fb++ ) { gp_score.s_final_fitness.insertSample( currentGen->vec.at(fb)->stdFitness ); } } else { gp_score.s_final_fitness= currentGen->vec.at(0)->stdFitness; } gp_score.say( gp_answer ); } void BasicGP::doRogersCalculationsOnManyCreatures() { if ( params->mrRogersNeighborhoodSize ) { doRogersCalculationsOnOneCreature( currentGen->bestYet, /*out*/ gp_score.s_rogers_bestyet, string("RogersHistoBestYet") ); gp_score.s_rogers_final.resize( params->finalBestToShow ); for (uint fb= 0; fb < params->finalBestToShow; fb++ ) { doRogersCalculationsOnOneCreature( currentGen->vec.at(fb), /*out*/ gp_score.s_rogers_final[fb], string("RogersHistoFinalBest_")+ToString(fb) ); } gp_score.computeAverageFinal(); for ( uint depth=1; depth<=params->mrRogersNeighborhoodDepth; depth++) { displayBuckets( gp_score.s_rogers_final_average.r_histos[depth-1], string("RogersHistoFinalBest_Average_"+ToString(depth)), params->mrRogersNeighborhoodSize ); } } } void GpScore::computeAverageFinal() { int num_fb= s_rogers_final.size(); s_rogers_final_average.clearForInsertions(); for (uint fb=0; fb samps= new Samplings; samps->vec.resize(NUM_BUCKETS); for ( uint b= 0; bvec[b].clearForInsertions(); r_histos.push_back(samps); } assert( r_histos.size() >= d+1 ); for ( uint b= 0; bvec[b].insertSample( x.r_histos[d]->vec[b] ); } } } void GpScore::clearForInsertions() { s_num_generations.clearForInsertions(); s_final_fitness.clearForInsertions(); s_best_fitness.clearForInsertions(); s_rogers_bestyet.clearForInsertions(); s_rogers_final.clear(); /// this vector will not be used s_rogers_final_average.clearForInsertions(); } void GpScore::insertSample(const GpScore& x) { s_num_generations.insertSample( x.s_num_generations ); s_final_fitness.insertSample( x.s_final_fitness ); s_best_fitness.insertSample( x.s_best_fitness ); s_rogers_bestyet.insertSample( x.s_rogers_bestyet ); s_rogers_final_average.insertSample( x.s_rogers_final_average ); } void BasicGP::doRogersCalculationsOnOneCreature( Creature* prog, RogersScore &rScore, string prefix ) { rScore.r_histos.clear(); for (int depth= 1; depth <= params->mrRogersNeighborhoodDepth; depth++) { Link fitnesses= exploreRogersAtOneDepth( currentGen->bestYet, depth, /*out*/ rScore.r_neutrality ); Link histo= histogramOfExploration( fitnesses, currentGen->bestYet->stdFitness, prefix + "_" + ToString(depth) ); // copy Doubles to Samples Link samps= new Samplings(); for ( int j=0; jsize(); j++) { samps->push_back( histo->vec[j] ); } assert( samps->vec.size() == histo->vec.size() ); rScore.r_histos.push_back( samps ); assert( rScore.r_histos.size() == depth ); } } void GpScore::say( Logger& lg ) { lg << s_label << "gen" << s_num_generations << "final" << s_final_fitness << "best" << s_best_fitness; lg << s_label+"_neutrality" << "final_avg_neutral" << s_rogers_final_average.r_neutrality << "best_neutral" << s_rogers_bestyet.r_neutrality; } long TestCase::AssertSuccessCount; long TestCase::AssertFailureCount; long TestCase::TestExceptionCount; AssertException::AssertException(string _what) throw() : ex_what(_what) { } AssertException::~AssertException() throw() { } const char* AssertException::what() const throw() { return ex_what.c_str(); } // Notice the TEST(NAME) macro is used to create TestCases // Linked List of all TestCases TestCase *TestRoot; // These constructors run on static objects when module loaded TestCase::TestCase( void (*_func)(void), const char* _name ) { func= _func; name= _name; next= TestRoot; TestRoot= this; } // Static method to run all tests that are loaded void TestCase::RunAll() { int count= 0; TestCase* p= TestRoot; while (p) { #ifndef NDEBUG Say(stderr, "Testing %s ... ", p->name ); fflush(stderr); #endif try { (p->func)(); } catch (exception& ex) { Say(stderr, "\n*** CAUGHT EXECPTION: %s\n", ex.what() ); fflush(stderr); ++ TestExceptionCount; } ++count; p= p->next; } if ( AssertFailureCount || TestExceptionCount) { Say(stderr, "\n******** BAD: %d Tests, %ld Asserts Failed, %ld Exceptions ********\n", count, AssertFailureCount, TestExceptionCount ); fflush(stderr); // Don't continue if things ar broken. Exit Here. exit(13); } else { #ifndef NDEBUG Say(stderr, "\nOKAY: %d Tests, %ld Asserts Succeeded\n", count, AssertSuccessCount ); fflush(stderr); #endif } } void Assert_Impl(bool cond, const char* expr, const char* pretty, const char* file, int line) { if ( cond ) { // good ++ TestCase::AssertSuccessCount; } else { ++ TestCase::AssertFailureCount; Say(stderr, "Assert Failure #%d in %s at %s:%d <= %s\n", TestCase::AssertFailureCount, pretty, file, line, expr ); fflush(stderr); TestCase::ThrowAssertionFailure( __FILE__, __LINE__ ); } } void TestCase::ThrowAssertionFailure( const char* file, int line) throw (AssertException) { throw AssertException( string() + "Assertion Failure " + file + ":" + ToString(line) ); } void AssertStrEq_Impl(const string expected, const string got, const char* expectedExpr, const char* gotExpr, const char* pretty, const char* file, int line) { if ( expected == got ) { // good ++ TestCase::AssertSuccessCount; } else { ++ TestCase::AssertFailureCount; Say(stderr, "AssertStrEq Failure #%d in %s at %s:%d\nExpected: %s => %s\nGot: %s => %s\n", TestCase::AssertFailureCount, pretty,file, line, expectedExpr, expected.c_str(), gotExpr, got.c_str() ); fflush(stderr); TestCase::ThrowAssertionFailure( __FILE__, __LINE__ ); } } void AssertStrBeginsWith_Impl(const string expected, const string got, const char* expectedExpr, const char* gotExpr, const char* pretty, const char* file, int line) { if ( !strncmp( expected.c_str(), got.c_str(), (int)expected.size() ) ) { // good ++ TestCase::AssertSuccessCount; } else { ++ TestCase::AssertFailureCount; Say(stderr, "AssertStrEq Failure #%d in %s at %s:%d\nExpected: %s => %s\nGot: %s => %s\n", TestCase::AssertFailureCount, pretty,file, line, expectedExpr, expected.c_str(), gotExpr, got.c_str() ); fflush(stderr); TestCase::ThrowAssertionFailure( __FILE__, __LINE__ ); } } string ToString(int x) { char buf[99]; sprintf( buf, "%d", x ); return string(buf); } string ToString(uint x) { char buf[99]; sprintf( buf, "%u", x ); return string(buf); } string ToString(long x) { char buf[99]; sprintf( buf, "%ld", x ); return string(buf); } string ToString(unsigned long x) { char buf[99]; sprintf( buf, "%lu", x ); return string(buf); } string ToString(float x) { return ToString( (double)x ); } string ToString(double x) { char buf[9999]; double absX= (x<0) ? -x : x; // the man page seems to say %g looses precision when x<.1 but greater than 1.0e- if ( absX > 0 && absX < 0.01 ) { sprintf( buf, extendPrecisionPrintingDoubles? "%.12e": "%.4e", x ); } else { sprintf( buf, extendPrecisionPrintingDoubles? "%20.6g": "%13.2f", x ); } return Trim( buf ); } string ToString(void* x) { char buf[99]; sprintf( buf, "0x%lx", (long)x ); return string(buf); } TEST(ToString_int) { AssertStrEq( "-161", ToString(-161).c_str() ); } TEST(ToString_uint) { AssertStrEq( "3000111222", ToString(3000111222U).c_str() ); } TEST(ToString_long) { AssertStrEq( "232", ToString(232LU).c_str() ); } TEST(ToString_ulong) { AssertStrEq( "-1661", ToString(-1661L).c_str( ) ); } TEST(ToString_float) { AssertStrBeginsWith( "2.5", Trim(ToString( (float)2.5 )).c_str( ) ); } TEST(ToString_double) { AssertStrBeginsWith( "3.5", Trim(ToString(3.5)).c_str() ); } TEST(ToString_voidptr) { AssertStrEq( "0x64", ToString( (void*)100 ).c_str() ); } void JustEvalAndPrint( string lang_s, string source_s, Target* targ ) { Logger log( "eval", stdout ); Language* lang= Language::Find(lang_s); Link params= lang->defaultParams(); Link prog= new Creature(source_s, lang); Link mach= lang->createMachine( params, prog ); mach->eval(); log << "outputs" << ToString( mach->m_outputs->vec ); if (targ) { assert( not "TODO" ); double dist= 0;//LoggyDistance( targ->numbers, mach->m_outputs, params->wedge, params->shortPenalty ); log << "Distance" << dist; } } vector Command_dispatch( vector words ); vector Command_dispatch( string words ) { return Command_dispatch( SplitWords( words ) ); } vector Command_gp( vector argv ) { if ( argv.size() < 2 ) { FATAL( "(use -h for help) command `gp' needs at least `lang' and 'target,target,target...' arguments" ); } string lang_s= PopFront( argv ); //auto LabelScope obj1( string("Lang=") + lang_s ); string targets_s= PopFront( argv ); vector args; for ( uint i=0; i targets= SplitWordsOnChar<','>(targets_s); vector zScores; Link gen= NULL; uint i= 0; BasicGP* gp= new BasicGP( lang_s, targets.at(0), args ); while (true) { //auto LabelScope obj2( string("Targ=") + targets.at(i) ); gp->go(); zScores.push_back( gp->gp_score ); ++i; if ( i == targets.size() ) break; gp= new BasicGP( *gp, targets.at(i) ); } return zScores; } vector Command_multi( vector argv ) { Logger log("multi", stderr); log << "NUMBER_OF_JOBS" << ToString(argv.size()) ; vector zScores; for ( uint i=0; i jobWords= SplitWords( argv[i] ); vector scores= Command_dispatch( argv[i] ); for (int k=0; k Command_times( vector argv ) { Logger log("times", stderr); string spec= PopFront(argv); uint n= AsciiTo( spec, "multi should be followed by an integer number of times" ); vector zScores; zScores.resize(1); zScores[0].clearForInsertions(); string label= "?"; for ( uint i=0; i scores= Command_dispatch( argv ); if (scores.size() != 1) FATAL("The 'times' command cannot call command returning multiple results"); zScores[0].insertSample( scores[0] ); label= scores[0].s_label; // remember any label } if (n>1) zScores[0].s_label= label; return zScores; } vector ToVector( int argc, char* argv[] ) { vector z; for (int i=0; i Command_echo( vector v, string verb ) { vector zScores; string s; for ( int i=0; i Command_for( vector v ) { vector zScores; string spec= PopFront(v); int sep= strchr( spec.c_str(), '=' ) - spec.c_str(); if ( sep<1 ) FATAL("cannot find `=' within for loop spec: ", spec ); string pname= spec.substr(0, sep); vector values= SplitWordsOnChar<','>(spec.substr(sep+1)); if ( pname.size() != 1 ) FATAL("Only single letter variables are supported"); for ( int i=0; i(); // return empty vector } else if ( cmd == "evaltarg" ) { string lang= PopFront(v); string t= PopFront(v); string prog= PopFront(v); Target* targ= Target::Find( t.c_str() ); JustEvalAndPrint( lang, prog, targ ); return vector(); // return empty vector } else { FATAL("Unknown dispatch command: `%s'", cmd); /*NOTREACHED*/ return vector(); // return empty vector } } string Language::List() { string z; for (Language* p= root; p; p= p->next) { z += string(p->name) + " "; } return z; } string Target::List() { string z; for (Target* p= root; p; p= p->next) { z += string(p->name) + " "; } return z; } static void Help() { fprintf(stderr, "Usage: a.out \n" ); fprintf(stderr, " -h : print this help\n" ); fprintf(stderr, " -e : extend precision in output of doubles\n" ); fprintf(stderr, " -v : print verbose notes to stderr\n" ); fprintf(stderr, " -q : quiet -- suppress normal stdout except main result\n" ); fprintf(stderr, " -t : enable interpreter tracing (very verbose!)\n" ); fprintf(stderr, " -n : no unit testing at startup\n" ); fprintf(stderr, "Commands:\n" ); fprintf(stderr, " gp ,,... =value1 =value2 ...\n" ); fprintf(stderr, " eval 'PROGRAM'\n" ); fprintf(stderr, " evaltarg 'PROGRAM'\n" ); string langs= Language::List(); fprintf(stderr, "Languages: %s\n", langs.c_str() ); string targs= Target::List(); fprintf(stderr, "Targets: %s\n", targs.c_str() ); string params= ParamInfoBase::List(); fprintf(stderr, "Parameters (and default values) for gp:\n%s\n", params.c_str() ); exit(13); } static void processDashOptions( vector & v ) { while ( v.size() > 0 && v.at(0).size()>=2 && v.at(0)[0]=='-' ) { // process dash options string opt= PopFront( v ); switch( opt[1] ) { case '?': case 'h': case 'H': Help(); break; case 'e': if ( opt.size() != 2 ) FATAL("Trailing junk in -e argument"); ++ extendPrecisionPrintingDoubles; break; case 'q': if ( opt.size() != 2 ) FATAL("Trailing junk in -q argument"); ++ quiet; break; case 'v': if ( opt.size() != 2 ) FATAL("Trailing junk in -v argument"); ++ verbose; break; case 't': if ( opt.size() != 2 ) FATAL("Trailing junk in -t argument"); ++ trace; break; case 'n': if ( opt.size() != 2 ) FATAL("Trailing junk in -n argument"); ++ noTest; break; case 's': if (not ( '0' <= opt[2] && opt[2] <= '9' )) { FATAL("Expected integer after -s (e.g. -s23)", opt.c_str() + 2 ); } randomSeed= AsciiTo( opt.substr(2), "Illegal integer in -s parameter" ); break; default: FATAL("(use -h for help) Unknown Option: %s" , opt.c_str() ); break; } } } int Li_main( vector v ) { string argv0= PopFront( v ); Say = & fprintf; processDashOptions( v ); int dup_of_stdout= dup(1); // quietness works by temporarily routing stdout to /dev/null if (quiet) { close(1); fopen("/dev/null", "a"); } Li::Random::setSeed( randomSeed ); // set for unit tests if ( not noTest ) { TestCase::RunAll(); } if ( v.size() < 1 ) { fprintf( stderr, "No command ... doing nothing. (use -h for help)\n" ); return 0; } Li::Random::setSeed( randomSeed ); // set again, for real run vector scores= Command_dispatch( v ); // undo quietness by restoring stdout from its dup if (quiet) { close(1); dup(dup_of_stdout); } // print final scores with an '@' prepended Logger main( "main", stdout ); for (int i=0; i