#ifdef _MSC_VER #pragma warning(disable:4786) #endif #include #include using namespace gp_parse_tree; using namespace std; //----------------------------------------------------------------------------- class SymregNode { public : enum Operator {X = 'x', Plus = '+', Min = '-', Mult = '*', PDiv = '/'}; SymregNode() { init(); } SymregNode(Operator _op) { op = _op; } virtual ~SymregNode() {} // arity function, need this function! int arity() const { return op == X? 0 : 2; } void randomize() {} // evaluation function, single case, using first argument to give value of variable template void operator()(double& result, Children args, double var) const { double r1(0.), r2(0.); if (arity() == 2) { args[0].apply(r1, var); args[1].apply(r2, var); } switch (op) { case Plus : result = r1 + r2; break; case Min : result = r1 - r2; break; case Mult : result = r1 * r2; break; case PDiv : { if (r2 == 0.0) // protection a la Koza, realistic implementations // should maybe throw an exception result = 1.0; else result = r1 / r2; break; } case X : result = var; break; } } /// 'Pretty' print to ostream function template void operator()(string& result, Children args) const { static const string lb = "("; static const string rb = ")"; char opStr[4] = " "; opStr[1] = op; if (arity() == 0) { result = "x"; return; } // else string r1; args[0].apply(r1); result = lb + r1; result += opStr; args[1].apply(r1); result += r1 + rb; } Operator getOp() const { return op; } protected : void init() { op = X; } private : Operator op; // the type of node }; /// initializor static SymregNode init_sequence[5] = {SymregNode::X, SymregNode::Plus, SymregNode::Min, SymregNode::Mult, SymregNode::PDiv}; // needed for intialization // MSVC does not recognize the lt_arity in eoParseTreeDepthInit // without this specialization ... // 2 months later, it seems it does not accept this definition ... // but dies accept the lt_arity in eoParseTreeDepthInit // !!! // #ifdef _MSC_VER // template <> // bool lt_arity(const SymregNode &node1, const SymregNode &node2) // { // return (node1.arity() < node2.arity()); // } // #endif //----------------------------------------------------------- // saving, loading std::ostream& operator<<(std::ostream& os, const SymregNode& eot) { os << static_cast(eot.getOp()); return os; } std::istream& operator>>(std::istream& is, SymregNode& eot) { char type; type = (char) is.get(); eot = SymregNode(static_cast(type)); return is; } //----------------------------------------------------------------------------- /** Implementation of a function evaluation object. */ double targetFunction(double x) { return x * x * x * x - x * x * x + x * x * x - x * x + x - 10; } // parameters controlling the sampling of points const double xbegin = -10.0f; const double xend = 10.0f; const double xstep = 1.3f; template struct RMS: public eoEvalFunc< eoParseTree > { public : typedef eoParseTree EoType; typedef eoParseTree argument_type; typedef double fitness_type; RMS() : eoEvalFunc() { int n = int( (xend - xbegin) / xstep); inputs.resize(n); target.resize(n); int i = 0; for (double x = xbegin; x < xend && i < n; ++i, x+=xstep) { target[i] = targetFunction(x); inputs[i] = x; } } ~RMS() {} void operator()( EoType & _eo ) { vector outputs; outputs.resize(inputs.size()); double fitness = 0.0; for (unsigned i = 0; i < inputs.size(); ++i) { _eo.apply(outputs[i], inputs[i]); fitness += (outputs[i] - target[i]) * (outputs[i] - target[i]); } fitness /= (double) target.size(); fitness = sqrt(fitness); if (fitness > 1e+20) fitness = 1e+20; _eo.fitness(fitness); } private : vector inputs; vector target; }; template void print_best(eoPop& pop) { std::cout << std::endl; FitnessType best = pop[0].fitness(); int index = 0; for (unsigned i = 1; i < pop.size(); ++i) { if (best < pop[i].fitness()) { best = pop[i].fitness(); index = i; } } std::cout << "\t"; string str; pop[index].apply(str); std::cout << str.c_str(); std::cout << std::endl << "RMS Error = " << pop[index].fitness() << std::endl; } int main() { typedef eoMinimizingFitness FitnessType; typedef SymregNode GpNode; typedef eoParseTree EoType; typedef eoPop Pop; const int MaxSize = 100; const int nGenerations = 10; // only a test, so few generations // Initializor sequence, contains the allowable nodes vector init(init_sequence, init_sequence + 5); // Depth Initializor, defaults to grow method. eoGpDepthInitializer initializer(10, init); // Root Mean Squared Error Measure RMS eval; Pop pop(50, initializer); apply(eval, pop); eoSubtreeXOver xover(MaxSize); eoBranchMutation mutation(initializer, MaxSize); // The operators are encapsulated into an eoTRansform object, // that performs sequentially crossover and mutation eoSGATransform transform(xover, 0.75, mutation, 0.25); // The robust tournament selection eoDetTournamentSelect selectOne(2); // tSize in [2,POPSIZE] // is now encapsulated in a eoSelectMany: 2 at a time -> SteadyState eoSelectMany select(selectOne,2, eo_is_an_integer); // and the Steady-State replacement eoSSGAWorseReplacement replace; // Terminators eoGenContinue term(nGenerations); eoCheckPoint checkPoint(term); eoAverageStat avg; eoBestFitnessStat best; eoStdoutMonitor monitor; checkPoint.add(monitor); checkPoint.add(avg); checkPoint.add(best); monitor.add(avg); monitor.add(best); // GP generation eoEasyEA gp(checkPoint, eval, select, transform, replace); std::cout << "Initialization done" << std::endl; print_best(pop); try { gp(pop); } catch (std::exception& e) { std::cout << "exception: " << e.what() << std::endl;; exit(EXIT_FAILURE); } print_best(pop); } // Local Variables: // coding: iso-8859-1 // mode: C++ // c-file-offsets: ((c . 0)) // c-file-style: "Stroustrup" // fill-column: 80 // End: