00001 
00006 #ifndef JMATH_JANN_HPP
00007 #define JMATH_JANN_HPP
00008 
00009 #include "jafarConfig.h"
00010 
00011 #ifdef HAVE_FLANN
00012 
00013 #include "jmath/jmathException.hpp"
00014 
00015 #include <flann/flann.hpp>
00016 #include <boost/numeric/ublas/vector.hpp>
00017 #include <boost/numeric/ublas/matrix.hpp>
00018 
00021 
00023 namespace ublas = boost::numeric::ublas;
00024 
00026 namespace jann {
00027   
00028   
00029   
00030   
00031   
00032   
00033   
00034   
00035   
00036   
00037   
00038   
00039   
00040   
00041   
00042   
00043   
00044   
00045   
00046   
00047   
00048   
00049   
00050   
00051   
00052   
00053   
00054   
00055   
00056   
00057   
00058   
00059   
00060   
00061   
00062   
00063   
00064 
00067   class search_params : public flann::SearchParams {
00068   public:
00074     search_params(int checks = 32, float eps = 0, bool sorted = true ) :
00075       flann::SearchParams(checks, eps, sorted){}    
00076   };
00077 
00083   template<typename D>
00084   class index_factory 
00085   {
00086     flann::Index<D> *m_index;
00088     template<typename T>
00089     inline void convert(const flann::Matrix<T>& flann_mat, ublas::matrix<T>& ublas_mat){
00090       JFR_ASSERT(((flann_mat.rows == ublas_mat.size1()) && 
00091                   (flann_mat.cols == ublas_mat.size2())),
00092                  "ublas matrix and flann matrix need to have the same sizes");
00093       for(size_t r = 0; r < flann_mat.rows; r++)
00094         for(size_t c = 0; c < flann_mat.cols; c++)
00095           ublas_mat(r,c) = flann_mat[r][c];
00096     }
00098     template<typename T>
00099     inline void convert(const ublas::matrix<T>& ublas_mat, flann::Matrix<T>& flann_mat){
00100       JFR_ASSERT(((flann_mat.rows == ublas_mat.size1()) && 
00101                   (flann_mat.cols == ublas_mat.size2())),
00102                  "ublas matrix and flann matrix need to have the same sizes");
00103       for(size_t r = 0; r < flann_mat.rows; r++)
00104         for(size_t c = 0; c < flann_mat.cols; c++)
00105           flann_mat[r][c] = ublas_mat(r,c);
00106     }
00110     template<typename T>
00111     inline void convert(const flann::Matrix<T>& flann_mat, ublas::vector<T>& ublas_vec){
00112       JFR_ASSERT(((flann_mat.rows == 1) && (flann_mat.cols >= ublas_vec.size())),
00113                  "ublas vector and flann matrix rows need to have the same sizes");
00114       if(flann_mat.cols > ublas_vec.size())
00115         ublas_vec.resize(flann_mat.cols);
00116       for(size_t counter = 0; counter < ublas_vec.size(); counter++)
00117           ublas_vec[counter] = flann_mat[0][counter];
00118     }
00120     template<typename T>
00121     inline void convert(const ublas::vector<T>& ublas_vec, flann::Matrix<T>& flann_mat){
00122       JFR_ASSERT(((flann_mat.rows == 1) && (flann_mat.cols >= ublas_vec.size())),
00123                  "ublas vector and flann matrix rows need to have the same sizes");
00124         for(size_t counter = 0; counter < ublas_vec.size(); counter++)
00125           flann_mat[0][counter] = ublas_vec[counter];
00126     }
00128     template<typename T>
00129     inline void convert(const std::vector<T>& std_vec, flann::Matrix<T>& flann_mat){
00130       JFR_ASSERT(((flann_mat.rows == 1) && (flann_mat.cols >= std_vec.size())),
00131                  "std vector and flann matrix rows need to have the same sizes");
00132         for(size_t counter = 0; counter < std_vec.size(); counter++)
00133           flann_mat[0][counter] = std_vec[counter];
00134     }
00135   public:
00137     typedef typename D::ElementType element;
00139     typedef typename D::ResultType result;
00141     flann::Matrix<element> dataset;
00143     index_factory() {}
00149     void operator()(const ublas::matrix<typename D::ElementType>& _data, 
00150                     const flann::IndexParams& params, D d = D() )
00151     {
00152       dataset = flann::Matrix<element>(new element[_data.size1()*_data.size2()], 
00153                                        _data.size1(), _data.size2());
00154       convert(_data, dataset);
00155       m_index = new flann::Index<D>(dataset, params, d);
00156     }
00162     index_factory(const ublas::matrix<typename D::ElementType>& _data, 
00163                   const flann::IndexParams& params, D d = D() )
00164     {
00165       dataset = flann::Matrix<element>(new element[_data.size1()*_data.size2()], 
00166                                        _data.size1(), _data.size2());
00167       convert(_data, dataset);
00168       m_index = new flann::Index<D>(dataset, params, d);
00169     }
00171     virtual ~index_factory() {
00172       dataset.free();
00173     }
00175     void build() {
00176       m_index->buildIndex();
00177     }
00179     void knn_search(const ublas::matrix<typename D::ElementType>& queries, 
00180                     ublas::matrix<int>& indices, 
00181                     ublas::matrix<result>& dists, int knn, 
00182                     const search_params& params) 
00183     {
00184       size_t rows = queries.size1();
00185       size_t cols = queries.size2();
00186       JFR_PRED_ERROR(cols == dataset.cols,
00187                      jafar::jmath::JmathException,
00188                      jafar::jmath::JmathException::WRONG_SIZE,
00189                      "queries and dataset need to have same columns size")
00190       JFR_PRED_ERROR(((indices.size2() >= (size_t)knn) && 
00191                       (dists.size2() >= (size_t)knn)),
00192                      jafar::jmath::JmathException,
00193                      jafar::jmath::JmathException::WRONG_SIZE,
00194                      "indices and dists must have at least "<<knn<<" columns")
00195       JFR_PRED_ERROR(((indices.size1() == rows) && (dists.size1() == rows)),
00196                      jafar::jmath::JmathException,
00197                      jafar::jmath::JmathException::WRONG_SIZE,
00198                      "queries, indices and dists need to be of same row size")
00199       flann::Matrix<element> _queries(new element[rows*cols], rows, cols);
00200       convert(queries, _queries);
00201       flann::Matrix<int> _indices(new int[rows*indices.size2()], rows, indices.size2());
00202       flann::Matrix<result> _dists(new result[rows*dists.size2()], rows, dists.size2());
00203       m_index->knnSearch(_queries, _indices, _dists, knn, params);
00204       convert(_indices, indices);
00205       convert(_dists, dists);
00206 
00207       _queries.free();
00208       _dists.free();
00209       _indices.free();
00210     }
00212     void knn_search(const ublas::vector<element>& query, 
00213                     ublas::vector<int>& indices, ublas::vector<result>& dists, 
00214                     int knn, const search_params& params) 
00215     {
00216       size_t length = query.size();
00217       JFR_PRED_ERROR(length == dataset.cols,
00218                      jafar::jmath::JmathException,
00219                      jafar::jmath::JmathException::WRONG_SIZE,
00220                      "query size must be of dataset columns size")
00221         JFR_PRED_ERROR(((indices.size() >= size_t(knn)) && (dists.size() >= size_t(knn))),
00222                      jafar::jmath::JmathException,
00223                      jafar::jmath::JmathException::WRONG_SIZE,
00224                      "indices and dists must be at least of size "<<knn)
00225       flann::Matrix<element> _query(new element[length], 1, length);
00226       convert(query,_query);
00227       flann::Matrix<int> _indices(new int[indices.size()], 1, indices.size());
00228       flann::Matrix<result> _dists(new result[dists.size()], 1, dists.size());
00229       m_index->knnSearch(_query, _indices, _dists, knn, params);
00230       convert(_indices, indices);
00231       convert(_dists, dists);
00232 
00233       _query.free();
00234       _dists.free();
00235       _indices.free();
00236     }
00238     void knn_search(const std::vector<element>& query, 
00239                     std::vector<int>& indices, std::vector<result>& dists, 
00240                     int knn, const search_params& params) 
00241     {
00242       size_t length = query.size();
00243       JFR_PRED_ERROR(length == dataset.cols,
00244                      jafar::jmath::JmathException,
00245                      jafar::jmath::JmathException::WRONG_SIZE,
00246                      "query size must be of dataset columns size")
00247         JFR_PRED_ERROR(((indices.size() >= size_t(knn)) && (dists.size() >= size_t(knn))),
00248                      jafar::jmath::JmathException,
00249                      jafar::jmath::JmathException::WRONG_SIZE,
00250                      "indices and dists must be at least of size "<<knn)
00251       flann::Matrix<element> _query(new element[length], 1, length);
00252       convert(query,_query);
00253       flann::Matrix<int> _indices(new int[indices.size()], 1, indices.size());
00254       flann::Matrix<result> _dists(new result[dists.size()], 1, dists.size());
00255       m_index->knnSearch(_query, _indices, _dists, knn, params);
00256       convert(_indices, indices);
00257       convert(_dists, dists);
00258 
00259       _query.free();
00260       _dists.free();
00261       _indices.free();
00262     }
00266     int radius_search(const ublas::vector<element>& query, 
00267                       ublas::matrix<int>& indices, 
00268                       ublas::matrix<result>& dists, float radius, 
00269                       const search_params& params) 
00270     {
00271       size_t length = query.size();
00272       JFR_PRED_ERROR(length == dataset.cols,
00273                      jafar::jmath::JmathException,
00274                      jafar::jmath::JmathException::WRONG_SIZE,
00275                      "query length and dataset columns must be equal")
00276       JFR_PRED_ERROR((indices.size2() == dists.size2()),
00277                      jafar::jmath::JmathException,
00278                      jafar::jmath::JmathException::WRONG_SIZE,
00279                      "indices and dists must have same columns number")
00280       flann::Matrix<element> _query(new element[length], 1, length);
00281       convert(query, _query);
00282       flann::Matrix<int> _indices(new int[indices.size1()*indices.size2()], indices.size1(), indices.size2());
00283       flann::Matrix<result> _dists(new result[dists.size1()*dists.size2()], dists.size1(), dists.size2());
00284       m_index->radiusSearch(_query, _indices, _dists, radius, params);
00285       convert(_indices, indices);
00286       convert(_dists, dists);
00287 
00288       _query.free();
00289       _dists.free();
00290       _indices.free();
00291     }
00295     int radius_search(const ublas::vector<element>& query,
00296                       ublas::vector<int>& indices, ublas::vector<result>& dists, 
00297                       float radius, const search_params& params) 
00298     {
00299       size_t length = query.size();
00300       JFR_PRED_ERROR(length == dataset.cols,
00301                      jafar::jmath::JmathException,
00302                      jafar::jmath::JmathException::WRONG_SIZE,
00303                      "query size must be of dataset columns size")
00304         JFR_PRED_ERROR((indices.size() == dists.size()),
00305                      jafar::jmath::JmathException,
00306                      jafar::jmath::JmathException::WRONG_SIZE,
00307                      "indices and dists must have same size")
00308       flann::Matrix<element> _query(new element[length], 1, length);
00309       convert(query,_query);
00310       flann::Matrix<int> _indices(new int[indices.size()], 1, indices.size());
00311       flann::Matrix<result> _dists(new result[dists.size()], 1, dists.size());
00312       int result = m_index->radiusSearch(_query, _indices, _dists, radius, params);
00313       convert(_indices, indices);
00314       convert(_dists, dists);
00315 
00316       _query.free();
00317       _dists.free();
00318       _indices.free();
00319       return result;
00320     }
00324     int radius_search(const std::vector<element>& query,
00325                       std::vector<int>& indices, std::vector<result>& dists, 
00326                       float radius, const search_params& params) 
00327     {
00328       size_t length = query.size();
00329       JFR_PRED_ERROR(length == dataset.cols,
00330                      jafar::jmath::JmathException,
00331                      jafar::jmath::JmathException::WRONG_SIZE,
00332                      "query size must be of dataset columns size")
00333         JFR_PRED_ERROR((indices.size() == dists.size()),
00334                      jafar::jmath::JmathException,
00335                      jafar::jmath::JmathException::WRONG_SIZE,
00336                      "indices and dists must have same size")
00337       flann::Matrix<element> _query(new element[length], 1, length);
00338       convert(query,_query);
00339       flann::Matrix<int> _indices(new int[indices.size()], 1, indices.size());
00340       flann::Matrix<result> _dists(new result[dists.size()], 1, dists.size());
00341       int result = m_index->radiusSearch(_query, _indices, _dists, radius, params);
00342       convert(_indices, indices);
00343       convert(_dists, dists);
00344 
00345       _query.free();
00346       _dists.free();
00347       _indices.free();
00348       return result;
00349     }
00350 
00351   public:
00353     void save(std::string filename) const
00354     {
00355       m_index->save(filename);
00356     }
00358     int data_size() const 
00359     {
00360       return m_index->veclen();
00361     }
00363     int size() const
00364     {
00365       return m_index->size();
00366     }
00368     flann::NNIndex<result>* index() 
00369     { 
00370       return m_index->nnIndex; 
00371     }
00373     const flann::IndexParams* parameters() { 
00374       return m_index->nnIndex->getParameters(); 
00375     }
00376   };
00377     
00378   
00379   
00380   
00381   
00382   
00383   
00384   
00385   
00386   
00387   
00388   
00389   
00390   
00391   
00392   
00393   
00394   
00395   
00396   
00397   
00398   
00399   
00400   
00401   
00402   
00403   
00404   
00405   
00406   
00407     
00411   template<typename DISTANCE>
00412   class linear_index : public index_factory<DISTANCE> {
00413   public:
00414     linear_index(const ublas::matrix<typename DISTANCE::ElementType>& dataset) : 
00415       index_factory<DISTANCE>(dataset, flann::LinearIndexParams()) {}
00416   };
00417     
00421   template<typename DISTANCE>
00422   class KD_tree_index : public index_factory<DISTANCE> {
00423   public:
00425     KD_tree_index(const ublas::matrix<typename DISTANCE::ElementType>& dataset, 
00426                   int nb_trees = 4) : 
00427       index_factory<DISTANCE>(dataset, flann::KDTreeIndexParams(nb_trees)) {}
00428     KD_tree_index() {}
00429     void operator() (const ublas::matrix<typename DISTANCE::ElementType>& dataset, 
00430                      int nb_trees = 4) {
00431       index_factory<DISTANCE>::operator()(dataset, flann::KDTreeIndexParams(nb_trees));
00432     }
00433   };
00434     
00438   template<typename DISTANCE>
00439   class K_means_index : public index_factory<DISTANCE> {
00440   public:
00447     K_means_index(const ublas::matrix<typename DISTANCE::ElementType>& dataset,
00448                   int branching = 32, int iterations = 11, 
00449                   flann::flann_centers_init_t init = flann::CENTERS_RANDOM, 
00450                   float cb_index = 0.2 ) :
00451       index_factory<DISTANCE>(dataset, 
00452                               flann::KMeansIndexParams(branching, iterations, 
00453                                                        init, cb_index)) {}
00454   };
00455 
00459   template<typename DISTANCE>
00460   class composite_index : public index_factory<DISTANCE> {
00468     composite_index(const ublas::matrix<typename DISTANCE::ElementType>& dataset,
00469                     int trees = 4, int branching = 32, int iterations = 11,
00470                     flann::flann_centers_init_t init = flann::CENTERS_RANDOM, float cb_index = 0.2 ) :
00471       index_factory<DISTANCE>(dataset, 
00472                               flann::CompositeIndexParams(trees, branching,
00473                                                           iterations, init,
00474                                                           cb_index)) {}
00475   };
00476 
00480   template<typename DISTANCE>
00481   class autotuned_index : public index_factory<DISTANCE> {
00482   public:
00489     autotuned_index(const ublas::matrix<typename DISTANCE::ElementType>& dataset, 
00490                     float target_precision = 0.9, float build_weight = 0.01,
00491                     float memory_weight = 0, float sample_fraction = 0.1) :   
00492     
00493       index_factory<DISTANCE>(dataset,
00494                               flann::AutotunedIndexParams(target_precision, build_weight,
00495                                                           memory_weight, sample_fraction)) {}
00496   };
00497   
00498   
00499   
00500   
00501   
00502   
00503   
00504 
00505   
00506 
00507 } 
00508 
00510 
00511 
00512 #endif // HAVE_FLANN
00513 #endif // JMATH_JANN_HPP