summaryrefslogtreecommitdiff
path: root/bottleneck/include/dnn
diff options
context:
space:
mode:
Diffstat (limited to 'bottleneck/include/dnn')
-rw-r--r--bottleneck/include/dnn/geometry/euclidean-fixed.h162
-rw-r--r--bottleneck/include/dnn/local/kd-tree.h106
-rw-r--r--bottleneck/include/dnn/local/kd-tree.hpp296
-rw-r--r--bottleneck/include/dnn/local/search-functors.h119
-rw-r--r--bottleneck/include/dnn/parallel/tbb.h235
-rw-r--r--bottleneck/include/dnn/parallel/utils.h100
-rw-r--r--bottleneck/include/dnn/utils.h47
7 files changed, 1065 insertions, 0 deletions
diff --git a/bottleneck/include/dnn/geometry/euclidean-fixed.h b/bottleneck/include/dnn/geometry/euclidean-fixed.h
new file mode 100644
index 0000000..f45b980
--- /dev/null
+++ b/bottleneck/include/dnn/geometry/euclidean-fixed.h
@@ -0,0 +1,162 @@
+#ifndef HERA_BT_DNN_GEOMETRY_EUCLIDEAN_FIXED_H
+#define HERA_BT_DNN_GEOMETRY_EUCLIDEAN_FIXED_H
+
+#include <boost/operators.hpp>
+#include <boost/array.hpp>
+#include <boost/range/value_type.hpp>
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/base_object.hpp>
+
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+
+#include "../parallel/tbb.h" // for dnn::vector<...>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ // TODO: wrap in another namespace (e.g., euclidean)
+
+ template<size_t D, typename Real = double>
+ struct Point:
+ boost::addable< Point<D,Real>,
+ boost::subtractable< Point<D,Real>,
+ boost::dividable2< Point<D, Real>, Real,
+ boost::multipliable2< Point<D, Real>, Real > > > >,
+ public boost::array<Real, D>
+ {
+ public:
+ typedef Real Coordinate;
+ typedef Real DistanceType;
+
+
+ public:
+ Point(size_t id = 0): id_(id) {}
+ template<size_t DD>
+ Point(const Point<DD,Real>& p, size_t id = 0):
+ id_(id) { *this = p; }
+
+ static size_t dimension() { return D; }
+
+ // Assign a point of different dimension
+ template<size_t DD>
+ Point& operator=(const Point<DD,Real>& p) { for (size_t i = 0; i < (D < DD ? D : DD); ++i) (*this)[i] = p[i]; if (DD < D) for (size_t i = DD; i < D; ++i) (*this)[i] = 0; return *this; }
+
+ Point& operator+=(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] += p[i]; return *this; }
+ Point& operator-=(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] -= p[i]; return *this; }
+ Point& operator/=(Real r) { for (size_t i = 0; i < D; ++i) (*this)[i] /= r; return *this; }
+ Point& operator*=(Real r) { for (size_t i = 0; i < D; ++i) (*this)[i] *= r; return *this; }
+
+ Real norm2() const { Real n = 0; for (size_t i = 0; i < D; ++i) n += (*this)[i] * (*this)[i]; return n; }
+ Real max_norm() const
+ {
+ Real m = std::fabs((*this)[0]);
+ for (size_t i = 1; i < D; ++i)
+ if (std::fabs((*this)[i]) > m)
+ m = std::fabs((*this)[i]);
+ return m; }
+
+ // quick and dirty for now; make generic later
+ //DistanceType distance(const Point& other) const { return sqrt(sq_distance(other)); }
+ //DistanceType sq_distance(const Point& other) const { return (other - *this).norm2(); }
+
+ DistanceType distance(const Point& other) const { return (other - *this).max_norm(); }
+ DistanceType sq_distance(const Point& other) const { DistanceType d = distance(other); return d*d; }
+
+ size_t id() const { return id_; }
+ size_t& id() { return id_; }
+
+ private:
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object< boost::array<Real,D> >(*this) & id_; }
+
+ private:
+ size_t id_;
+ };
+
+ template<size_t D, typename Real>
+ std::ostream&
+ operator<<(std::ostream& out, const Point<D,Real>& p)
+ { out << p[0]; for (size_t i = 1; i < D; ++i) out << " " << p[i]; return out; }
+
+
+ template<class Point>
+ struct PointTraits; // intentionally undefined; should be specialized for each type
+
+ template<size_t D, typename Real>
+ struct PointTraits< Point<D, Real> > // specialization for dnn::Point
+ {
+ typedef Point<D,Real> PointType;
+ typedef const PointType* PointHandle;
+ typedef std::vector<PointType> PointContainer;
+
+ typedef typename PointType::Coordinate Coordinate;
+ typedef typename PointType::DistanceType DistanceType;
+
+ static DistanceType
+ distance(const PointType& p1, const PointType& p2) { return p1.distance(p2); }
+ static DistanceType
+ distance(PointHandle p1, PointHandle p2) { return distance(*p1,*p2); }
+ static DistanceType
+ sq_distance(const PointType& p1,
+ const PointType& p2) { return p1.sq_distance(p2); }
+ static DistanceType
+ sq_distance(PointHandle p1, PointHandle p2) { return sq_distance(*p1,*p2); }
+ static size_t dimension() { return D; }
+ static Real coordinate(const PointType& p, size_t i) { return p[i]; }
+ static Real& coordinate(PointType& p, size_t i) { return p[i]; }
+ static Real coordinate(PointHandle p, size_t i) { return coordinate(*p,i); }
+
+ static size_t id(const PointType& p) { return p.id(); }
+ static size_t& id(PointType& p) { return p.id(); }
+ static size_t id(PointHandle p) { return id(*p); }
+
+ static PointHandle
+ handle(const PointType& p) { return &p; }
+ static const PointType&
+ point(PointHandle ph) { return *ph; }
+
+ void swap(PointType& p1, PointType& p2) const { return std::swap(p1, p2); }
+
+ static PointContainer
+ container(size_t n = 0, const PointType& p = PointType()) { return PointContainer(n, p); }
+ static typename PointContainer::iterator
+ iterator(PointContainer& c, PointHandle ph) { return c.begin() + (ph - &c[0]); }
+ static typename PointContainer::const_iterator
+ iterator(const PointContainer& c, PointHandle ph) { return c.begin() + (ph - &c[0]); }
+
+ private:
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int version) {}
+ };
+
+ template<class PointContainer>
+ void read_points(const std::string& filename, PointContainer& points)
+ {
+ typedef typename boost::range_value<PointContainer>::type Point;
+ typedef typename PointTraits<Point>::Coordinate Coordinate;
+
+ std::ifstream in(filename.c_str());
+ std::string line;
+ while(std::getline(in, line))
+ {
+ if (line[0] == '#') continue; // comment line in the file
+ std::stringstream linestream(line);
+ Coordinate x;
+ points.push_back(Point());
+ size_t i = 0;
+ while (linestream >> x)
+ points.back()[i++] = x;
+ }
+ }
+} // dnn
+} // bt
+} // hera
+#endif
diff --git a/bottleneck/include/dnn/local/kd-tree.h b/bottleneck/include/dnn/local/kd-tree.h
new file mode 100644
index 0000000..c1aed2b
--- /dev/null
+++ b/bottleneck/include/dnn/local/kd-tree.h
@@ -0,0 +1,106 @@
+#ifndef HERA_BT_DNN_LOCAL_KD_TREE_H
+#define HERA_BT_DNN_LOCAL_KD_TREE_H
+
+#include "../utils.h"
+#include "search-functors.h"
+
+#include <unordered_map>
+#include <stack>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/range/value_type.hpp>
+
+#include <boost/static_assert.hpp>
+#include <boost/type_traits.hpp>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ // Weighted KDTree
+ // Traits_ provides Coordinate, DistanceType, PointType, dimension(), distance(p1,p2), coordinate(p,i)
+ template< class Traits_ >
+ class KDTree
+ {
+ public:
+ typedef Traits_ Traits;
+ typedef hera::bt::dnn::HandleDistance<KDTree> HandleDistance;
+
+ typedef typename Traits::PointType Point;
+ typedef typename Traits::PointHandle PointHandle;
+ typedef typename Traits::Coordinate Coordinate;
+ typedef typename Traits::DistanceType DistanceType;
+ typedef std::vector<PointHandle> HandleContainer;
+ typedef std::vector<HandleDistance> HDContainer; // TODO: use tbb::scalable_allocator
+ typedef HDContainer Result;
+ typedef std::vector<DistanceType> DistanceContainer;
+ typedef std::unordered_map<PointHandle, size_t> HandleMap;
+ //private:
+ typedef typename HandleContainer::iterator HCIterator;
+ typedef std::tuple<HCIterator, HCIterator, size_t, ssize_t> KDTreeNode;
+ typedef std::tuple<HCIterator, HCIterator> KDTreeNodeNoCut;
+
+ //BOOST_STATIC_ASSERT_MSG(has_coordinates<Traits, PointHandle, int>::value, "KDTree requires coordinates");
+
+ public:
+ KDTree(const Traits& traits):
+ traits_(traits) {}
+
+ KDTree(const Traits& traits, HandleContainer&& handles);
+
+ template<class Range>
+ KDTree(const Traits& traits, const Range& range);
+
+ template<class Range>
+ void init(const Range& range);
+
+ HandleDistance find(PointHandle q) const;
+ Result findR(PointHandle q, DistanceType r) const; // all neighbors within r
+ Result findFirstR(PointHandle q, DistanceType r) const; // first neighbor within r
+ Result findK(PointHandle q, size_t k) const; // k nearest neighbors
+
+ HandleDistance find(const Point& q) const { return find(traits().handle(q)); }
+ Result findR(const Point& q, DistanceType r) const { return findR(traits().handle(q), r); }
+ Result findFirstR(const Point& q, DistanceType r) const { return findFirstR(traits().handle(q), r); }
+ Result findK(const Point& q, size_t k) const { return findK(traits().handle(q), k); }
+
+
+
+ template<class ResultsFunctor>
+ void search(PointHandle q, ResultsFunctor& rf) const;
+
+ const Traits& traits() const { return traits_; }
+
+ void get_path_to_root(const size_t idx, std::stack<KDTreeNodeNoCut>& s);
+ // to support deletion
+ void init_n_elems();
+ void delete_point(const size_t idx);
+ void delete_point(PointHandle p);
+ void update_n_elems(const ssize_t idx, const int delta);
+ void increase_n_elems(const ssize_t idx);
+ void decrease_n_elems(const ssize_t idx);
+ size_t get_num_points() const { return num_points_; }
+ //private:
+ void init();
+
+
+ struct CoordinateComparison;
+ struct OrderTree;
+
+ //private:
+ Traits traits_;
+ HandleContainer tree_;
+ std::vector<char> delete_flags_;
+ std::vector<int> subtree_n_elems;
+ HandleMap indices_;
+ std::vector<ssize_t> parents_;
+
+ size_t num_points_;
+ };
+} // dnn
+} // bt
+} // hera
+#include "kd-tree.hpp"
+
+#endif
diff --git a/bottleneck/include/dnn/local/kd-tree.hpp b/bottleneck/include/dnn/local/kd-tree.hpp
new file mode 100644
index 0000000..5b84eb0
--- /dev/null
+++ b/bottleneck/include/dnn/local/kd-tree.hpp
@@ -0,0 +1,296 @@
+#include <boost/range/counting_range.hpp>
+#include <boost/range/algorithm_ext/push_back.hpp>
+#include <boost/range.hpp>
+
+#include <queue>
+
+#include "../parallel/tbb.h"
+
+template<class T>
+hera::bt::dnn::KDTree<T>::KDTree(const Traits& traits, HandleContainer&& handles):
+ traits_(traits),
+ tree_(std::move(handles)),
+ delete_flags_(handles.size(), static_cast<char>(0) ),
+ subtree_n_elems(handles.size(), static_cast<size_t>(0)),
+ num_points_(handles.size())
+{
+ init();
+}
+
+template<class T>
+template<class Range>
+hera::bt::dnn::KDTree<T>::KDTree(const Traits& traits, const Range& range):
+ traits_(traits)
+{
+ init(range);
+}
+
+template<class T>
+template<class Range>
+void hera::bt::dnn::KDTree<T>::init(const Range& range)
+{
+ size_t sz = std::distance(std::begin(range), std::end(range));
+ subtree_n_elems = std::vector<int>(sz, 0);
+ delete_flags_ = std::vector<char>(sz, 0);
+ num_points_ = sz;
+ tree_.reserve(sz);
+ for (PointHandle h : range)
+ tree_.push_back(h);
+ parents_.resize(sz, -1);
+ init();
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::init()
+{
+ if (tree_.empty())
+ return;
+
+#if defined(TBB)
+ task_group g;
+ g.run(OrderTree(this, tree_.begin(), tree_.end(), -1, 0, traits()));
+ g.wait();
+#else
+ OrderTree(this, tree_.begin(), tree_.end(), -1, 0, traits()).serial();
+#endif
+
+ for (size_t i = 0; i < tree_.size(); ++i)
+ indices_[tree_[i]] = i;
+ init_n_elems();
+}
+
+template<class T>
+struct
+hera::bt::dnn::KDTree<T>::OrderTree
+{
+ OrderTree(KDTree* tree_, HCIterator b_, HCIterator e_, ssize_t p_, size_t i_, const Traits& traits_):
+ tree(tree_), b(b_), e(e_), p(p_), i(i_), traits(traits_) {}
+
+ void operator()() const
+ {
+ if (e - b < 1000)
+ {
+ serial();
+ return;
+ }
+
+ HCIterator m = b + (e - b)/2;
+ ssize_t im = m - tree->tree_.begin();
+ tree->parents_[im] = p;
+
+ CoordinateComparison cmp(i, traits);
+ std::nth_element(b,m,e, cmp);
+ size_t next_i = (i + 1) % traits.dimension();
+
+ task_group g;
+ if (b < m - 1) g.run(OrderTree(tree, b, m, im, next_i, traits));
+ if (e > m + 2) g.run(OrderTree(tree, m+1, e, im, next_i, traits));
+ g.wait();
+ }
+
+ void serial() const
+ {
+ std::queue<KDTreeNode> q;
+ q.push(KDTreeNode(b,e,p,i));
+ while (!q.empty())
+ {
+ HCIterator b, e; ssize_t p; size_t i;
+ std::tie(b,e,p,i) = q.front();
+ q.pop();
+ HCIterator m = b + (e - b)/2;
+ ssize_t im = m - tree->tree_.begin();
+ tree->parents_[im] = p;
+
+ CoordinateComparison cmp(i, traits);
+ std::nth_element(b,m,e, cmp);
+ size_t next_i = (i + 1) % traits.dimension();
+
+ // Replace with a size condition instead?
+ if (m - b > 1)
+ q.push(KDTreeNode(b, m, im, next_i));
+ else if (b < m)
+ tree->parents_[im - 1] = im;
+ if (e - m > 2)
+ q.push(KDTreeNode(m+1, e, im, next_i));
+ else if (e > m + 1)
+ tree->parents_[im + 1] = im;
+ }
+ }
+
+ KDTree* tree;
+ HCIterator b, e;
+ ssize_t p;
+ size_t i;
+ const Traits& traits;
+};
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::update_n_elems(ssize_t idx, const int delta)
+// add delta to the number of points in node idx and update subtree_n_elems
+// for all parents of the node idx
+{
+ //std::cout << "subtree_n_elems.size = " << subtree_n_elems.size() << std::endl;
+ // update the node itself
+ while (idx != -1)
+ {
+ //std::cout << idx << std::endl;
+ subtree_n_elems[idx] += delta;
+ idx = parents_[idx];
+ }
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::increase_n_elems(const ssize_t idx)
+{
+ update_n_elems(idx, static_cast<ssize_t>(1));
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::decrease_n_elems(const ssize_t idx)
+{
+ update_n_elems(idx, static_cast<ssize_t>(-1));
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::init_n_elems()
+{
+ for(size_t idx = 0; idx < tree_.size(); ++idx) {
+ increase_n_elems(idx);
+ }
+}
+
+
+template<class T>
+template<class ResultsFunctor>
+void hera::bt::dnn::KDTree<T>::search(PointHandle q, ResultsFunctor& rf) const
+{
+ typedef typename HandleContainer::const_iterator HCIterator;
+ typedef std::tuple<HCIterator, HCIterator, size_t> KDTreeNode;
+
+ if (tree_.empty())
+ return;
+
+ DistanceType D = std::numeric_limits<DistanceType>::infinity();
+
+ // TODO: use tbb::scalable_allocator for the queue
+ std::queue<KDTreeNode> nodes;
+
+ nodes.push(KDTreeNode(tree_.begin(), tree_.end(), 0));
+
+ //std::cout << "started kdtree::search" << std::endl;
+
+ while (!nodes.empty())
+ {
+ HCIterator b, e; size_t i;
+ std::tie(b,e,i) = nodes.front();
+ nodes.pop();
+
+ CoordinateComparison cmp(i, traits());
+ i = (i + 1) % traits().dimension();
+
+ HCIterator m = b + (e - b)/2;
+ size_t m_idx = m - tree_.begin();
+ // ignore deleted points
+ if ( delete_flags_[m_idx] == 0 ) {
+ DistanceType dist = traits().distance(q, *m);
+ // + weights_[m - tree_.begin()];
+ //std::cout << "Supplied to functor: m : ";
+ //std::cout << "(" << (*(*m))[0] << ", " << (*(*m))[1] << ")";
+ //std::cout << " and q : ";
+ //std::cout << "(" << (*q)[0] << ", " << (*q)[1] << ")" << std::endl;
+ //std::cout << "dist^q + weight = " << dist << std::endl;
+ //std::cout << "weight = " << weights_[m - tree_.begin()] << std::endl;
+ //std::cout << "dist = " << traits().distance(q, *m) << std::endl;
+ //std::cout << "dist^q = " << pow(traits().distance(q, *m), wassersteinPower) << std::endl;
+
+ D = rf(*m, dist);
+ }
+ // we are really searching w.r.t L_\infty ball; could prune better with an L_2 ball
+ Coordinate diff = cmp.diff(q, *m); // diff returns signed distance
+ DistanceType diffToWasserPower = (diff > 0 ? 1.0 : -1.0) * fabs(diff);
+
+ size_t lm = m + 1 + (e - (m+1))/2 - tree_.begin();
+ if ( e > m + 1 and subtree_n_elems[lm] > 0 ) {
+ if (e > m + 1 && diffToWasserPower >= -D) {
+ nodes.push(KDTreeNode(m+1, e, i));
+ }
+ }
+
+ size_t rm = b + (m - b) / 2 - tree_.begin();
+ if ( subtree_n_elems[rm] > 0 ) {
+ if (b < m && diffToWasserPower <= D) {
+ nodes.push(KDTreeNode(b, m, i));
+ }
+ }
+ }
+ //std::cout << "exited kdtree::search" << std::endl;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::HandleDistance hera::bt::dnn::KDTree<T>::find(PointHandle q) const
+{
+ hera::bt::dnn::NNRecord<HandleDistance> nn;
+ search(q, nn);
+ return nn.result;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::Result hera::bt::dnn::KDTree<T>::findR(PointHandle q, DistanceType r) const
+{
+ hera::bt::dnn::rNNRecord<HandleDistance> rnn(r);
+ search(q, rnn);
+ //std::sort(rnn.result.begin(), rnn.result.end());
+ return rnn.result;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::Result hera::bt::dnn::KDTree<T>::findFirstR(PointHandle q, DistanceType r) const
+{
+ hera::bt::dnn::firstrNNRecord<HandleDistance> rnn(r);
+ search(q, rnn);
+ return rnn.result;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::Result hera::bt::dnn::KDTree<T>::findK(PointHandle q, size_t k) const
+{
+ hera::bt::dnn::kNNRecord<HandleDistance> knn(k);
+ search(q, knn);
+ // do we need this???
+ std::sort(knn.result.begin(), knn.result.end());
+ return knn.result;
+}
+
+template<class T>
+struct hera::bt::dnn::KDTree<T>::CoordinateComparison
+{
+ CoordinateComparison(size_t i, const Traits& traits):
+ i_(i), traits_(traits) {}
+
+ bool operator()(PointHandle p1, PointHandle p2) const { return coordinate(p1) < coordinate(p2); }
+ Coordinate diff(PointHandle p1, PointHandle p2) const { return coordinate(p1) - coordinate(p2); }
+
+ Coordinate coordinate(PointHandle p) const { return traits_.coordinate(p, i_); }
+ size_t axis() const { return i_; }
+
+ private:
+ size_t i_;
+ const Traits& traits_;
+};
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::delete_point(const size_t idx)
+{
+ // prevent double deletion
+ assert(delete_flags_[idx] == 0);
+ delete_flags_[idx] = 1;
+ decrease_n_elems(idx);
+ --num_points_;
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::delete_point(PointHandle p)
+{
+ delete_point(indices_[p]);
+}
+
diff --git a/bottleneck/include/dnn/local/search-functors.h b/bottleneck/include/dnn/local/search-functors.h
new file mode 100644
index 0000000..63ad11d
--- /dev/null
+++ b/bottleneck/include/dnn/local/search-functors.h
@@ -0,0 +1,119 @@
+#ifndef HERA_BT_DNN_LOCAL_SEARCH_FUNCTORS_H
+#define HERA_BT_DNN_LOCAL_SEARCH_FUNCTORS_H
+
+#include <boost/range/algorithm/heap_algorithm.hpp>
+
+namespace hera
+{
+namespace bt
+{
+namespace dnn
+{
+
+template<class NN>
+struct HandleDistance
+{
+ typedef typename NN::PointHandle PointHandle;
+ typedef typename NN::DistanceType DistanceType;
+ typedef typename NN::HDContainer HDContainer;
+
+ HandleDistance() {}
+ HandleDistance(PointHandle pp, DistanceType dd):
+ p(pp), d(dd) {}
+ bool operator<(const HandleDistance& other) const { return d < other.d; }
+
+ PointHandle p;
+ DistanceType d;
+};
+
+template<class HandleDistance>
+struct NNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+
+ NNRecord() { result.d = std::numeric_limits<DistanceType>::infinity(); }
+ DistanceType operator()(PointHandle p, DistanceType d) { if (d < result.d) { result.p = p; result.d = d; } return result.d; }
+ HandleDistance result;
+};
+
+template<class HandleDistance>
+struct rNNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+ typedef typename HandleDistance::HDContainer HDContainer;
+
+ rNNRecord(DistanceType r_): r(r_) {}
+ DistanceType operator()(PointHandle p, DistanceType d)
+ {
+ if (d <= r)
+ result.push_back(HandleDistance(p,d));
+ return r;
+ }
+
+ DistanceType r;
+ HDContainer result;
+};
+
+template<class HandleDistance>
+struct firstrNNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+ typedef typename HandleDistance::HDContainer HDContainer;
+
+ firstrNNRecord(DistanceType r_): r(r_) {}
+
+ DistanceType operator()(PointHandle p, DistanceType d)
+ {
+ if (d <= r) {
+ result.push_back(HandleDistance(p,d));
+ return -100000000.0;
+ } else {
+ return r;
+ }
+ }
+
+ DistanceType r;
+ HDContainer result;
+};
+
+
+template<class HandleDistance>
+struct kNNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+ typedef typename HandleDistance::HDContainer HDContainer;
+
+ kNNRecord(unsigned k_): k(k_) {}
+ DistanceType operator()(PointHandle p, DistanceType d)
+ {
+ if (result.size() < k)
+ {
+ result.push_back(HandleDistance(p,d));
+ boost::push_heap(result);
+ if (result.size() < k)
+ return std::numeric_limits<DistanceType>::infinity();
+ } else if (d < result[0].d)
+ {
+ boost::pop_heap(result);
+ result.back() = HandleDistance(p,d);
+ boost::push_heap(result);
+ }
+ if ( result.size() > 1 ) {
+ assert( result[0].d >= result[1].d );
+ }
+ return result[0].d;
+ }
+
+ unsigned k;
+ HDContainer result;
+};
+
+} // dnn
+} // bt
+} // hera
+
+#endif // HERA_BT_DNN_LOCAL_SEARCH_FUNCTORS_H
diff --git a/bottleneck/include/dnn/parallel/tbb.h b/bottleneck/include/dnn/parallel/tbb.h
new file mode 100644
index 0000000..14f0093
--- /dev/null
+++ b/bottleneck/include/dnn/parallel/tbb.h
@@ -0,0 +1,235 @@
+#ifndef HERA_BT_PARALLEL_H
+#define HERA_BT_PARALLEL_H
+
+#ifndef FOR_R_TDA
+#include <iostream>
+#endif
+
+#include <vector>
+
+#include <boost/range.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#ifdef TBB
+
+#include <tbb/tbb.h>
+#include <tbb/concurrent_hash_map.h>
+#include <tbb/scalable_allocator.h>
+
+#include <boost/serialization/split_free.hpp>
+#include <boost/serialization/collections_load_imp.hpp>
+#include <boost/serialization/collections_save_imp.hpp>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ using tbb::mutex;
+ using tbb::task_scheduler_init;
+ using tbb::task_group;
+ using tbb::task;
+
+ template<class T>
+ struct vector
+ {
+ typedef tbb::concurrent_vector<T> type;
+ };
+
+ template<class T>
+ struct atomic
+ {
+ typedef tbb::atomic<T> type;
+ static T compare_and_swap(type& v, T n, T o) { return v.compare_and_swap(n,o); }
+ };
+
+ template<class Iterator, class F>
+ void do_foreach(Iterator begin, Iterator end, const F& f) { tbb::parallel_do(begin, end, f); }
+
+ template<class Range, class F>
+ void for_each_range_(const Range& r, const F& f)
+ {
+ for (typename Range::iterator cur = r.begin(); cur != r.end(); ++cur)
+ f(*cur);
+ }
+
+ template<class F>
+ void for_each_range(size_t from, size_t to, const F& f)
+ {
+ //static tbb::affinity_partitioner ap;
+ //tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f), ap);
+ tbb::parallel_for(from, to, f);
+ }
+
+ template<class Container, class F>
+ void for_each_range(const Container& c, const F& f)
+ {
+ //static tbb::affinity_partitioner ap;
+ //tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f), ap);
+ tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::const_range_type, F>, _1, f));
+ }
+
+ template<class Container, class F>
+ void for_each_range(Container& c, const F& f)
+ {
+ //static tbb::affinity_partitioner ap;
+ //tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f), ap);
+ tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f));
+ }
+
+ template<class ID, class NodePointer, class IDTraits, class Allocator>
+ struct map_traits
+ {
+ typedef tbb::concurrent_hash_map<ID, NodePointer, IDTraits, Allocator> type;
+ typedef typename type::range_type range;
+ };
+
+ struct progress_timer
+ {
+ progress_timer(): start(tbb::tick_count::now()) {}
+ ~progress_timer()
+ {
+#ifndef FOR_R_TDA
+ std::cout << (tbb::tick_count::now() - start).seconds() << " s" << std::endl;
+#endif
+ }
+
+ tbb::tick_count start;
+ };
+}
+}
+}
+
+// Serialization for tbb::concurrent_vector<...>
+namespace boost
+{
+ namespace serialization
+ {
+ template<class Archive, class T, class A>
+ void save(Archive& ar, const tbb::concurrent_vector<T,A>& v, const unsigned int file_version)
+ { stl::save_collection(ar, v); }
+
+ template<class Archive, class T, class A>
+ void load(Archive& ar, tbb::concurrent_vector<T,A>& v, const unsigned int file_version)
+ {
+ stl::load_collection<Archive,
+ tbb::concurrent_vector<T,A>,
+ stl::archive_input_seq< Archive, tbb::concurrent_vector<T,A> >,
+ stl::reserve_imp< tbb::concurrent_vector<T,A> >
+ >(ar, v);
+ }
+
+ template<class Archive, class T, class A>
+ void serialize(Archive& ar, tbb::concurrent_vector<T,A>& v, const unsigned int file_version)
+ { split_free(ar, v, file_version); }
+
+ template<class Archive, class T>
+ void save(Archive& ar, const tbb::atomic<T>& v, const unsigned int file_version)
+ { T v_ = v; ar << v_; }
+
+ template<class Archive, class T>
+ void load(Archive& ar, tbb::atomic<T>& v, const unsigned int file_version)
+ { T v_; ar >> v_; v = v_; }
+
+ template<class Archive, class T>
+ void serialize(Archive& ar, tbb::atomic<T>& v, const unsigned int file_version)
+ { split_free(ar, v, file_version); }
+ }
+}
+
+#else
+
+#include <algorithm>
+#include <map>
+#include <boost/progress.hpp>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ template<class T>
+ struct vector
+ {
+ typedef ::std::vector<T> type;
+ };
+
+ template<class T>
+ struct atomic
+ {
+ typedef T type;
+ static T compare_and_swap(type& v, T n, T o) { if (v != o) return v; v = n; return o; }
+ };
+
+ template<class Iterator, class F>
+ void do_foreach(Iterator begin, Iterator end, const F& f) { std::for_each(begin, end, f); }
+
+ template<class F>
+ void for_each_range(size_t from, size_t to, const F& f)
+ {
+ for (size_t i = from; i < to; ++i)
+ f(i);
+ }
+
+ template<class Container, class F>
+ void for_each_range(Container& c, const F& f)
+ {
+ BOOST_FOREACH(const typename Container::value_type& i, c)
+ f(i);
+ }
+
+ template<class Container, class F>
+ void for_each_range(const Container& c, const F& f)
+ {
+ BOOST_FOREACH(const typename Container::value_type& i, c)
+ f(i);
+ }
+
+ struct mutex
+ {
+ struct scoped_lock
+ {
+ scoped_lock() {}
+ scoped_lock(mutex& ) {}
+ void acquire(mutex& ) const {}
+ void release() const {}
+ };
+ };
+
+ struct task_scheduler_init
+ {
+ task_scheduler_init(unsigned) {}
+ void initialize(unsigned) {}
+ static const unsigned automatic = 0;
+ static const unsigned deferred = 0;
+ };
+
+ struct task_group
+ {
+ template<class Functor>
+ void run(const Functor& f) const { f(); }
+ void wait() const {}
+ };
+
+ template<class ID, class NodePointer, class IDTraits, class Allocator>
+ struct map_traits
+ {
+ typedef std::map<ID, NodePointer,
+ typename IDTraits::Comparison,
+ Allocator> type;
+ typedef type range;
+ };
+
+ using boost::progress_timer;
+}
+}
+}
+
+#endif // TBB
+
+namespace dnn
+{
+ template<class Range, class F>
+ void do_foreach(const Range& range, const F& f) { do_foreach(boost::begin(range), boost::end(range), f); }
+}
+
+#endif
diff --git a/bottleneck/include/dnn/parallel/utils.h b/bottleneck/include/dnn/parallel/utils.h
new file mode 100644
index 0000000..9809e77
--- /dev/null
+++ b/bottleneck/include/dnn/parallel/utils.h
@@ -0,0 +1,100 @@
+#ifndef HERA_BT_PARALLEL_UTILS_H
+#define HERA_BT_PARALLEL_UTILS_H
+
+#include "../utils.h"
+
+namespace hera
+{
+namespace bt
+{
+namespace dnn
+{
+ // Assumes rng is synchronized across ranks
+ template<class DataVector, class RNGType, class SwapFunctor>
+ void shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const SwapFunctor& swap, DataVector empty = DataVector());
+
+ template<class DataVector, class RNGType>
+ void shuffle(mpi::communicator& world, DataVector& data, RNGType& rng)
+ {
+ typedef decltype(data[0]) T;
+ shuffle(world, data, rng, [](T& x, T& y) { std::swap(x,y); });
+ }
+}
+}
+}
+
+template<class DataVector, class RNGType, class SwapFunctor>
+void
+hera::bt::dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const SwapFunctor& swap, DataVector empty)
+{
+ // This is not a perfect shuffle: it dishes out data in chunks of 1/size.
+ // (It can be interpreted as generating a bistochastic matrix by taking the
+ // sum of size random permutation matrices.) Hopefully, it works for our purposes.
+
+ typedef typename RNGType::result_type RNGResult;
+
+ int size = world.size();
+ int rank = world.rank();
+
+ // Generate local seeds
+ boost::uniform_int<RNGResult> uniform;
+ RNGResult seed;
+ for (size_t i = 0; i < size; ++i)
+ {
+ RNGResult v = uniform(rng);
+ if (i == rank)
+ seed = v;
+ }
+ RNGType local_rng(seed);
+
+ // Shuffle local data
+ hera::bt::dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
+
+ // Decide how much of our data goes to i-th processor
+ std::vector<size_t> out_counts(size);
+ std::vector<int> ranks(boost::counting_iterator<int>(0),
+ boost::counting_iterator<int>(size));
+ for (size_t i = 0; i < size; ++i)
+ {
+ hera::bt::dnn::random_shuffle(ranks.begin(), ranks.end(), rng);
+ ++out_counts[ranks[rank]];
+ }
+
+ // Fill the outgoing array
+ size_t total = 0;
+ std::vector< DataVector > outgoing(size, empty);
+ for (size_t i = 0; i < size; ++i)
+ {
+ size_t count = data.size()*out_counts[i]/size;
+ if (total + count > data.size())
+ count = data.size() - total;
+
+ outgoing[i].reserve(count);
+ for (size_t j = total; j < total + count; ++j)
+ outgoing[i].push_back(data[j]);
+
+ total += count;
+ }
+
+ boost::uniform_int<size_t> uniform_outgoing(0,size-1); // in range [0,size-1]
+ while(total < data.size()) // send leftover to random processes
+ {
+ outgoing[uniform_outgoing(local_rng)].push_back(data[total]);
+ ++total;
+ }
+ data.clear();
+
+ // Exchange the data
+ std::vector< DataVector > incoming(size, empty);
+ mpi::all_to_all(world, outgoing, incoming);
+ outgoing.clear();
+
+ // Assemble our data
+ for(const DataVector& vec : incoming)
+ for (size_t i = 0; i < vec.size(); ++i)
+ data.push_back(vec[i]);
+ hera::bt::dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
+ // XXX: the final shuffle is irrelevant for our purposes. But it's also cheap.
+}
+
+#endif
diff --git a/bottleneck/include/dnn/utils.h b/bottleneck/include/dnn/utils.h
new file mode 100644
index 0000000..f4ce632
--- /dev/null
+++ b/bottleneck/include/dnn/utils.h
@@ -0,0 +1,47 @@
+#ifndef HERA_BT_DNN_UTILS_H
+#define HERA_BT_DNN_UTILS_H
+
+#include <boost/random/uniform_int.hpp>
+#include <boost/foreach.hpp>
+#include <boost/typeof/typeof.hpp>
+
+namespace hera
+{
+namespace bt
+{
+namespace dnn
+{
+
+template <typename T, typename... Args>
+struct has_coordinates
+{
+ template <typename C, typename = decltype( std::declval<C>().coordinate(std::declval<Args>()...) )>
+ static std::true_type test(int);
+
+ template <typename C>
+ static std::false_type test(...);
+
+ static constexpr bool value = decltype(test<T>(0))::value;
+};
+
+template<class RandomIt, class UniformRandomNumberGenerator, class SwapFunctor>
+void random_shuffle(RandomIt first, RandomIt last, UniformRandomNumberGenerator& g, const SwapFunctor& swap)
+{
+ size_t n = last - first;
+ boost::uniform_int<size_t> uniform(0,n);
+ for (size_t i = n-1; i > 0; --i)
+ swap(first[i], first[uniform(g,i+1)]); // picks a random number in [0,i] range
+}
+
+template<class RandomIt, class UniformRandomNumberGenerator>
+void random_shuffle(RandomIt first, RandomIt last, UniformRandomNumberGenerator& g)
+{
+ typedef decltype(*first) T;
+ random_shuffle(first, last, g, [](T& x, T& y) { std::swap(x,y); });
+}
+
+} // dnn
+} // bt
+} // hera
+
+#endif