diff --git a/eo/src/pyeo/PyEO.cpp b/eo/src/pyeo/PyEO.cpp new file mode 100644 index 00000000..2a502070 --- /dev/null +++ b/eo/src/pyeo/PyEO.cpp @@ -0,0 +1,221 @@ +/* + PyEO + + Copyright (C) 2003 Maarten Keijzer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include +#include "PyEO.h" + + +// static member, needs to be instantiated somewhere +std::vector PyFitness::objective_info; + +bool PyFitness::dominates(const PyFitness& oth) const +{ + bool dom = false; + + for (unsigned i = 0; i < nObjectives(); ++i) + { + int objective = objective_info[i]; + + if (objective == 0) // ignore + continue; + + bool maxim = objective > 0; + + double aval = maxim? (*this)[i] : -(*this)[i]; + double bval = maxim? oth[i] : -oth[i]; + + if (fabs(aval - bval) > tol()) + { + if (aval < bval) + { + return false; // cannot dominate + } + // else aval < bval + dom = true; // for the moment: goto next objective + } + //else they're equal in this objective, goto next + } + + return dom; +} + +bool dominates(const PyEO& a, const PyEO& b) +{ + return PyFitness(a.fitness()).dominates(b.fitness()); +} + +ostream& operator<<(ostream& os, const PyEO& _eo) +{ + os << _eo.to_string(); + return os; +} + +struct pyPop_pickle_suite : boost::python::pickle_suite +{ + static boost::python::tuple getstate(const eoPop& _pop) + { + boost::python::list entries; + for (unsigned i = 0; i != _pop.size(); ++i) + entries.append( PyEO_pickle_suite::getstate(_pop[i]) ); + + return make_tuple(object(_pop.size()), entries); + } + + static void setstate( eoPop& _pop, boost::python::tuple pickled) + { + int sz = extract(pickled[0]); + boost::python::list entries = pickled[1]; + _pop.resize(sz); + for (unsigned i = 0; i != _pop.size(); ++i) + PyEO_pickle_suite::setstate(_pop[i], tuple(entries[i]) ); + } +}; + + +template +boost::python::str to_string(T& _p) +{ + std::ostrstream os; + _p.printOn(os); + os << ends; + std::string s(os.str()); + return str(s.c_str()); +} + +void pop_sort(eoPop& pop) { pop.sort(); } +void pop_shuffle(eoPop& pop) { pop.shuffle(); } + +void translate_index_error(index_error const& e) +{ + PyErr_SetString(PyExc_IndexError, e.what.c_str()); +} + +PyEO& pop_getitem(eoPop& pop, object key) +{ + extract x(key); + if (!x.check()) + throw index_error("Slicing not allowed"); + + int i = x(); + + if (static_cast(i) >= pop.size()) + { + throw index_error("Index out of bounds"); + } + + return pop[i]; +} +void pop_setitem(eoPop& pop, object key, PyEO& value) +{ + + extract x(key); + if (!x.check()) + throw index_error("Slicing not allowed"); + + int i = x(); + + if (static_cast(i) >= pop.size()) + { + throw index_error("Index out of bounds"); + } + + pop[i] = value; +} + +void pop_push_back(eoPop& pop, PyEO& p) { pop.push_back(p); } +void pop_resize( eoPop& pop, unsigned i) { pop.resize(i); } + +extern void abstract1(); +extern void algos(); +extern void random_numbers(); +extern void geneticOps(); +extern void selectOne(); +extern void continuators(); +extern void reduce(); +extern void replacement(); +extern void selectors(); +extern void breeders(); +extern void mergers(); +extern void valueParam(); +extern void perf2worth(); + +BOOST_PYTHON_MODULE(PyEO) +{ + register_exception_translator(&translate_index_error); + + class_("EO") + .add_property("fitness", &PyEO::getFitness, &PyEO::setFitness) + .add_property("genome", &PyEO::getGenome, &PyEO::setGenome) + .def_pickle(PyEO_pickle_suite()) + .def("invalidate", &PyEO::invalidate) + .def("invalid", &PyEO::invalid) + .def("__str__", &PyEO::to_string) + ; + + class_ >("Pop", init<>() ) + .def( init< unsigned, eoInit& >() ) + .def("append", &eoPop::append) + .def("__str__", to_string >) + .def("__len__", &eoPop::size) + .def("sort", pop_sort ) + .def("shuffle", pop_shuffle) + .def("__getitem__", pop_getitem, return_internal_reference<>() ) + .def("__setitem__", pop_setitem) + .def("best", &eoPop::best_element, return_internal_reference<>() ) + .def("push_back", pop_push_back) + .def("resize", pop_resize) + .def_pickle(pyPop_pickle_suite()) + ; + + + // Other definitions in different compilation units, + // this to avoid having g++ to choke on the load + random_numbers(); + valueParam(); + abstract1(); + geneticOps(); + selectOne(); + selectors(); + perf2worth(); + continuators(); + reduce(); + replacement(); + breeders(); + mergers(); + algos(); + + // The traits class + class_("PyFitness"); + + def("nObjectives", &PyFitness::nObjectives); + def("tol", &PyFitness::tol); + def("maximizing", &PyFitness::maximizing); + def("setObjectivesSize", &PyFitness::setObjectivesSize); + def("setObjectivesValue", &PyFitness::setObjectivesValue); + def("dominates", dominates); +} + + +// to avoid having to build with libeo.a +ostream & operator << ( ostream& _os, const eoPrintable& _o ) { + _o.printOn(_os); + return _os; +}; diff --git a/eo/src/pyeo/PyEO.h b/eo/src/pyeo/PyEO.h new file mode 100644 index 00000000..dc5dd260 --- /dev/null +++ b/eo/src/pyeo/PyEO.h @@ -0,0 +1,153 @@ +/* + PyEO + + Copyright (C) 2003 Maarten Keijzer + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PYEO_H +#define PYEO_H + +#include + +#include +#include + +using namespace boost::python; + +struct index_error { index_error(std::string w) : what(w) {}; std::string what; }; + +class PyFitness : public boost::python::object +{ + public : + + typedef PyFitness fitness_traits; // it's its own traits class :-) + + PyFitness() : object() {} + + template + PyFitness(const T& o) : object(o) {} + + + static unsigned nObjectives() { return objective_info.size(); } + static double tol() { return 1e-6; } + static bool maximizing(int which) { return objective_info[which] > 0; } + + static void setObjectivesSize(int sz) { objective_info.resize(sz); } + static void setObjectivesValue(unsigned which, int value) + { + if (which >= objective_info.size()) + { + throw index_error("Too few elements allocated, resize objectives first"); + } + + objective_info[which] = value; + } + + static std::vector objective_info; + + bool dominates(const PyFitness& oth) const; + + double operator[](int i) const + { + extract x(object::operator[](i)); + + if (!x.check()) + throw runtime_error("PyFitness: does not contain doubles"); + return x(); + } + + bool operator<(const PyFitness& other) const + { + if (objective_info.size() == 0) + { + const object& self = *this; + const object& oth = other; + return self < oth; + } + // otherwise use objective_info + + for (unsigned i = 0; i < objective_info.size(); ++i) + { + double a = objective_info[i] * operator[](i); + double b = objective_info[i] * other[i]; + + if ( fabs(a - b) > tol()) + { + if (a < b) + return true; + return false; + } + } + + return false; + } + + bool operator>(const PyFitness& other) const + { + return other.operator<(*this); + } + + //void printOn(ostream& os) const { const object& o = *this; os << o; } + //friend ostream& operator<<(ostream& os, const PyFitness& p) { p.printOn(os); return os; } + //friend istream& operator>>(istream& is, PyFitness& p) { object o; is >> o; p = o; return is; } +}; + +struct PyEO : public EO< PyFitness > +{ + typedef PyFitness Fitness; + + object getFitness() const { return invalid()? Fitness(): fitness(); } + void setFitness(object f) { if (f == Fitness()) invalidate(); else fitness(f); } + + object getGenome() const { return genome; } + void setGenome(object g) { genome = g; } + object genome; + + std::string to_string() const + { + std::string result; + result += extract(str(getFitness())); + result += ' '; + result += extract(str(genome)); + return result; + } + + bool operator<(const PyEO& other) const { return EO::operator<(other); } + bool operator>(const PyEO& other) const { return EO::operator>(other); } + +}; + +ostream& operator<<(ostream& os, const PyEO& _eo); + +struct PyEO_pickle_suite : boost::python::pickle_suite +{ + typedef PyEO::Fitness Fitness; + + static + boost::python::tuple getstate(const PyEO& _eo) + { + return make_tuple(_eo.getFitness(), _eo.genome); + } + static + void setstate(PyEO& _eo, boost::python::tuple pickled) + { + _eo.setFitness( Fitness(pickled[0]) ); + _eo.genome = pickled[1]; + } +}; + +#endif