Skip to content

Commit

Permalink
new kdtree storage
Browse files Browse the repository at this point in the history
  • Loading branch information
jbmouret committed Jan 20, 2019
1 parent a35890a commit 83cce55
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 100 deletions.
17 changes: 8 additions & 9 deletions examples/ex_map_elites.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
#include <sferes/qd/container/archive.hpp>
#include <sferes/qd/container/grid.hpp>
#include <sferes/qd/quality_diversity.hpp>
#include <sferes/qd/selector/tournament.hpp>
#include <sferes/qd/selector/uniform.hpp>

using namespace sferes::gen::evo_float;
Expand All @@ -67,16 +66,16 @@ struct Params {
// number of initial random points
SFERES_CONST size_t init_size = 1000;
// size of a batch
SFERES_CONST size_t size = 200;
SFERES_CONST size_t nb_gen = 500;
SFERES_CONST size_t dump_period = -1;
SFERES_CONST size_t size = 2000;
SFERES_CONST size_t nb_gen = 10001;
SFERES_CONST size_t dump_period = 1000;
};
struct parameters {
SFERES_CONST float min = -5;
SFERES_CONST float max = 5;
};
struct evo_float {
SFERES_CONST float cross_rate = 0.5f;
SFERES_CONST float cross_rate = 0.75f;
SFERES_CONST float mutation_rate = 0.1f;
SFERES_CONST float eta_m = 10.0f;
SFERES_CONST float eta_c = 10.0f;
Expand All @@ -86,7 +85,7 @@ struct Params {
struct qd {
SFERES_CONST size_t dim = 2;
SFERES_CONST size_t behav_dim = 2;
SFERES_ARRAY(size_t, grid_shape, 100, 100);
SFERES_ARRAY(size_t, grid_shape, 128, 128);
};
};

Expand Down Expand Up @@ -118,15 +117,15 @@ int main(int argc, char **argv)
typedef boost::fusion::vector<
stat::BestFit<phen_t, Params>,
stat::QdContainer<phen_t, Params>,
stat::QdProgress<phen_t, Params>,
stat::QdSelection<phen_t, Params>>
stat::QdProgress<phen_t, Params>
>
stat_t;
typedef modif::Dummy<> modifier_t;
typedef qd::MapElites<phen_t, eval_t, stat_t, modifier_t, Params>
qd_t;

qd_t qd;
qd.run();
run_ea(argc, argv, qd);
std::cout<<"best fitness:" << qd.stat<0>().best()->fit().value() << std::endl;
std::cout<<"archive size:" << qd.stat<1>().archive().size() << std::endl;
return 0;
Expand Down
88 changes: 33 additions & 55 deletions sferes/qd/container/archive.hpp
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
#ifndef QD_CONTAINER_ARCHIVE_HPP
#define QD_CONTAINER_ARCHIVE_HPP

#ifdef USE_KDTREE

#define LIBSSRCKDTREE_HAVE_BOOST
#include <ssrc/spatial/kd_tree.h>

#include "tools.hpp"
#include <tbb/parallel_for_each.h>

namespace sferes {
namespace qd {
namespace container {

template <typename Phen, typename Params>
class Archive {
template <typename Phen, typename Storage, typename Params> class Archive {
public:
typedef boost::shared_ptr<Phen> indiv_t;
typedef typename std::vector<indiv_t> pop_t;
typedef typename pop_t::iterator it_t;
typedef std::array<float, Params::qd::dim> point_t;
typedef ssrc::spatial::kd_tree<point_t, indiv_t> Tree;
// typedef ssrc::spatial::kd_tree<point_t, indiv_t> Tree;
typedef Storage storage_t;
typedef Eigen::Map<Eigen::VectorXd> point_t;

Archive() {}


void erase_content() { _archive = Tree(); }
void erase_content() { _archive = storage_t(); }

void get_full_content(std::vector<indiv_t>& content)
{
Expand All @@ -35,15 +30,15 @@ namespace sferes {

bool add(indiv_t i1)
{
// TODO
// update archive
// p_i1 is the behavioral coordinate of i1
point_t p_i1(i1->fit().desc().data(), i1->fit().desc().size());

if (i1->fit().dead())
return false;

if (_archive.size() == 0
|| /*_archive.size()<Params::nov::k ||*/ _dist(
get_nearest(i1, _archive, false)->fit().desc(), i1->fit().desc())
> Params::nov::l) // ADD because new
{
|| _dist(_archive.get_nearest().first, p_i1) > Params::nov::l) {
// this is novel enough, we add
direct_add(i1);
return true;
}
Expand All @@ -53,22 +48,16 @@ namespace sferes {
}
else // archive size min = 2
{
pop_t neigh_current;

// Be careful, the first one referes to nn
get_knn(i1, _archive, 2, neigh_current, false);
if (_dist(i1->fit().desc(), neigh_current[1]->fit().desc())
< (1 - Params::nov::eps)
* Params::nov::l) // too close the second NN -- this works better
// if(_dist(i1->fit().desc(), neigh_current[1]->fit().desc()) <
// Params::nov::l)//too close the second NN
auto knn_2 = _archive.knn(p_i1, 2);
if (_dist(p_i1, nn[1].first) < (1 - Params::nov::eps) * Params::nov::l)
{
// too close the second nearest neighbor, we skip
return false;
}
indiv_t nn = neigh_current[0];
indiv_t nn = neigh_cur;
std::vector<double> score_cur(2, 0), score_nn(2, 0);
score_cur[0] = i1->fit().value();
score_nn[0] = nn->fit().value();
score_nn[0] = nn->second->fit().value();
// Compute the Novelty
neigh_current.clear();
if (_archive.size() < Params::nov::k + 1) {
Expand All @@ -84,12 +73,9 @@ namespace sferes {
score_nn[1] = get_novelty(nn, _archive);
// TEST
int score = 0;
if ((score_cur[0]
>= (1 - sign(score_nn[0]) * Params::nov::eps) * score_nn[0]
&& score_cur[1]
>= (1 - sign(score_nn[1]) * Params::nov::eps) * score_nn[1])
&& ((score_cur[0] - score_nn[0]) * std::abs(score_nn[1])
> -(score_cur[1] - score_nn[1]) * std::abs(score_nn[0]))) {
if ((score_cur[0]>= (1 - sign(score_nn[0]) * Params::nov::eps) * score_nn[0]
&& score_cur[1]>= (1 - sign(score_nn[1]) * Params::nov::eps) * score_nn[1])
&& ((score_cur[0] - score_nn[0]) * std::abs(score_nn[1]) > -(score_cur[1] - score_nn[1]) * std::abs(score_nn[0]))) {
// add if significatively better on one objective
_replace(nn, i1);
return true;
Expand All @@ -110,17 +96,16 @@ namespace sferes {
std::for_each(parents.begin(), parents.end(), nov);
}

static indiv_t get_nearest(
const indiv_t& indiv, Tree& apop, const bool omit_query_point)
// be careful that if you query this on a member of the archive, you get...the
// member of the archive (distance = 0)
static indiv_t get_nearest(const indiv_t& indiv, const storage_t& apop)
{
typename Tree::key_type q;
Archive::_behavior_to_point(indiv->fit().desc(), &q);
typename Tree::iterator it = apop.find_nearest_neighbor(q, omit_query_point);
return it->second;
point_t point = _behavior_to_point(indiv->fit().desc());
return apop.nearest(point);
}

static void get_knn(const indiv_t& indiv, const Tree& apop, int k, pop_t& nearest,
const bool omit_query_point)
const bool omit_query_point)
{
typename Tree::key_type q;
Archive::_behavior_to_point(indiv->fit().desc(), &q);
Expand All @@ -135,7 +120,8 @@ namespace sferes {
// for (size_t z = 0; z < it->second && nearest.size() < Params::nov::k;
// ++z)
nearest.push_back(it->second);
assert(nearest.size() == k || nearest.size() == apop.size() || nearest.size() == apop.size());
assert(nearest.size() == k || nearest.size() == apop.size()
|| nearest.size() == apop.size());
}

static double get_novelty(const indiv_t& indiv, Tree& apop)
Expand Down Expand Up @@ -170,8 +156,7 @@ namespace sferes {
return sum / std::distance(begin, end); // Params::nov::k;
}

static std::pair<double, double> get_nov_and_lq(
const indiv_t& indiv, Tree& apop)
static std::pair<double, double> get_nov_and_lq(const indiv_t& indiv, Tree& apop)
{
pop_t nearest;
if (apop.size() < (Params::nov::k + 1))
Expand Down Expand Up @@ -211,7 +196,6 @@ namespace sferes {

const Tree& archive() const { return _archive; }


void direct_add(const indiv_t& tobeinserted)
{
point_t p;
Expand All @@ -232,17 +216,15 @@ namespace sferes {
direct_add(tobeinserted);
}

template <typename Behavior, typename Point>
static void _behavior_to_point(const Behavior& b, Point* p)
template <typename Behavior>
static inline point_t _behavior_to_point(const Behavior& b)
{
assert(p->size() == b.size());
for (size_t i = 0; i < p->size(); ++i)
(*p)[i] = b[i];
return p(b.data(), b.size());
}

struct _p_novelty {
Tree& _apop;
_p_novelty( Tree& apop) : _apop(apop) {}
_p_novelty(Tree& apop) : _apop(apop) {}
_p_novelty(const _p_novelty& ev) : _apop(ev._apop) {}

// use the Euclidean distance !
Expand All @@ -266,12 +248,8 @@ namespace sferes {
}
};

Tree _archive;
storage_t _archive;
};
} // namespace container
} // namespace qd
} // namespace sferes
#else
#warning "No KD_TREE library found: no qd/container/archive.hpp"
#endif
#endif
106 changes: 106 additions & 0 deletions sferes/qd/container/kdtree_storage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#ifndef SFERES_KDTREE_STORAGE_HPP_
#define SFERES_KDTREE_STORAGE_HPP_

#include <Eigen/Core>
#include <algorithm>
#include <numeric>
#include <vector>

#ifdef USE_KDTREE
#define LIBSSRCKDTREE_HAVE_BOOST

#include <ssrc/spatial/kd_tree.h>

namespace sferes {
namespace qd {
namespace container {
/// compute the k-nearest neighbors using a kd-tree (optional external library)
/// this is effective in low-dimensional spaces (< 10)
/// but not in high-dimensional spaces (> 10)
template <typename Value, int Dim> class KdtreeStorage {
public:
using point_t = std::array<float, Dim>;
using tree_t = ssrc::spatial::kd_tree<point_t, Value>;
using data_t = std::pair<Eigen::VectorXd, Value>;
using iterator_t = typename tree_t::iterator;
using const_iterator_t = typename tree_t::const_iterator;

/// number of elements in the archive
size_t size() const { return _data.size(); }

/// accesss to the elements
iterator_t begin() { return _data.begin(); }
iterator_t end() { return _data.end(); }
const_iterator_t begin() const { return _data.begin(); }
const_iterator_t end() const { return _data.end(); }

/// Add a point to the storage
void add(const Eigen::VectorXd& p, const Value& v)
{
point_t pp = convert_point(p);
_data.insert(pp, v);
}

/// Remove a point (return false if not found)
void remove(const Eigen::VectorXd& v)
{
point_t p = convert_point(v);
_data.remove(p);
}


// return the data that correspond to the k closest neighbors
// (if you neeed indices, store them in Value)
std::vector<data_t> knn(const Eigen::VectorXd& pp, int k) const
{
assert(!_data.empty());
k = std::min(k, (int)_data.size());
point_t p = convert_point(pp);
auto range = _data.find_nearest_neighbors(p, k, true);
std::vector<data_t> res;
for (auto it = range.first, end = range.second;
it != end && res.size() < k; ++it)
res.push_back(std::make_pair(convert_point(it->first), it->second));
assert(res.size() <= k);
assert(res.size() != 0);
return res;
}

// return the data that correspond to the closest neighbor
// (if you neeed indices, store them in Value)
data_t nearest(const Eigen::VectorXd& p) const
{
assert(!_data.empty());
point_t pp = convert_point(p);
auto it = _data.find_nearest_neighbor(pp, true);
return std::make_pair(convert_point(it->first), it->second);
}

/// Return true if the point p is already in the archive
bool contains(const Eigen::VectorXd& p) const
{
assert(0); // not implemented yet
}


point_t convert_point(const Eigen::VectorXd& v) const {
assert(v.size() == Dim);
point_t p;
for (size_t i = 0; i < v.size(); ++i)
p[i] = v[i];
return p;
}
Eigen::VectorXd convert_point(const point_t& v) const {
Eigen::VectorXd p(v.size());
for (size_t i = 0; i < v.size(); ++i)
p[i] = v[i];
return p;
}
protected:
tree_t _data;
};
} // namespace container
} // namespace qd
} // namespace sferes
#endif
#endif
Loading

0 comments on commit 83cce55

Please sign in to comment.