From 1bc61db6c279e7b8e714190cd58dc8161b7f4a50 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 3 Jun 2016 20:25:54 -0400 Subject: [PATCH 01/34] Refactor for faster assembly of secondHashTable. --- src/mlpack/methods/lsh/lsh_search.hpp | 2 +- src/mlpack/methods/lsh/lsh_search_impl.hpp | 72 ++++++++++++---------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/mlpack/methods/lsh/lsh_search.hpp b/src/mlpack/methods/lsh/lsh_search.hpp index b42bb7a81e..a755a9981f 100644 --- a/src/mlpack/methods/lsh/lsh_search.hpp +++ b/src/mlpack/methods/lsh/lsh_search.hpp @@ -322,7 +322,7 @@ class LSHSearch arma::Col bucketContentSize; //! For a particular hash value, points to the row in secondHashTable - //! corresponding to this value. Should be secondHashSize. + //! corresponding to this value. Length secondHashSize. arma::Col bucketRowInHashTable; //! The number of distance evaluations. diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index 9ab206760e..a141aa2c04 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -60,7 +60,7 @@ LSHSearch(const arma::mat& referenceSet, // Empty constructor. template LSHSearch::LSHSearch() : - referenceSet(new arma::mat()), // empty dataset + referenceSet(new arma::mat()), // Use an empty dataset. ownsSet(true), numProj(0), numTables(0), @@ -153,9 +153,6 @@ void LSHSearch::Train(const arma::mat& referenceSet, bucketRowInHashTable.set_size(secondHashSize); bucketRowInHashTable.fill(secondHashSize); - // Keep track of number of non-empty rows in the 'secondHashTable'. - size_t numRowsInTable = 0; - // Step II: The offsets for all projections in all tables. // Since the 'offsets' are in [0, hashWidth], we obtain the 'offsets' // as randu(numProj, numTables) * hashWidth. @@ -183,6 +180,10 @@ void LSHSearch::Train(const arma::mat& referenceSet, "tables provided must be equal to numProj"); } + // We will store the second hash vectors in this matrix; the second hash + // vector for table i will be held in row i. + arma::Mat secondHashVectors(numTables, referenceSet.n_cols); + for (size_t i = 0; i < numTables; i++) { // Step IV: create the 'numProj'-dimensional key for each point in each @@ -204,20 +205,36 @@ void LSHSearch::Train(const arma::mat& referenceSet, // Step V: Putting the points in the 'secondHashTable' by hashing the key. // Now we hash every key, point ID to its corresponding bucket. - arma::rowvec secondHashVec = secondHashWeights.t() * arma::floor(hashMat); + secondHashVectors.row(i) = arma::conv_to>::from( + secondHashWeights.t() * arma::floor(hashMat)); + } - // This gives us the bucket for the corresponding point ID. - for (size_t j = 0; j < secondHashVec.n_elem; j++) - secondHashVec[j] = (double) ((size_t) secondHashVec[j] % secondHashSize); + // Normalize hashes (take modulus with secondHashSize). + secondHashVectors.transform([secondHashSize](size_t val) + { return val % secondHashSize; }); - Log::Assert(secondHashVec.n_elem == referenceSet.n_cols); + // Now, using the hash vectors for each table, count the number of rows we + // have in the second hash table. + arma::Row secondHashBinCounts(secondHashSize, arma::fill::zeros); + for (size_t i = 0; i < secondHashVectors.n_elem; ++i) + secondHashBinCounts[secondHashVectors[i]]++; + const size_t numRowsInTable = arma::accu(secondHashBinCounts > 0); + const size_t maxBucketSize = std::min(arma::max(secondHashBinCounts), + bucketSize); + secondHashTable.resize(numRowsInTable, maxBucketSize); + + // Next we must assign each point in each table to the right second hash + // table. + size_t currentRow = 0; + for (size_t i = 0; i < numTables; ++i) + { // Insert the point in the corresponding row to its bucket in the // 'secondHashTable'. - for (size_t j = 0; j < secondHashVec.n_elem; j++) + for (size_t j = 0; j < secondHashVectors.n_cols; j++) { // This is the bucket number. - size_t hashInd = (size_t) secondHashVec[j]; + size_t hashInd = (size_t) secondHashVectors(i, j); // The point ID is 'j'. // If this is currently an empty bucket, start a new row keep track of @@ -225,37 +242,24 @@ void LSHSearch::Train(const arma::mat& referenceSet, if (bucketContentSize[hashInd] == 0) { // Start a new row for hash. - bucketRowInHashTable[hashInd] = numRowsInTable; - secondHashTable(numRowsInTable, 0) = j; - - numRowsInTable++; + bucketRowInHashTable[hashInd] = currentRow; + bucketContentSize[hashInd] = 1; + secondHashTable(currentRow, 0) = j; + currentRow++; } - - else + else if (bucketContentSize[hashInd] < maxBucketSize) { // If bucket is already present in the 'secondHashTable', find the // corresponding row and insert the point ID in this row unless the - // bucket is full, in which case, do nothing. - if (bucketContentSize[hashInd] < bucketSize) - secondHashTable(bucketRowInHashTable[hashInd], - bucketContentSize[hashInd]) = j; + // bucket is full (in which case we are not inside this else if). + secondHashTable(bucketRowInHashTable[hashInd], + bucketContentSize[hashInd]++) = j; } - - // Increment the count of the points in this bucket. - if (bucketContentSize[hashInd] < bucketSize) - bucketContentSize[hashInd]++; } // Loop over all points in the reference set. } // Loop over tables. - // Step VI: Condensing the 'secondHashTable'. - size_t maxBucketSize = 0; - for (size_t i = 0; i < bucketContentSize.n_elem; i++) - if (bucketContentSize[i] > maxBucketSize) - maxBucketSize = bucketContentSize[i]; - - Log::Info << "Final hash table size: (" << numRowsInTable << " x " - << maxBucketSize << ")" << std::endl; - secondHashTable.resize(numRowsInTable, maxBucketSize); + Log::Info << "Final hash table size: " << numRowsInTable << " x " + << maxBucketSize << "." << std::endl; } template From 8ad5711d77865c6a2df5b6a296de8a905f587c94 Mon Sep 17 00:00:00 2001 From: Yannis Mentekidis Date: Sat, 4 Jun 2016 16:30:45 +0300 Subject: [PATCH 02/34] Adds 2 deterministic LSH tests --- src/mlpack/tests/lsh_test.cpp | 178 ++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/src/mlpack/tests/lsh_test.cpp b/src/mlpack/tests/lsh_test.cpp index d42566694f..26cf24d854 100644 --- a/src/mlpack/tests/lsh_test.cpp +++ b/src/mlpack/tests/lsh_test.cpp @@ -15,6 +15,9 @@ using namespace std; using namespace mlpack; using namespace mlpack::neighbor; +/** + * Computes Recall (percent of neighbors found correctly) + */ double ComputeRecall( const arma::Mat& lshNeighbors, const arma::Mat& groundTruth) @@ -26,6 +29,62 @@ double ComputeRecall( return same / (static_cast(queries * neigh)); } +/** + * Generates a point set of four clusters around (0.5, 0.5), + * (3.5, 0.5), (0.5, 3.5), (3.5, 3.5) + */ +void getPointset(const size_t N, arma::mat& rdata) +{ + const size_t d = 2; + // Create four clusters of points + arma::mat C1(d, N/4, arma::fill::randu); + arma::mat C2(d, N/4, arma::fill::randu); + arma::mat C3(d, N/4, arma::fill::randu); + arma::mat C4(d, N/4, arma::fill::randu); + + arma::colvec offset1; + offset1<<0< lshTest(rdata, projections, + hashWidth, secondHashSize, bucketSize); + + arma::Mat neighbors; + arma::mat distances; + lshTest.Search(qdata, k, neighbors, distances); + + // test query 1 + size_t q; + for (size_t j = 0; j < k; ++j) //for each neighbor + { + q = 0; + if (neighbors(j, 0) == N || neighbors(j, 1) == N) //neighbor not found, ignore + continue; + + //query 1 is in cluster 3, which under this projection was merged with + //cluster 4. Clusters 3 and 4 have points 20:39, so only neighbors among + //those should be found + q = 0; + BOOST_REQUIRE(neighbors(j, q) >= N/2); + + //query 2 is in cluster 2, which under this projection was merged with + //cluster 1. Clusters 1 and 2 have points 0:19, so only neighbors among + //those should be found + q = 1; + BOOST_REQUIRE(neighbors(j, q) < N/2); + + } +} + + +/** + * Test: This is a deterministic test that projects 2-di points to the plane. + * The reference set contains 4 well-separated clusters that should not merge. + * + * We create two queries, each one belonging in one cluster (q1 in cluster 3 + * located around (0, 0) and q2 in cluster 2 located around (3, 3). The test is + * a success if, after the projection, q1 should have neighbors in C3 and q2 + * in C2. + */ +BOOST_AUTO_TEST_CASE(DeterministicNoMerge) +{ + const size_t N = 40; + arma::mat rdata; + arma::mat qdata; + getPointset(N, rdata); + getQueries(qdata); + + + const int k = N/2; + const double hashWidth = 1; + const int secondHashSize = 99901; + const int bucketSize = 500; + + //1 table, with one projection to axis 1 + arma::cube projections(2, 2, 1); + projections(0, 0, 0) = 0; + projections(1, 0, 0) = 1; + projections(0, 1, 0) = 1; + projections(1, 1, 0) = 0; + + LSHSearch<> lshTest(rdata, projections, + hashWidth, secondHashSize, bucketSize); + + arma::Mat neighbors; + arma::mat distances; + lshTest.Search(qdata, k, neighbors, distances); + + // test query 1 + size_t q; + for (size_t j = 0; j < k; ++j) //for each neighbor + { + + //neighbor not found, ignore + if (neighbors(j, 0) == N || neighbors(j, 1) == N) + continue; + + q = 0; + //query 1 is in cluster 3, which is points 20:29 + BOOST_REQUIRE( + neighbors(j, q) >= N/2 && neighbors(j, q) < 3*N/4 + ); + + q = 1; + //query 2 is in cluster 2, which is points 10:19 + BOOST_REQUIRE( + neighbors(j, q) >= N/4 && neighbors(j, q) < N/2 + ); + } + +} BOOST_AUTO_TEST_CASE(LSHTrainTest) { // This is a not very good test that simply checks that the re-trained LSH From 330e82c648e83b6e8f417a41384957dfe8c81bb4 Mon Sep 17 00:00:00 2001 From: Yannis Mentekidis Date: Sat, 4 Jun 2016 16:43:08 +0300 Subject: [PATCH 03/34] Style fixes and remove redundancies --- src/mlpack/tests/lsh_test.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mlpack/tests/lsh_test.cpp b/src/mlpack/tests/lsh_test.cpp index 26cf24d854..9104dab6de 100644 --- a/src/mlpack/tests/lsh_test.cpp +++ b/src/mlpack/tests/lsh_test.cpp @@ -400,7 +400,6 @@ BOOST_AUTO_TEST_CASE(DeterministicMerge) size_t q; for (size_t j = 0; j < k; ++j) //for each neighbor { - q = 0; if (neighbors(j, 0) == N || neighbors(j, 1) == N) //neighbor not found, ignore continue; @@ -466,14 +465,14 @@ BOOST_AUTO_TEST_CASE(DeterministicNoMerge) if (neighbors(j, 0) == N || neighbors(j, 1) == N) continue; - q = 0; //query 1 is in cluster 3, which is points 20:29 + q = 0; BOOST_REQUIRE( neighbors(j, q) >= N/2 && neighbors(j, q) < 3*N/4 ); - q = 1; //query 2 is in cluster 2, which is points 10:19 + q = 1; BOOST_REQUIRE( neighbors(j, q) >= N/4 && neighbors(j, q) < N/2 ); From 6469dac448ba9f583dc70b0b79c9785c30923a86 Mon Sep 17 00:00:00 2001 From: Yannis Mentekidis Date: Sun, 5 Jun 2016 09:26:15 +0300 Subject: [PATCH 04/34] Fixes style problems --- src/mlpack/tests/lsh_test.cpp | 72 +++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/src/mlpack/tests/lsh_test.cpp b/src/mlpack/tests/lsh_test.cpp index 9104dab6de..85881b78ce 100644 --- a/src/mlpack/tests/lsh_test.cpp +++ b/src/mlpack/tests/lsh_test.cpp @@ -33,40 +33,52 @@ double ComputeRecall( * Generates a point set of four clusters around (0.5, 0.5), * (3.5, 0.5), (0.5, 3.5), (3.5, 3.5) */ -void getPointset(const size_t N, arma::mat& rdata) +void GetPointset(const size_t N, arma::mat& rdata) { const size_t d = 2; // Create four clusters of points - arma::mat C1(d, N/4, arma::fill::randu); - arma::mat C2(d, N/4, arma::fill::randu); - arma::mat C3(d, N/4, arma::fill::randu); - arma::mat C4(d, N/4, arma::fill::randu); + arma::mat C1(d, N / 4, arma::fill::randu); + arma::mat C2(d, N / 4, arma::fill::randu); + arma::mat C3(d, N / 4, arma::fill::randu); + arma::mat C4(d, N / 4, arma::fill::randu); arma::colvec offset1; - offset1<<0<= N/2); + BOOST_REQUIRE(neighbors(j, q) >= N / 2); //query 2 is in cluster 2, which under this projection was merged with //cluster 1. Clusters 1 and 2 have points 0:19, so only neighbors among //those should be found q = 1; - BOOST_REQUIRE(neighbors(j, q) < N/2); + BOOST_REQUIRE(neighbors(j, q) < N / 2); } } @@ -433,11 +445,11 @@ BOOST_AUTO_TEST_CASE(DeterministicNoMerge) const size_t N = 40; arma::mat rdata; arma::mat qdata; - getPointset(N, rdata); - getQueries(qdata); + GetPointset(N, rdata); + GetQueries(qdata); - const int k = N/2; + const int k = N / 2; const double hashWidth = 1; const int secondHashSize = 99901; const int bucketSize = 500; @@ -468,13 +480,15 @@ BOOST_AUTO_TEST_CASE(DeterministicNoMerge) //query 1 is in cluster 3, which is points 20:29 q = 0; BOOST_REQUIRE( - neighbors(j, q) >= N/2 && neighbors(j, q) < 3*N/4 + neighbors(j, q) < 3 * N / 4 && + neighbors(j, q) >= N / 2 ); //query 2 is in cluster 2, which is points 10:19 q = 1; BOOST_REQUIRE( - neighbors(j, q) >= N/4 && neighbors(j, q) < N/2 + neighbors(j, q) < N / 2 && + neighbors(j, q) >= N / 4 ); } From 9d85b64c6c6bdff608331195351d09abf56cfc96 Mon Sep 17 00:00:00 2001 From: nilayjain Date: Sun, 5 Jun 2016 12:30:02 +0000 Subject: [PATCH 05/34] edge_boxes: feature extraction --- src/mlpack/methods/CMakeLists.txt | 1 + src/mlpack/methods/edge_boxes/CMakeLists.txt | 20 + .../methods/edge_boxes/edge_boxes_main.cpp | 90 ++ .../methods/edge_boxes/feature_extraction.hpp | 85 ++ .../edge_boxes/feature_extraction_impl.hpp | 903 ++++++++++++++++++ 5 files changed, 1099 insertions(+) create mode 100644 src/mlpack/methods/edge_boxes/CMakeLists.txt create mode 100644 src/mlpack/methods/edge_boxes/edge_boxes_main.cpp create mode 100644 src/mlpack/methods/edge_boxes/feature_extraction.hpp create mode 100644 src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt index d0ea04ca58..00a67bd154 100644 --- a/src/mlpack/methods/CMakeLists.txt +++ b/src/mlpack/methods/CMakeLists.txt @@ -22,6 +22,7 @@ set(DIRS decision_stump det emst + edge_boxes fastmks gmm hmm diff --git a/src/mlpack/methods/edge_boxes/CMakeLists.txt b/src/mlpack/methods/edge_boxes/CMakeLists.txt new file mode 100644 index 0000000000..e64722c2de --- /dev/null +++ b/src/mlpack/methods/edge_boxes/CMakeLists.txt @@ -0,0 +1,20 @@ + +cmake_minimum_required(VERSION 2.8) + +# Define the files we need to compile. +# Anything not in this list will not be compiled into mlpack. +set(SOURCES + feature_extraction.hpp + feature_extraction_impl.hpp +) + +# Add directory name to sources. +set(DIR_SRCS) +foreach(file ${SOURCES}) + set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file}) +endforeach() +# Append sources (with directory name) to list of all mlpack sources (used at +# the parent scope). +set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) + +add_cli_executable(edge_boxes) diff --git a/src/mlpack/methods/edge_boxes/edge_boxes_main.cpp b/src/mlpack/methods/edge_boxes/edge_boxes_main.cpp new file mode 100644 index 0000000000..3be7692ff9 --- /dev/null +++ b/src/mlpack/methods/edge_boxes/edge_boxes_main.cpp @@ -0,0 +1,90 @@ +/** + * @file decision_stump.hpp + * @author + * + * Definition of decision stumps. + */ +#include +#include "feature_extraction.hpp" + +using namespace mlpack; +using namespace mlpack::structured_tree; +using namespace std; + +int main() +{ + /* + :param options: + num_images: number of images in the dataset. + rgbd: 0 for RGB, 1 for RGB + depth + shrink: amount to shrink channels + n_orient: number of orientations per gradient scale + grd_smooth_rad: radius for image gradient smoothing + grd_norm_rad: radius for gradient normalization + reg_smooth_rad: radius for reg channel smoothing + ss_smooth_rad: radius for sim channel smoothing + p_size: size of image patches + g_size: size of ground truth patches + n_cell: number of self similarity cells + + n_pos: number of positive patches per tree + n_neg: number of negative patches per tree + fraction: fraction of features to use to train each tree + n_tree: number of trees in forest to train + n_class: number of classes (clusters) for binary splits + min_count: minimum number of data points to allow split + min_child: minimum number of data points allowed at child nodes + max_depth: maximum depth of tree + split: options include 'gini', 'entropy' and 'twoing' + discretize: optional function mapping structured to class labels + + stride: stride at which to compute edges + sharpen: sharpening amount (can only decrease after training) + n_tree_eval: number of trees to evaluate per location + nms: if true apply non-maximum suppression to edges + */ + + map options; + options["num_images"] = 2; + options["row_size"] = 321; + options["col_size"] = 481; + options["rgbd"] = 0; + options["shrink"] = 2; + options["n_orient"] = 4; + options["grd_smooth_rad"] = 0; + options["grd_norm_rad"] = 4; + options["reg_smooth_rad"] = 2; + options["ss_smooth_rad"] = 8; + options["p_size"] = 32; + options["g_size"] = 16; + options["n_cell"] = 5; + + options["n_pos"] = 10000; + options["n_neg"] = 10000; + options["fraction"] = 0.25; + options["n_tree"] = 8; + options["n_class"] = 2; + options["min_count"] = 1; + options["min_child"] = 8; + options["max_depth"] = 64; + options["split"] = 0; // we use 0 for gini, 1 for entropy, 2 for other + options["stride"] = 2; + options["sharpen"] = 2; + options["n_tree_eval"] = 4; + options["nms"] = 1; // 1 for true, 0 for false + + StructuredForests SF(options); +// arma::uvec x(2); + //SF.GetFeatureDimension(x); + + arma::mat segmentations, boundaries, images; + data::Load("/home/nilay/example/small_images.csv", images); + data::Load("/home/nilay/example/small_boundary_1.csv", boundaries); + data::Load("/home/nilay/example/small_segmentation_1.csv", segmentations); + + arma::mat input_data = SF.LoadData(images, boundaries, segmentations); + cout << input_data.n_rows << " " << input_data.n_cols << endl; + SF.PrepareData(input_data); + return 0; +} + diff --git a/src/mlpack/methods/edge_boxes/feature_extraction.hpp b/src/mlpack/methods/edge_boxes/feature_extraction.hpp new file mode 100644 index 0000000000..ba14e23b5f --- /dev/null +++ b/src/mlpack/methods/edge_boxes/feature_extraction.hpp @@ -0,0 +1,85 @@ +/** + * @file feature_extraction.hpp + * @author Nilay Jain + * + * Feature Extraction for the edge_boxes algorithm. + */ +#ifndef MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_HPP +#define MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_HPP +#define INF 999999.9999 +#define EPS 1E-20 +#include + +namespace mlpack { +namespace structured_tree { + +template +class StructuredForests +{ + + public: + + std::map options; + + StructuredForests(const std::map& inMap); + + MatType LoadData(MatType& images, MatType& boundaries, + MatType& segmentations); + + void PrepareData(MatType& InputData); + + private: + + arma::vec GetFeatureDimension(); + + arma::vec dt_1d(arma::vec& f, int n); + + void dt_2d(MatType& im); + + MatType dt_image(MatType& im, double on); + + arma::field GetFeatures(MatType& img,arma::umat& loc); + + CubeType CopyMakeBorder(CubeType& InImage, + int top, int left, int bottom, int right); + + void GetShrunkChannels(CubeType& InImage, CubeType& reg_ch, CubeType& ss_ch); + + CubeType RGB2LUV(CubeType& InImage); + + MatType bilinearInterpolation(MatType const &src, + size_t height, size_t width); + + CubeType sepFilter2D(CubeType& InImage, + arma::vec& kernel, int radius); + + CubeType ConvTriangle(CubeType& InImage, int radius); + + void Gradient(CubeType& InImage, + MatType& Magnitude, + MatType& Orientation); + + MatType MaxAndLoc(CubeType& mag, arma::umat& Location); + + CubeType Histogram(MatType& Magnitude, + MatType& Orientation, + int downscale, int interp); + + CubeType ViewAsWindows(CubeType& channels, arma::umat& loc); + + CubeType GetRegFtr(CubeType& channels, arma::umat& loc); + + CubeType GetSSFtr(CubeType& channels, arma::umat& loc); + + CubeType Rearrange(CubeType& channels); + + CubeType PDist(CubeType& features, arma::uvec& grid_pos); + +}; + + +} //namespace structured_tree +} // namespace mlpack +#include "feature_extraction_impl.hpp" +#endif + diff --git a/src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp b/src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp new file mode 100644 index 0000000000..9680faa214 --- /dev/null +++ b/src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp @@ -0,0 +1,903 @@ +/** + * @file feature_extraction_impl.hpp + * @author Nilay Jain + * + * Implementation of feature extraction methods. + */ +#ifndef MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_IMPL_HPP +#define MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_IMPL_HPP + + +#include "feature_extraction.hpp" +#include + +namespace mlpack { +namespace structured_tree { + +template +StructuredForests:: +StructuredForests(const std::map& inMap) +{ + this->options = inMap; +} + +template +MatType StructuredForests:: +LoadData(MatType& images, MatType& boundaries, MatType& segmentations) +{ + int num_images = this->options["num_images"]; + int row_size = this->options["row_size"]; + int col_size = this->options["col_size"]; + MatType input_data(num_images * row_size * 5, col_size); + // we store the input data as follows: + // images (3), boundaries (1), segmentations (1). + int loop_iter = num_images * 5; + size_t row_idx = 0; + int col_i = 0, col_s = 0, col_b = 0; + for(size_t i = 0; i < loop_iter; ++i) + { + if (i % 5 == 4) + { + input_data.submat(row_idx, 0, row_idx + row_size - 1,\ + col_size - 1) = MatType(segmentations.colptr(col_s),\ + col_size, row_size).t(); + ++col_s; + } + else if (i % 5 == 3) + { + input_data.submat(row_idx, 0, row_idx + row_size - 1,\ + col_size - 1) = MatType(boundaries.colptr(col_b),\ + col_size, row_size).t(); + ++col_b; + } + else + { + input_data.submat(row_idx, 0, row_idx + row_size - 1,\ + col_size - 1) = MatType(images.colptr(col_i), + col_size, row_size).t(); + ++col_i; + } + row_idx += row_size; + } + return input_data; +} + +template +arma::vec StructuredForests:: +GetFeatureDimension() +{ + /* + shrink: amount to shrink channels + p_size: size of image patches + n_cell: number of self similarity cells + n_orient: number of orientations per gradient scale + */ + arma::vec P(2); + int shrink, p_size, n_cell; + shrink = this->options["shrink"]; + p_size = this->options["p_size"]; + n_cell = this->options["n_cell"]; + + /* + n_color_ch: number of color channels + n_grad_ch: number of gradient channels + n_ch: total number of channels + */ + int n_color_ch, n_grad_ch, n_ch; + if (this->options["rgbd"] == 0) + n_color_ch = 3; + else + n_color_ch = 4; + + n_grad_ch = 2 * (1 + this->options["n_orient"]); + + n_ch = n_color_ch + n_grad_ch; + P[0] = pow((p_size / shrink) , 2) * n_ch; + P[1] = pow(n_cell , 2) * (pow (n_cell, 2) - 1) / 2 * n_ch; + return P; +} + +template +arma::vec StructuredForests:: +dt_1d(arma::vec& f, int n) +{ + arma::vec d(n), v(n), z(n + 1); + int k = 0; + v[0] = 0.0; + z[0] = -INF; + z[1] = +INF; + for (size_t q = 1; q <= n - 1; ++q) + { + float s = ( (f[q] + q * q)-( f[v[k]] + v[k] * v[k]) ) / (2 * q - 2 * v[k]); + while (s <= z[k]) + { + --k; + s = ( (f[q] + q * q) - (f[v[k]] + v[k] * v[k]) ) / (2 * q - 2 * v[k]); + } + + k++; + v[k] = (double)q; + z[k] = s; + z[k+1] = +INF; + } + + k = 0; + for (int q = 0; q <= n-1; q++) + { + while (z[k+1] < q) + k++; + d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; + } + return d; +} + +template +void StructuredForests:: +dt_2d(MatType& im) +{ + arma::vec f(std::max(im.n_rows, im.n_cols)); + // transform along columns + for (size_t x = 0; x < im.n_cols; ++x) + { + f.subvec(0, im.n_rows - 1) = im.col(x); + arma::vec d = this->dt_1d(f, im.n_rows); + im.col(x) = d; + } + + // transform along rows + for (int y = 0; y < im.n_rows; y++) + { + f.subvec(0, im.n_cols - 1) = im.row(y).t(); + arma::vec d = this->dt_1d(f, im.n_cols); + im.row(y) = d.t(); + } +} + +/* euclidean distance transform of binary image using squared distance */ +template +MatType StructuredForests:: +dt_image(MatType& im, double on) +{ + MatType out = MatType(im.n_rows, im.n_cols); + out.fill(0.0); + out.elem( find(im != on) ).fill(INF); + this->dt_2d(out); + return out; +} + +template +CubeType StructuredForests:: +CopyMakeBorder(CubeType& InImage, int top, + int left, int bottom, int right) +{ + CubeType OutImage(InImage.n_rows + top + bottom, InImage.n_cols + left + right, InImage.n_slices); + + for(size_t i = 0; i < InImage.n_slices; ++i) + { + OutImage.slice(i).submat(top, left, InImage.n_rows + top - 1, InImage.n_cols + left - 1) + = InImage.slice(i); + + for(size_t j = 0; j < right; ++j) + { + OutImage.slice(i).col(InImage.n_cols + left + j).subvec(top, InImage.n_rows + top - 1) + = InImage.slice(i).col(InImage.n_cols - j - 1); + } + + for(int j = 0; j < left; ++j) + { + OutImage.slice(i).col(j).subvec(top, InImage.n_rows + top - 1) + = InImage.slice(i).col(left - 1 - j); + } + + for(int j = 0; j < top; j++) + { + + OutImage.slice(i).row(j) + = OutImage.slice(i).row(2 * top - 1 - j); + } + + for(int j = 0; j < bottom; j++) + { + OutImage.slice(i).row(InImage.n_rows + top + j) + = OutImage.slice(i).row(InImage.n_rows + top - j - 1); + } + + } + return OutImage; +} + +template +CubeType StructuredForests:: +RGB2LUV(CubeType& InImage) +{ + //assert type is double or float. + double a, y0, maxi; + a = pow(29.0, 3) / 27.0; + y0 = 8.0 / a; + maxi = 1.0 / 270.0; + + arma::vec table(1025); + for (size_t i = 0; i < 1025; ++i) + { + table(i) = i / 1024.0; + + if (table(i) > y0) + table(i) = 116 * pow(table(i), 1.0/3.0) - 16.0; + else + table(i) = table(i) * a; + + table(i) = table(i) * maxi; + } + + MatType rgb2xyz(3,3); + rgb2xyz(0,0) = 0.430574; rgb2xyz(0,1) = 0.430574; rgb2xyz(0,2) = 0.430574; + rgb2xyz(1,0) = 0.430574; rgb2xyz(1,1) = 0.430574; rgb2xyz(1,2) = 0.430574; + rgb2xyz(2,0) = 0.430574; rgb2xyz(2,1) = 0.430574; rgb2xyz(2,2) = 0.430574; + + //see how to calculate this efficiently. numpy.dot does this. + CubeType xyz(InImage.n_rows, InImage.n_cols, rgb2xyz.n_cols); + for(size_t i = 0; i < InImage.n_rows; ++i) + { + for(size_t j = 0; j < InImage.n_cols; ++j) + { + for(size_t k = 0; k < rgb2xyz.n_cols; ++k) + { + double s = 0.0; + for(size_t l = 0; l < InImage.n_slices; ++l) + s += InImage(i, j, l) * rgb2xyz(l, k); + xyz(i, j, k) = s; + } + } + } + + MatType nz(InImage.n_rows, InImage.n_cols); + + nz = 1.0 / ( xyz.slice(0) + (15 * xyz.slice(1) ) + + (3 * xyz.slice(2) + EPS)); + + MatType L = arma::reshape(L, xyz.n_rows, xyz.n_cols); + + MatType U, V; + U = L % (13 * 4 * (xyz.slice(0) % nz) - 13 * 0.197833) + 88 * maxi; + V = L % (13 * 9 * (xyz.slice(1) % nz) - 13 * 0.468331) + 134 * maxi; + + CubeType OutImage(InImage.n_rows, InImage.n_cols, InImage.n_slices); + OutImage.slice(0) = L; + OutImage.slice(1) = U; + OutImage.slice(2) = V; + //OutImage = arma::join_slices(L,U); + //OutImage = arma::join_slices(OutImage, V); + return OutImage; +} + +template +MatType StructuredForests:: +bilinearInterpolation(MatType const &src, + size_t height, size_t width) +{ + MatType dst(height, width); + double const x_ratio = static_cast((src.n_cols - 1)) / width; + double const y_ratio = static_cast((src.n_rows - 1)) / height; + for(size_t row = 0; row != dst.n_rows; ++row) + { + size_t y = static_cast(row * y_ratio); + double const y_diff = (row * y_ratio) - y; //distance of the nearest pixel(y axis) + double const y_diff_2 = 1 - y_diff; + for(size_t col = 0; col != dst.n_cols; ++col) + { + size_t x = static_cast(col * x_ratio); + double const x_diff = (col * x_ratio) - x; //distance of the nearet pixel(x axis) + double const x_diff_2 = 1 - x_diff; + double const y2_cross_x2 = y_diff_2 * x_diff_2; + double const y2_cross_x = y_diff_2 * x_diff; + double const y_cross_x2 = y_diff * x_diff_2; + double const y_cross_x = y_diff * x_diff; + dst(row, col) = y2_cross_x2 * src(y, x) + + y2_cross_x * src(y, x + 1) + + y_cross_x2 * src(y + 1, x) + + y_cross_x * src(y + 1, x + 1); + } + } + + return dst; +} + +template +CubeType StructuredForests:: +sepFilter2D(CubeType& InImage, arma::vec& kernel, int radius) +{ + CubeType OutImage = this->CopyMakeBorder(InImage, radius, radius, radius, radius); + + arma::vec row_res(1), col_res(1); + // reverse InImage and OutImage to avoid making an extra matrix. + for(size_t k = 0; k < OutImage.n_slices; ++k) + { + for(size_t j = radius; j < OutImage.n_cols - radius; ++j) + { + for(size_t i = radius; i < OutImage.n_rows - radius; ++i) + { + row_res = OutImage.slice(k).row(i).subvec(j - radius, j + radius) * kernel; + col_res = OutImage.slice(k).col(i).subvec(i - radius, i + radius).t() * kernel; + // divide by 2: avg of row_res and col_res, divide by 3: avg over 3 locations. + InImage(i - radius, j - radius, k) = (row_res(0) + col_res(0)) / 2 / 3; + } + } + } + + return InImage; +} + +template +CubeType StructuredForests:: +ConvTriangle(CubeType& InImage, int radius) +{ + if (radius == 0) + { + return InImage; + } + else if (radius <= 1) + { + double p = 12.0 / radius / (radius + 2) - 2; + arma::vec kernel = {1 , p, 1}; + kernel = kernel / (p + 2); + + return this->sepFilter2D(InImage, kernel, radius); + } + else + { + int len = 2 * radius + 1; + arma::vec kernel(len); + for( size_t i = 0; i < radius; ++i) + kernel(i) = i + 1; + + kernel(radius) = radius + 1; + + for( size_t i = radius + 1; i < len; ++i) + kernel(i) = i - 1; + return this->sepFilter2D(InImage, kernel, radius); + } +} + +//just a helper function, can't use it for anything else +//finds max numbers on cube axis and returns max values, +// also stores the locations of max values in Location +template +MatType StructuredForests:: +MaxAndLoc(CubeType& mag, arma::umat& Location) +{ + MatType MaxVal(Location.n_rows, Location.n_cols); + for(size_t i = 0; i < mag.n_rows; ++i) + { + for(size_t j = 0; j < mag.n_cols; ++j) + { + double max = -9999999999.0; int max_loc = 0; + for(size_t k = 0; k < mag.n_slices; ++k) + { + if(mag(i, j, k) > max) + { + max = mag(i, j, k); + MaxVal(i, j) = max; + Location(i, j) = k; + } + } + } + } + return MaxVal; +} + +template +void StructuredForests:: +Gradient(CubeType& InImage, + MatType& Magnitude, + MatType& Orientation) +{ + int grd_norm_rad = this->options["grd_norm_rad"]; + CubeType dx(InImage.n_rows, InImage.n_cols, InImage.n_slices), + dy(InImage.n_rows, InImage.n_cols, InImage.n_slices); + + dx.zeros(); + dy.zeros(); + + /* + From MATLAB documentation: + [FX,FY] = gradient(F), where F is a matrix, returns the + x and y components of the two-dimensional numerical gradient. + FX corresponds to ∂F/∂x, the differences in x (horizontal) direction. + FY corresponds to ∂F/∂y, the differences in the y (vertical) direction. + */ + + + /* + gradient calculates the central difference for interior data points. + For example, consider a matrix with unit-spaced data, A, that has + horizontal gradient G = gradient(A). The interior gradient values, G(:,j), are: + + G(:,j) = 0.5*(A(:,j+1) - A(:,j-1)); + where j varies between 2 and N-1, where N is size(A,2). + + The gradient values along the edges of the matrix are calculated with single-sided differences, so that + + G(:,1) = A(:,2) - A(:,1); + G(:,N) = A(:,N) - A(:,N-1); + + The spacing between points in each direction is assumed to be one. + */ + for (size_t i = 0; i < InImage.n_slices; ++i) + { + dx.slice(i).col(0) = InImage.slice(i).col(1) - InImage.slice(i).col(0); + dx.slice(i).col(InImage.n_cols - 1) = InImage.slice(i).col(InImage.n_cols - 1) + - InImage.slice(i).col(InImage.n_cols - 2); + + for (int j = 1; j < InImage.n_cols-1; j++) + dx.slice(i).col(j) = 0.5 * ( InImage.slice(i).col(j+1) - InImage.slice(i).col(j) ); + + // do same for dy. + dy.slice(i).row(0) = InImage.slice(i).row(1) - InImage.slice(i).row(0); + dy.slice(i).row(InImage.n_rows - 1) = InImage.slice(i).row(InImage.n_rows - 1) + - InImage.slice(i).row(InImage.n_rows - 2); + + for (int j = 1; j < InImage.n_rows-1; j++) + dy.slice(i).row(j) = 0.5 * ( InImage.slice(i).row(j+1) - InImage.slice(i).row(j) ); + } + + CubeType mag(InImage.n_rows, InImage.n_cols, InImage.n_slices); + for (size_t i = 0; i < InImage.n_slices; ++i) + { + mag.slice(i) = arma::sqrt( arma::square \ + ( dx.slice(i) + arma::square( dy.slice(i) ) ) ); + } + + arma::umat Location(InImage.n_rows, InImage.n_cols); + Magnitude = this->MaxAndLoc(mag, Location); + if(grd_norm_rad != 0) + { + //we have to do this ugly thing, or override ConvTriangle + // and sepFilter2D methods. + CubeType mag2(InImage.n_rows, InImage.n_cols, 1); + mag2.slice(0) = Magnitude; + mag2 = this->ConvTriangle(mag2, grd_norm_rad); + Magnitude = Magnitude / (mag2.slice(0) + 0.01); + } + MatType dx_mat(dx.n_rows, dx.n_cols),\ + dy_mat(dy.n_rows, dy.n_cols); + + for(size_t j = 0; j < InImage.n_cols; ++j) + { + for(size_t i = 0; i < InImage.n_rows; ++i) + { + dx_mat(i, j) = dx(i, j, Location(i, j)); + dy_mat(i, j) = dy(i, j, Location(i, j)); + } + } + Orientation = arma::atan(dy_mat / dx_mat); + Orientation.transform( [](double val) { if(val < 0) return (val + arma::datum::pi); else return (val);} ); + + for(size_t j = 0; j < InImage.n_cols; ++j) + { + for(size_t i = 0; i < InImage.n_rows; ++i) + { + if( abs(dx_mat(i, j)) + abs(dy_mat(i, j)) < 1E-5) + Orientation(i, j) = 0.5 * arma::datum::pi; + } + } +} + +template +CubeType StructuredForests:: +Histogram(MatType& Magnitude, + MatType& Orientation, + int downscale, int interp) +{ + //i don't think this function can be vectorized. + + //n_orient: number of orientations per gradient scale + int n_orient = this->options["n_orient"]; + //size of HistArr: n_rbin * n_cbin * n_orient . . . (create in caller...) + int n_rbin = (Magnitude.n_rows + downscale - 1) / downscale; + int n_cbin = (Magnitude.n_cols + downscale - 1) / downscale; + double o_range, o; + o_range = arma::datum::pi / n_orient; + + CubeType HistArr(n_rbin, n_cbin, n_orient); + HistArr.zeros(); + + int r, c, o1, o2; + for(size_t i = 0; i < Magnitude.n_rows; ++i) + { + for(size_t j = 0; j < Magnitude.n_cols; ++j) + { + r = i / downscale; + c = j / downscale; + + if( interp != 0) + { + o = Orientation(i, j) / o_range; + o1 = ((int) o) % n_orient; + o2 = (o1 + 1) % n_orient; + HistArr(r, c, o1) += Magnitude(i, j) * (1 + (int)o - o); + HistArr(r, c, o2) += Magnitude(i, j) * (o - (int) o); + } + else + { + o1 = (int) (Orientation(i, j) / o_range + 0.5) % n_orient; + HistArr(r, c, o1) += Magnitude(i, j); + } + } + } + + HistArr = HistArr / downscale; + + for (size_t i = 0; i < HistArr.n_slices; ++i) + HistArr.slice(i) = arma::square(HistArr.slice(i)); + + return HistArr; +} + +template +void StructuredForests:: +GetShrunkChannels(CubeType& InImage, CubeType& reg_ch, CubeType& ss_ch) +{ + CubeType luv = this->RGB2LUV(InImage); + + int shrink = this->options["shrink"]; + int n_orient = this->options["n_orient"]; + int grd_smooth_rad = this->options["grd_smooth_rad"]; + int grd_norm_rad = this->options["grd_norm_rad"]; + int num_channels = 13; + int rsize = luv.n_rows / shrink; + int csize = luv.n_cols / shrink; + CubeType channels(rsize, csize, num_channels); + + + int slice_idx = 0; + + for( slice_idx = 0; slice_idx < luv.n_slices; ++slice_idx) + channels.slice(slice_idx) + = this->bilinearInterpolation(luv.slice(slice_idx), (size_t)rsize, (size_t)csize); + + double scale = 0.5; + + while(scale <= 1.0) + { + CubeType img( (luv.n_rows * scale), + (luv.n_cols * scale), + luv.n_slices ); + + for( slice_idx = 0; slice_idx < luv.n_slices; ++slice_idx) + { + img.slice(slice_idx) = + this->bilinearInterpolation(luv.slice(slice_idx), + (luv.n_rows * scale), + (luv.n_cols * scale) ); + } + + CubeType OutImage = this->ConvTriangle(img, grd_smooth_rad); + + MatType Magnitude(InImage.n_rows, InImage.n_cols), + Orientation(InImage.n_rows, InImage.n_cols); + + this->Gradient(OutImage, Magnitude, Orientation); + + int downscale = std::max(1, (int) (shrink * scale)); + + CubeType Hist = this->Histogram(Magnitude, Orientation, + downscale, 0); + + channels.slice(slice_idx) = + bilinearInterpolation( Magnitude, rsize, csize); + slice_idx++; + for(size_t i = 0; i < InImage.n_slices; ++i) + channels.slice(i + slice_idx) = + bilinearInterpolation( Magnitude, rsize, csize); + slice_idx += 3; + scale += 0.5; + } + + //cout << "size of channels: " << arma::size(channels) << endl; + double reg_smooth_rad, ss_smooth_rad; + reg_smooth_rad = this->options["reg_smooth_rad"] / (double) shrink; + ss_smooth_rad = this->options["ss_smooth_rad"] / (double) shrink; + + + + + if (reg_smooth_rad > 1.0) + reg_ch = this->ConvTriangle(channels, (int) (std::round(reg_smooth_rad)) ); + else + reg_ch = this->ConvTriangle(channels, reg_smooth_rad); + + if (ss_smooth_rad > 1.0) + ss_ch = this->ConvTriangle(channels, (int) (std::round(ss_smooth_rad)) ); + else + ss_ch = this->ConvTriangle(channels, ss_smooth_rad); + +} + +template +CubeType StructuredForests:: +ViewAsWindows(CubeType& channels, arma::umat& loc) +{ + // 500 for pos_loc, and 500 for neg_loc. + // channels = 160, 240, 13. + CubeType features = CubeType(16, 16, 1000 * 13); + int patchSize = 16; + int p = patchSize / 2; + //increase the channel boundary to protect error against image boundaries. + CubeType inc_ch = this->CopyMakeBorder(channels, p, p, p, p); + for (size_t i = 0, channel = 0; i < loc.n_rows; ++i) + { + int x = loc(i, 0); + int y = loc(i, 1); + + /*(x,y) in channels, is ((x+p), (y+p)) in inc_ch*/ + //cout << "(x,y) = " << x << " " << y << endl; + CubeType patch = inc_ch.tube((x + p) - p, (y + p) - p,\ + (x + p) + p - 1, (y + p) + p - 1); + // since each patch has 13 channel we have to increase the index by 13 + + //cout <<"patch size = " << arma::size(patch) << endl; + + features.slices(channel, channel + 12) = patch; + //cout << "sahi hai " << endl; + channel += 13; + + } + //cout << "successfully returned. . ." << endl; + return features; +} + +template +CubeType StructuredForests:: +Rearrange(CubeType& channels) +{ + //we do (16,16,13*1000) to 256, 1000, 13, in vectorized code. + CubeType ch = CubeType(256, 1000, 13); + for(size_t i = 0; i < 1000; i++) + { + //MatType m(256, 13); + for(size_t j = 0; j < 13; ++j) + { + int sl = (i * j) / 1000; + //cout << "(i,j) = " << i << ", " << j << endl; + ch.slice(sl).col(i) = arma::vectorise(channels.slice(i * j)); + } + } + return ch; +} + +// returns 256 * 1000 * 13 dimension features. +template +CubeType StructuredForests:: +GetRegFtr(CubeType& channels, arma::umat& loc) +{ + int shrink = this->options["shrink"]; + int p_size = this->options["p_size"] / shrink; + CubeType wind = this->ViewAsWindows(channels, loc); + return this->Rearrange(wind); +} + +template +CubeType StructuredForests:: +PDist(CubeType& features, arma::uvec& grid_pos) +{ + // size of DestArr: + // InImage.n_rows * (InImage.n_rows - 1)/2 * InImage.n_slices + //find nC2 differences, for locations in the grid_pos. + //python: input: (716, 256, 13) --->(716, 25, 13) ; output: (716, 300, 13). + //input features : 256,1000,13; output: 300, 1000, 13 + + CubeType output(300, 1000, 13); + for(size_t k = 0; k < features.n_slices; ++k) + { + size_t r_idx = 0; + for(size_t i = 0; i < grid_pos.n_elem; ++i) //loop length : 25 + { + for(size_t j = i + 1; j < grid_pos.n_elem; ++j) //loop length : 25 + { + output.slice(k).row(r_idx) = features.slice(k).row(grid_pos(i)) + - features.slice(k).row(grid_pos(j)); + ++r_idx; + } + } + } + return output; +} + +//returns 300,1000,13 dimension features. +template +CubeType StructuredForests:: +GetSSFtr(CubeType& channels, arma::umat& loc) +{ + int shrink = this->options["shrink"]; + int p_size = this->options["p_size"] / shrink; + + //n_cell: number of self similarity cells + int n_cell = this->options["n_cell"]; + int half_cell_size = (int) round(p_size / (2.0 * n_cell)); + + arma::uvec g_pos(n_cell); + for(size_t i = 0; i < n_cell; ++i) + { + g_pos(i) = (int)round( (i + 1) * (p_size + 2 * half_cell_size \ + - 1) / (n_cell + 1.0) - half_cell_size); + } + arma::uvec grid_pos(n_cell * n_cell); + size_t k = 0; + for(size_t i = 0; i < n_cell; ++i) + { + for(size_t j = 0; j < n_cell; ++j) + { + grid_pos(k) = g_pos(i) * p_size + g_pos(j); + ++k; + } + } + + CubeType wind = this->ViewAsWindows(channels, loc); + CubeType re_wind = this->Rearrange(wind); + + return this->PDist(re_wind, grid_pos); +} + +template +arma::field StructuredForests:: +GetFeatures(MatType& image, arma::umat& loc) +{ + int row_size = this->options["row_size"]; + int col_size = this->options["col_size"]; + int bottom, right; + bottom = (4 - (image.n_rows / 3) % 4) % 4; + right = (4 - image.n_cols % 4) % 4; + //cout << "Botttom = " << bottom << " right = " << right << endl; + + CubeType InImage(image.n_rows / 3, image.n_cols, 3); + + for(size_t i = 0; i < 3; ++i) + { + InImage.slice(i) = image.submat(i * row_size, 0, \ + (i + 1) * row_size - 1, col_size - 1); + } + + CubeType OutImage = this->CopyMakeBorder(InImage, 0, 0, bottom, right); + + int num_channels = 13; + int shrink = this->options["shrink"]; + int rsize = OutImage.n_rows / shrink; + int csize = OutImage.n_cols / shrink; + + /* this part gives double free or corruption out error + when executed for a second time */ + CubeType reg_ch = CubeType(rsize, csize, num_channels); + CubeType ss_ch = CubeType(rsize, csize, num_channels); + this->GetShrunkChannels(InImage, reg_ch, ss_ch); + + loc = loc / shrink; + + CubeType reg_ftr = this->GetRegFtr(reg_ch, loc); + CubeType ss_ftr = this->GetSSFtr(ss_ch, loc); + arma::field F(2,1); + F(0,0) = reg_ftr; + F(1,0) = ss_ftr; + return F; + //delete reg_ch; + //free(reg_ch); + //free(ss_ch); +} + +template +void StructuredForests:: +PrepareData(MatType& InputData) +{ + int num_images = this->options["num_images"]; + int n_tree = this->options["n_tree"]; + int n_pos = this->options["n_pos"]; + int n_neg = this->options["n_neg"]; + double fraction = 0.25; + int p_size = this->options["p_size"]; + int g_size = this->options["g_size"]; + int shrink = this->options["shrink"]; + int row_size = this->options["row_size"]; + int col_size = this->options["col_size"]; + // p_rad = radius of image patches. + // g_rad = radius of ground truth patches. + int p_rad = p_size / 2, g_rad = g_size / 2; + + arma::vec FtrDim = this->GetFeatureDimension(); + int n_ftr_dim = FtrDim(0) + FtrDim(1); + int n_smp_ftr_dim = int(n_ftr_dim * fraction); + + for(size_t i = 0; i < n_tree; ++i) + { + //implement the logic for if data already exists. + MatType ftrs = arma::zeros(n_pos + n_neg, n_smp_ftr_dim); + + //effectively a 3d array. . . + MatType lbls = arma::zeros( (n_pos + n_neg ) * g_size, g_size); + + + int loop_iter = num_images * 5; + for(size_t j = 0; j < loop_iter; j += 5) + { + MatType img, bnds, segs; + img = InputData.submat(j * row_size, 0, (j + 3) * row_size - 1, col_size - 1); + bnds = InputData.submat( (j + 3) * row_size, 0, \ + (j + 4) * row_size - 1, col_size - 1 ); + segs = InputData.submat( (j + 4) * row_size, 0, \ + (j + 5) * row_size - 1, col_size - 1 ); + + MatType mask = arma::zeros(row_size, col_size); + for(size_t b = 0; b < mask.n_cols; b = b + shrink) + for(size_t a = 0; a < mask.n_rows; a = a + shrink) + mask(a, b) = 1; + mask.col(p_rad - 1).fill(0); + mask.row( (mask.n_rows - 1) - (p_rad - 1) ).fill(0); + mask.submat(0, 0, mask.n_rows - 1, p_rad - 1).fill(0); + mask.submat(0, mask.n_cols - p_rad, mask.n_rows - 1, + mask.n_cols - 1).fill(0); + + // number of positive or negative patches per ground truth. + //int n_patches_per_gt = (int) (ceil( (float)n_pos / num_images )); + int n_patches_per_gt = 500; + //cout << "n_patches_per_gt = " << n_patches_per_gt << endl; + MatType dis = arma::sqrt( this->dt_image(bnds, 1) ); + MatType dis2 = dis; + //dis.transform( [](double val, const int& g_rad) { return (double)(val < g_rad); } ); + //dis2.transform( [](double val, const int& g_rad) { return (double)(val >= g_rad); } ); + //dis.elem( arma::find(dis >= g_rad) ).zeros(); + //dis2.elem( arma::find(dis < g_rad) ).zeros(); + + + arma::uvec pos_loc = arma::find( (dis < g_rad) % mask ); + arma::uvec neg_loc = arma::find( (dis >= g_rad) % mask ); + + pos_loc = arma::shuffle(pos_loc); + neg_loc = arma::shuffle(neg_loc); + + arma::umat loc(n_patches_per_gt * 2, 2); + //cout << "pos_loc size: " << arma::size(pos_loc) << " neg_loc size: " << arma::size(neg_loc) << endl; + //cout << "n_patches_per_gt = " << n_patches_per_gt << endl; + for(size_t i = 0; i < n_patches_per_gt; ++i) + { + loc.row(i) = arma::ind2sub(arma::size(dis.n_rows, dis.n_cols), pos_loc(i) ).t(); + //cout << "pos_loc: " << loc(i, 0) << ", " << loc(i, 1) << endl; + } + + for(size_t i = n_patches_per_gt; i < 2 * n_patches_per_gt; ++i) + { + loc.row(i) = arma::ind2sub(arma::size(dis.n_rows, dis.n_cols), neg_loc(i) ).t(); + //cout << "neg_loc: " << loc(i, 0) << ", " << loc(i, 1) << endl; + } + + // cout << "num patches = " << n_patches_per_gt << " num elements + = " << pos_loc.n_elem\ + // << " num elements - = " << neg_loc.n_elem << " dis.size " << dis.n_elem << endl; + + //Field F contains reg_ftr and ss_ftr. + arma::field F = this->GetFeatures(img, loc); + //randomly sample 70 values each from reg_ftr and ss_ftr. + /* + CubeType ftr(140, 1000, 13); + arma::uvec r = (0, 255, 256); + arma::uvec s = (0, 299, 300); + arma::uvec rs = r.shuffle(); + arma::uvec ss = s.shuffle(); + */ + CubeType lbl(g_size, g_size, 1000); + CubeType s(segs.n_rows, segs.n_cols, 1); + s.slice(0) = segs; + CubeType in_segs = this->CopyMakeBorder(s, g_rad, + g_rad, g_rad, g_rad); + for(size_t i = 0; i < loc.n_rows; ++i) + { + int x = loc(i, 0); int y = loc(i, 1); + //cout << "x, y = " << x << " " << y << endl; + lbl.slice(i) = in_segs.slice(0).submat((x + g_rad) - g_rad, (y + g_rad) - g_rad, + (x + g_rad) + g_rad - 1, (y + g_rad) + g_rad - 1); + } + } + } +} + + +} // namespace structured_tree +} // namespace mlpack +#endif + From 04a643935896e7f561c7794af802dba740ad722f Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Fri, 3 Jun 2016 09:43:33 -0300 Subject: [PATCH 06/34] Properly resetting auxBound. Start using a Reset() method, to avoid futures errors like this. --- .../methods/neighbor_search/neighbor_search_impl.hpp | 4 +--- .../methods/neighbor_search/neighbor_search_stat.hpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp index e092766ebb..d86f5146e1 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp @@ -638,9 +638,7 @@ Search(const size_t k, nodes.pop(); // Reset bounds of this node. - node->Stat().FirstBound() = SortPolicy::WorstDistance(); - node->Stat().SecondBound() = SortPolicy::WorstDistance(); - node->Stat().LastDistance() = 0.0; + node->Stat().Reset(); // Then add the children. for (size_t i = 0; i < node->NumChildren(); ++i) diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_stat.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_stat.hpp index dfcc5ad743..433ea6486a 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_stat.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_stat.hpp @@ -57,6 +57,17 @@ class NeighborSearchStat auxBound(SortPolicy::WorstDistance()), lastDistance(0.0) { } + /** + * Reset statistic parameters to initial values. + */ + void Reset() + { + firstBound = SortPolicy::WorstDistance(); + secondBound = SortPolicy::WorstDistance(); + auxBound = SortPolicy::WorstDistance(); + lastDistance = 0.0; + } + //! Get the first bound. double FirstBound() const { return firstBound; } //! Modify the first bound. From c3582e2477adccc338d3ce328fd4d39b97b1b435 Mon Sep 17 00:00:00 2001 From: nilayjain Date: Mon, 6 Jun 2016 20:45:26 +0000 Subject: [PATCH 07/34] backported ind2sub and sub2ind --- src/mlpack/core/arma_extend/CMakeLists.txt | 1 + src/mlpack/core/arma_extend/arma_extend.hpp | 2 + src/mlpack/core/arma_extend/fn_ind2sub.hpp | 69 +++++++++++++++++++++ src/mlpack/methods/CMakeLists.txt | 2 +- src/mlpack/tests/CMakeLists.txt | 5 +- src/mlpack/tests/ind2sub_test.cpp | 19 ++++++ 6 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/mlpack/core/arma_extend/fn_ind2sub.hpp create mode 100644 src/mlpack/tests/ind2sub_test.cpp diff --git a/src/mlpack/core/arma_extend/CMakeLists.txt b/src/mlpack/core/arma_extend/CMakeLists.txt index 4307b26652..db0c2212c3 100644 --- a/src/mlpack/core/arma_extend/CMakeLists.txt +++ b/src/mlpack/core/arma_extend/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES arma_extend.hpp fn_ccov.hpp + fn_ind2sub.hpp glue_ccov_meat.hpp glue_ccov_proto.hpp hdf5_misc.hpp diff --git a/src/mlpack/core/arma_extend/arma_extend.hpp b/src/mlpack/core/arma_extend/arma_extend.hpp index b8346e55c8..12765c775f 100644 --- a/src/mlpack/core/arma_extend/arma_extend.hpp +++ b/src/mlpack/core/arma_extend/arma_extend.hpp @@ -66,6 +66,8 @@ namespace arma { #include "glue_ccov_meat.hpp" #include "fn_ccov.hpp" + // index to subscript and vice versa + #include "fn_ind2sub.hpp" // inplace_reshape() #include "fn_inplace_reshape.hpp" diff --git a/src/mlpack/core/arma_extend/fn_ind2sub.hpp b/src/mlpack/core/arma_extend/fn_ind2sub.hpp new file mode 100644 index 0000000000..b4bbfe7077 --- /dev/null +++ b/src/mlpack/core/arma_extend/fn_ind2sub.hpp @@ -0,0 +1,69 @@ + + #if (ARMA_VERSION_MAJOR < 6 && ARMA_VERSION_MINOR < 399) + inline + uvec + ind2sub(const SizeMat& s, const uword i) + { + arma_extra_debug_sigprint(); + + arma_debug_check( (i >= (s.n_rows * s.n_cols) ), "ind2sub(): index out of range" ); + + uvec out(2); + + out[0] = i % s.n_rows; + out[1] = i / s.n_rows; + + return out; + } + + + inline + uvec + ind2sub(const SizeCube& s, const uword i) + { + arma_extra_debug_sigprint(); + + arma_debug_check( (i >= (s.n_rows * s.n_cols * s.n_slices) ), "ind2sub(): index out of range" ); + + const uword n_elem_slice = s.n_rows * s.n_cols; + + const uword slice = i / n_elem_slice; + const uword j = i - (slice * n_elem_slice); + const uword row = j % s.n_rows; + const uword col = j / s.n_rows; + + uvec out(3); + + out[0] = row; + out[1] = col; + out[2] = slice; + + return out; + } + + + arma_inline + uword + sub2ind(const SizeMat& s, const uword row, const uword col) + { + arma_extra_debug_sigprint(); + + arma_debug_check( ((row >= s.n_rows) || (col >= s.n_cols)), "sub2ind(): subscript out of range" ); + + return uword(row + col*s.n_rows); + } + + + arma_inline + uword + sub2ind(const SizeCube& s, const uword row, const uword col, const uword slice) + { + arma_extra_debug_sigprint(); + + arma_debug_check( ((row >= s.n_rows) || (col >= s.n_cols) || (slice >= s.n_slices)), "sub2ind(): subscript out of range" ); + + return uword( (slice * s.n_rows * s.n_cols) + (col * s.n_rows) + row ); + } +#endif + + diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt index 00a67bd154..5c58bf9e45 100644 --- a/src/mlpack/methods/CMakeLists.txt +++ b/src/mlpack/methods/CMakeLists.txt @@ -23,7 +23,7 @@ set(DIRS det emst edge_boxes - fastmks +# fastmks gmm hmm hoeffding_trees diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index e1f255aa72..fab4151a57 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -17,11 +17,12 @@ add_executable(mlpack_test det_test.cpp distribution_test.cpp emst_test.cpp - fastmks_test.cpp +# fastmks_test.cpp feedforward_network_test.cpp gmm_test.cpp hmm_test.cpp hoeffding_tree_test.cpp + ind2sub_test.cpp init_rules_test.cpp kernel_test.cpp kernel_pca_test.cpp @@ -62,7 +63,7 @@ add_executable(mlpack_test sgd_test.cpp serialization.hpp serialization.cpp - serialization_test.cpp + # serialization_test.cpp softmax_regression_test.cpp sort_policy_test.cpp sparse_autoencoder_test.cpp diff --git a/src/mlpack/tests/ind2sub_test.cpp b/src/mlpack/tests/ind2sub_test.cpp new file mode 100644 index 0000000000..14baeba9bf --- /dev/null +++ b/src/mlpack/tests/ind2sub_test.cpp @@ -0,0 +1,19 @@ +#include +//#include + +#include +#include "old_boost_test_definitions.hpp" +BOOST_AUTO_TEST_SUITE(ind2sub_test); + +/** + * This tests handles the case wherein only one class exists in the input + * labels. It checks whether the only class supplied was the only class + * predicted. + */ +BOOST_AUTO_TEST_CASE(ind2sub_test) +{ + arma::mat A = arma::randu(5,5); + arma::uvec u = arma::ind2sub(arma::size(A), 3); + u.print(); +} +BOOST_AUTO_TEST_SUITE_END(); From dec9ab0b96fabfc61ddb29387658365d6ea44086 Mon Sep 17 00:00:00 2001 From: nilayjain Date: Mon, 6 Jun 2016 20:48:43 +0000 Subject: [PATCH 08/34] backported ind2sub and sub2ind --- src/mlpack/methods/CMakeLists.txt | 2 +- src/mlpack/tests/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt index 5c58bf9e45..00a67bd154 100644 --- a/src/mlpack/methods/CMakeLists.txt +++ b/src/mlpack/methods/CMakeLists.txt @@ -23,7 +23,7 @@ set(DIRS det emst edge_boxes -# fastmks + fastmks gmm hmm hoeffding_trees diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index fab4151a57..8b36a941c9 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -17,7 +17,7 @@ add_executable(mlpack_test det_test.cpp distribution_test.cpp emst_test.cpp -# fastmks_test.cpp + fastmks_test.cpp feedforward_network_test.cpp gmm_test.cpp hmm_test.cpp @@ -63,7 +63,7 @@ add_executable(mlpack_test sgd_test.cpp serialization.hpp serialization.cpp - # serialization_test.cpp + serialization_test.cpp softmax_regression_test.cpp sort_policy_test.cpp sparse_autoencoder_test.cpp From 69260a5adaa4f15f703e41e54a328d19f0fb01d6 Mon Sep 17 00:00:00 2001 From: nilayjain Date: Mon, 6 Jun 2016 21:02:17 +0000 Subject: [PATCH 09/34] Revert "edge_boxes: feature extraction" This reverts commit 9d85b64c6c6bdff608331195351d09abf56cfc96. --- src/mlpack/methods/CMakeLists.txt | 1 - src/mlpack/methods/edge_boxes/CMakeLists.txt | 20 - .../methods/edge_boxes/edge_boxes_main.cpp | 90 -- .../methods/edge_boxes/feature_extraction.hpp | 85 -- .../edge_boxes/feature_extraction_impl.hpp | 903 ------------------ 5 files changed, 1099 deletions(-) delete mode 100644 src/mlpack/methods/edge_boxes/CMakeLists.txt delete mode 100644 src/mlpack/methods/edge_boxes/edge_boxes_main.cpp delete mode 100644 src/mlpack/methods/edge_boxes/feature_extraction.hpp delete mode 100644 src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt index 00a67bd154..d0ea04ca58 100644 --- a/src/mlpack/methods/CMakeLists.txt +++ b/src/mlpack/methods/CMakeLists.txt @@ -22,7 +22,6 @@ set(DIRS decision_stump det emst - edge_boxes fastmks gmm hmm diff --git a/src/mlpack/methods/edge_boxes/CMakeLists.txt b/src/mlpack/methods/edge_boxes/CMakeLists.txt deleted file mode 100644 index e64722c2de..0000000000 --- a/src/mlpack/methods/edge_boxes/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ - -cmake_minimum_required(VERSION 2.8) - -# Define the files we need to compile. -# Anything not in this list will not be compiled into mlpack. -set(SOURCES - feature_extraction.hpp - feature_extraction_impl.hpp -) - -# Add directory name to sources. -set(DIR_SRCS) -foreach(file ${SOURCES}) - set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file}) -endforeach() -# Append sources (with directory name) to list of all mlpack sources (used at -# the parent scope). -set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) - -add_cli_executable(edge_boxes) diff --git a/src/mlpack/methods/edge_boxes/edge_boxes_main.cpp b/src/mlpack/methods/edge_boxes/edge_boxes_main.cpp deleted file mode 100644 index 3be7692ff9..0000000000 --- a/src/mlpack/methods/edge_boxes/edge_boxes_main.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file decision_stump.hpp - * @author - * - * Definition of decision stumps. - */ -#include -#include "feature_extraction.hpp" - -using namespace mlpack; -using namespace mlpack::structured_tree; -using namespace std; - -int main() -{ - /* - :param options: - num_images: number of images in the dataset. - rgbd: 0 for RGB, 1 for RGB + depth - shrink: amount to shrink channels - n_orient: number of orientations per gradient scale - grd_smooth_rad: radius for image gradient smoothing - grd_norm_rad: radius for gradient normalization - reg_smooth_rad: radius for reg channel smoothing - ss_smooth_rad: radius for sim channel smoothing - p_size: size of image patches - g_size: size of ground truth patches - n_cell: number of self similarity cells - - n_pos: number of positive patches per tree - n_neg: number of negative patches per tree - fraction: fraction of features to use to train each tree - n_tree: number of trees in forest to train - n_class: number of classes (clusters) for binary splits - min_count: minimum number of data points to allow split - min_child: minimum number of data points allowed at child nodes - max_depth: maximum depth of tree - split: options include 'gini', 'entropy' and 'twoing' - discretize: optional function mapping structured to class labels - - stride: stride at which to compute edges - sharpen: sharpening amount (can only decrease after training) - n_tree_eval: number of trees to evaluate per location - nms: if true apply non-maximum suppression to edges - */ - - map options; - options["num_images"] = 2; - options["row_size"] = 321; - options["col_size"] = 481; - options["rgbd"] = 0; - options["shrink"] = 2; - options["n_orient"] = 4; - options["grd_smooth_rad"] = 0; - options["grd_norm_rad"] = 4; - options["reg_smooth_rad"] = 2; - options["ss_smooth_rad"] = 8; - options["p_size"] = 32; - options["g_size"] = 16; - options["n_cell"] = 5; - - options["n_pos"] = 10000; - options["n_neg"] = 10000; - options["fraction"] = 0.25; - options["n_tree"] = 8; - options["n_class"] = 2; - options["min_count"] = 1; - options["min_child"] = 8; - options["max_depth"] = 64; - options["split"] = 0; // we use 0 for gini, 1 for entropy, 2 for other - options["stride"] = 2; - options["sharpen"] = 2; - options["n_tree_eval"] = 4; - options["nms"] = 1; // 1 for true, 0 for false - - StructuredForests SF(options); -// arma::uvec x(2); - //SF.GetFeatureDimension(x); - - arma::mat segmentations, boundaries, images; - data::Load("/home/nilay/example/small_images.csv", images); - data::Load("/home/nilay/example/small_boundary_1.csv", boundaries); - data::Load("/home/nilay/example/small_segmentation_1.csv", segmentations); - - arma::mat input_data = SF.LoadData(images, boundaries, segmentations); - cout << input_data.n_rows << " " << input_data.n_cols << endl; - SF.PrepareData(input_data); - return 0; -} - diff --git a/src/mlpack/methods/edge_boxes/feature_extraction.hpp b/src/mlpack/methods/edge_boxes/feature_extraction.hpp deleted file mode 100644 index ba14e23b5f..0000000000 --- a/src/mlpack/methods/edge_boxes/feature_extraction.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @file feature_extraction.hpp - * @author Nilay Jain - * - * Feature Extraction for the edge_boxes algorithm. - */ -#ifndef MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_HPP -#define MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_HPP -#define INF 999999.9999 -#define EPS 1E-20 -#include - -namespace mlpack { -namespace structured_tree { - -template -class StructuredForests -{ - - public: - - std::map options; - - StructuredForests(const std::map& inMap); - - MatType LoadData(MatType& images, MatType& boundaries, - MatType& segmentations); - - void PrepareData(MatType& InputData); - - private: - - arma::vec GetFeatureDimension(); - - arma::vec dt_1d(arma::vec& f, int n); - - void dt_2d(MatType& im); - - MatType dt_image(MatType& im, double on); - - arma::field GetFeatures(MatType& img,arma::umat& loc); - - CubeType CopyMakeBorder(CubeType& InImage, - int top, int left, int bottom, int right); - - void GetShrunkChannels(CubeType& InImage, CubeType& reg_ch, CubeType& ss_ch); - - CubeType RGB2LUV(CubeType& InImage); - - MatType bilinearInterpolation(MatType const &src, - size_t height, size_t width); - - CubeType sepFilter2D(CubeType& InImage, - arma::vec& kernel, int radius); - - CubeType ConvTriangle(CubeType& InImage, int radius); - - void Gradient(CubeType& InImage, - MatType& Magnitude, - MatType& Orientation); - - MatType MaxAndLoc(CubeType& mag, arma::umat& Location); - - CubeType Histogram(MatType& Magnitude, - MatType& Orientation, - int downscale, int interp); - - CubeType ViewAsWindows(CubeType& channels, arma::umat& loc); - - CubeType GetRegFtr(CubeType& channels, arma::umat& loc); - - CubeType GetSSFtr(CubeType& channels, arma::umat& loc); - - CubeType Rearrange(CubeType& channels); - - CubeType PDist(CubeType& features, arma::uvec& grid_pos); - -}; - - -} //namespace structured_tree -} // namespace mlpack -#include "feature_extraction_impl.hpp" -#endif - diff --git a/src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp b/src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp deleted file mode 100644 index 9680faa214..0000000000 --- a/src/mlpack/methods/edge_boxes/feature_extraction_impl.hpp +++ /dev/null @@ -1,903 +0,0 @@ -/** - * @file feature_extraction_impl.hpp - * @author Nilay Jain - * - * Implementation of feature extraction methods. - */ -#ifndef MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_IMPL_HPP -#define MLPACK_METHODS_EDGE_BOXES_STRUCTURED_TREE_IMPL_HPP - - -#include "feature_extraction.hpp" -#include - -namespace mlpack { -namespace structured_tree { - -template -StructuredForests:: -StructuredForests(const std::map& inMap) -{ - this->options = inMap; -} - -template -MatType StructuredForests:: -LoadData(MatType& images, MatType& boundaries, MatType& segmentations) -{ - int num_images = this->options["num_images"]; - int row_size = this->options["row_size"]; - int col_size = this->options["col_size"]; - MatType input_data(num_images * row_size * 5, col_size); - // we store the input data as follows: - // images (3), boundaries (1), segmentations (1). - int loop_iter = num_images * 5; - size_t row_idx = 0; - int col_i = 0, col_s = 0, col_b = 0; - for(size_t i = 0; i < loop_iter; ++i) - { - if (i % 5 == 4) - { - input_data.submat(row_idx, 0, row_idx + row_size - 1,\ - col_size - 1) = MatType(segmentations.colptr(col_s),\ - col_size, row_size).t(); - ++col_s; - } - else if (i % 5 == 3) - { - input_data.submat(row_idx, 0, row_idx + row_size - 1,\ - col_size - 1) = MatType(boundaries.colptr(col_b),\ - col_size, row_size).t(); - ++col_b; - } - else - { - input_data.submat(row_idx, 0, row_idx + row_size - 1,\ - col_size - 1) = MatType(images.colptr(col_i), - col_size, row_size).t(); - ++col_i; - } - row_idx += row_size; - } - return input_data; -} - -template -arma::vec StructuredForests:: -GetFeatureDimension() -{ - /* - shrink: amount to shrink channels - p_size: size of image patches - n_cell: number of self similarity cells - n_orient: number of orientations per gradient scale - */ - arma::vec P(2); - int shrink, p_size, n_cell; - shrink = this->options["shrink"]; - p_size = this->options["p_size"]; - n_cell = this->options["n_cell"]; - - /* - n_color_ch: number of color channels - n_grad_ch: number of gradient channels - n_ch: total number of channels - */ - int n_color_ch, n_grad_ch, n_ch; - if (this->options["rgbd"] == 0) - n_color_ch = 3; - else - n_color_ch = 4; - - n_grad_ch = 2 * (1 + this->options["n_orient"]); - - n_ch = n_color_ch + n_grad_ch; - P[0] = pow((p_size / shrink) , 2) * n_ch; - P[1] = pow(n_cell , 2) * (pow (n_cell, 2) - 1) / 2 * n_ch; - return P; -} - -template -arma::vec StructuredForests:: -dt_1d(arma::vec& f, int n) -{ - arma::vec d(n), v(n), z(n + 1); - int k = 0; - v[0] = 0.0; - z[0] = -INF; - z[1] = +INF; - for (size_t q = 1; q <= n - 1; ++q) - { - float s = ( (f[q] + q * q)-( f[v[k]] + v[k] * v[k]) ) / (2 * q - 2 * v[k]); - while (s <= z[k]) - { - --k; - s = ( (f[q] + q * q) - (f[v[k]] + v[k] * v[k]) ) / (2 * q - 2 * v[k]); - } - - k++; - v[k] = (double)q; - z[k] = s; - z[k+1] = +INF; - } - - k = 0; - for (int q = 0; q <= n-1; q++) - { - while (z[k+1] < q) - k++; - d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; - } - return d; -} - -template -void StructuredForests:: -dt_2d(MatType& im) -{ - arma::vec f(std::max(im.n_rows, im.n_cols)); - // transform along columns - for (size_t x = 0; x < im.n_cols; ++x) - { - f.subvec(0, im.n_rows - 1) = im.col(x); - arma::vec d = this->dt_1d(f, im.n_rows); - im.col(x) = d; - } - - // transform along rows - for (int y = 0; y < im.n_rows; y++) - { - f.subvec(0, im.n_cols - 1) = im.row(y).t(); - arma::vec d = this->dt_1d(f, im.n_cols); - im.row(y) = d.t(); - } -} - -/* euclidean distance transform of binary image using squared distance */ -template -MatType StructuredForests:: -dt_image(MatType& im, double on) -{ - MatType out = MatType(im.n_rows, im.n_cols); - out.fill(0.0); - out.elem( find(im != on) ).fill(INF); - this->dt_2d(out); - return out; -} - -template -CubeType StructuredForests:: -CopyMakeBorder(CubeType& InImage, int top, - int left, int bottom, int right) -{ - CubeType OutImage(InImage.n_rows + top + bottom, InImage.n_cols + left + right, InImage.n_slices); - - for(size_t i = 0; i < InImage.n_slices; ++i) - { - OutImage.slice(i).submat(top, left, InImage.n_rows + top - 1, InImage.n_cols + left - 1) - = InImage.slice(i); - - for(size_t j = 0; j < right; ++j) - { - OutImage.slice(i).col(InImage.n_cols + left + j).subvec(top, InImage.n_rows + top - 1) - = InImage.slice(i).col(InImage.n_cols - j - 1); - } - - for(int j = 0; j < left; ++j) - { - OutImage.slice(i).col(j).subvec(top, InImage.n_rows + top - 1) - = InImage.slice(i).col(left - 1 - j); - } - - for(int j = 0; j < top; j++) - { - - OutImage.slice(i).row(j) - = OutImage.slice(i).row(2 * top - 1 - j); - } - - for(int j = 0; j < bottom; j++) - { - OutImage.slice(i).row(InImage.n_rows + top + j) - = OutImage.slice(i).row(InImage.n_rows + top - j - 1); - } - - } - return OutImage; -} - -template -CubeType StructuredForests:: -RGB2LUV(CubeType& InImage) -{ - //assert type is double or float. - double a, y0, maxi; - a = pow(29.0, 3) / 27.0; - y0 = 8.0 / a; - maxi = 1.0 / 270.0; - - arma::vec table(1025); - for (size_t i = 0; i < 1025; ++i) - { - table(i) = i / 1024.0; - - if (table(i) > y0) - table(i) = 116 * pow(table(i), 1.0/3.0) - 16.0; - else - table(i) = table(i) * a; - - table(i) = table(i) * maxi; - } - - MatType rgb2xyz(3,3); - rgb2xyz(0,0) = 0.430574; rgb2xyz(0,1) = 0.430574; rgb2xyz(0,2) = 0.430574; - rgb2xyz(1,0) = 0.430574; rgb2xyz(1,1) = 0.430574; rgb2xyz(1,2) = 0.430574; - rgb2xyz(2,0) = 0.430574; rgb2xyz(2,1) = 0.430574; rgb2xyz(2,2) = 0.430574; - - //see how to calculate this efficiently. numpy.dot does this. - CubeType xyz(InImage.n_rows, InImage.n_cols, rgb2xyz.n_cols); - for(size_t i = 0; i < InImage.n_rows; ++i) - { - for(size_t j = 0; j < InImage.n_cols; ++j) - { - for(size_t k = 0; k < rgb2xyz.n_cols; ++k) - { - double s = 0.0; - for(size_t l = 0; l < InImage.n_slices; ++l) - s += InImage(i, j, l) * rgb2xyz(l, k); - xyz(i, j, k) = s; - } - } - } - - MatType nz(InImage.n_rows, InImage.n_cols); - - nz = 1.0 / ( xyz.slice(0) + (15 * xyz.slice(1) ) + - (3 * xyz.slice(2) + EPS)); - - MatType L = arma::reshape(L, xyz.n_rows, xyz.n_cols); - - MatType U, V; - U = L % (13 * 4 * (xyz.slice(0) % nz) - 13 * 0.197833) + 88 * maxi; - V = L % (13 * 9 * (xyz.slice(1) % nz) - 13 * 0.468331) + 134 * maxi; - - CubeType OutImage(InImage.n_rows, InImage.n_cols, InImage.n_slices); - OutImage.slice(0) = L; - OutImage.slice(1) = U; - OutImage.slice(2) = V; - //OutImage = arma::join_slices(L,U); - //OutImage = arma::join_slices(OutImage, V); - return OutImage; -} - -template -MatType StructuredForests:: -bilinearInterpolation(MatType const &src, - size_t height, size_t width) -{ - MatType dst(height, width); - double const x_ratio = static_cast((src.n_cols - 1)) / width; - double const y_ratio = static_cast((src.n_rows - 1)) / height; - for(size_t row = 0; row != dst.n_rows; ++row) - { - size_t y = static_cast(row * y_ratio); - double const y_diff = (row * y_ratio) - y; //distance of the nearest pixel(y axis) - double const y_diff_2 = 1 - y_diff; - for(size_t col = 0; col != dst.n_cols; ++col) - { - size_t x = static_cast(col * x_ratio); - double const x_diff = (col * x_ratio) - x; //distance of the nearet pixel(x axis) - double const x_diff_2 = 1 - x_diff; - double const y2_cross_x2 = y_diff_2 * x_diff_2; - double const y2_cross_x = y_diff_2 * x_diff; - double const y_cross_x2 = y_diff * x_diff_2; - double const y_cross_x = y_diff * x_diff; - dst(row, col) = y2_cross_x2 * src(y, x) + - y2_cross_x * src(y, x + 1) + - y_cross_x2 * src(y + 1, x) + - y_cross_x * src(y + 1, x + 1); - } - } - - return dst; -} - -template -CubeType StructuredForests:: -sepFilter2D(CubeType& InImage, arma::vec& kernel, int radius) -{ - CubeType OutImage = this->CopyMakeBorder(InImage, radius, radius, radius, radius); - - arma::vec row_res(1), col_res(1); - // reverse InImage and OutImage to avoid making an extra matrix. - for(size_t k = 0; k < OutImage.n_slices; ++k) - { - for(size_t j = radius; j < OutImage.n_cols - radius; ++j) - { - for(size_t i = radius; i < OutImage.n_rows - radius; ++i) - { - row_res = OutImage.slice(k).row(i).subvec(j - radius, j + radius) * kernel; - col_res = OutImage.slice(k).col(i).subvec(i - radius, i + radius).t() * kernel; - // divide by 2: avg of row_res and col_res, divide by 3: avg over 3 locations. - InImage(i - radius, j - radius, k) = (row_res(0) + col_res(0)) / 2 / 3; - } - } - } - - return InImage; -} - -template -CubeType StructuredForests:: -ConvTriangle(CubeType& InImage, int radius) -{ - if (radius == 0) - { - return InImage; - } - else if (radius <= 1) - { - double p = 12.0 / radius / (radius + 2) - 2; - arma::vec kernel = {1 , p, 1}; - kernel = kernel / (p + 2); - - return this->sepFilter2D(InImage, kernel, radius); - } - else - { - int len = 2 * radius + 1; - arma::vec kernel(len); - for( size_t i = 0; i < radius; ++i) - kernel(i) = i + 1; - - kernel(radius) = radius + 1; - - for( size_t i = radius + 1; i < len; ++i) - kernel(i) = i - 1; - return this->sepFilter2D(InImage, kernel, radius); - } -} - -//just a helper function, can't use it for anything else -//finds max numbers on cube axis and returns max values, -// also stores the locations of max values in Location -template -MatType StructuredForests:: -MaxAndLoc(CubeType& mag, arma::umat& Location) -{ - MatType MaxVal(Location.n_rows, Location.n_cols); - for(size_t i = 0; i < mag.n_rows; ++i) - { - for(size_t j = 0; j < mag.n_cols; ++j) - { - double max = -9999999999.0; int max_loc = 0; - for(size_t k = 0; k < mag.n_slices; ++k) - { - if(mag(i, j, k) > max) - { - max = mag(i, j, k); - MaxVal(i, j) = max; - Location(i, j) = k; - } - } - } - } - return MaxVal; -} - -template -void StructuredForests:: -Gradient(CubeType& InImage, - MatType& Magnitude, - MatType& Orientation) -{ - int grd_norm_rad = this->options["grd_norm_rad"]; - CubeType dx(InImage.n_rows, InImage.n_cols, InImage.n_slices), - dy(InImage.n_rows, InImage.n_cols, InImage.n_slices); - - dx.zeros(); - dy.zeros(); - - /* - From MATLAB documentation: - [FX,FY] = gradient(F), where F is a matrix, returns the - x and y components of the two-dimensional numerical gradient. - FX corresponds to ∂F/∂x, the differences in x (horizontal) direction. - FY corresponds to ∂F/∂y, the differences in the y (vertical) direction. - */ - - - /* - gradient calculates the central difference for interior data points. - For example, consider a matrix with unit-spaced data, A, that has - horizontal gradient G = gradient(A). The interior gradient values, G(:,j), are: - - G(:,j) = 0.5*(A(:,j+1) - A(:,j-1)); - where j varies between 2 and N-1, where N is size(A,2). - - The gradient values along the edges of the matrix are calculated with single-sided differences, so that - - G(:,1) = A(:,2) - A(:,1); - G(:,N) = A(:,N) - A(:,N-1); - - The spacing between points in each direction is assumed to be one. - */ - for (size_t i = 0; i < InImage.n_slices; ++i) - { - dx.slice(i).col(0) = InImage.slice(i).col(1) - InImage.slice(i).col(0); - dx.slice(i).col(InImage.n_cols - 1) = InImage.slice(i).col(InImage.n_cols - 1) - - InImage.slice(i).col(InImage.n_cols - 2); - - for (int j = 1; j < InImage.n_cols-1; j++) - dx.slice(i).col(j) = 0.5 * ( InImage.slice(i).col(j+1) - InImage.slice(i).col(j) ); - - // do same for dy. - dy.slice(i).row(0) = InImage.slice(i).row(1) - InImage.slice(i).row(0); - dy.slice(i).row(InImage.n_rows - 1) = InImage.slice(i).row(InImage.n_rows - 1) - - InImage.slice(i).row(InImage.n_rows - 2); - - for (int j = 1; j < InImage.n_rows-1; j++) - dy.slice(i).row(j) = 0.5 * ( InImage.slice(i).row(j+1) - InImage.slice(i).row(j) ); - } - - CubeType mag(InImage.n_rows, InImage.n_cols, InImage.n_slices); - for (size_t i = 0; i < InImage.n_slices; ++i) - { - mag.slice(i) = arma::sqrt( arma::square \ - ( dx.slice(i) + arma::square( dy.slice(i) ) ) ); - } - - arma::umat Location(InImage.n_rows, InImage.n_cols); - Magnitude = this->MaxAndLoc(mag, Location); - if(grd_norm_rad != 0) - { - //we have to do this ugly thing, or override ConvTriangle - // and sepFilter2D methods. - CubeType mag2(InImage.n_rows, InImage.n_cols, 1); - mag2.slice(0) = Magnitude; - mag2 = this->ConvTriangle(mag2, grd_norm_rad); - Magnitude = Magnitude / (mag2.slice(0) + 0.01); - } - MatType dx_mat(dx.n_rows, dx.n_cols),\ - dy_mat(dy.n_rows, dy.n_cols); - - for(size_t j = 0; j < InImage.n_cols; ++j) - { - for(size_t i = 0; i < InImage.n_rows; ++i) - { - dx_mat(i, j) = dx(i, j, Location(i, j)); - dy_mat(i, j) = dy(i, j, Location(i, j)); - } - } - Orientation = arma::atan(dy_mat / dx_mat); - Orientation.transform( [](double val) { if(val < 0) return (val + arma::datum::pi); else return (val);} ); - - for(size_t j = 0; j < InImage.n_cols; ++j) - { - for(size_t i = 0; i < InImage.n_rows; ++i) - { - if( abs(dx_mat(i, j)) + abs(dy_mat(i, j)) < 1E-5) - Orientation(i, j) = 0.5 * arma::datum::pi; - } - } -} - -template -CubeType StructuredForests:: -Histogram(MatType& Magnitude, - MatType& Orientation, - int downscale, int interp) -{ - //i don't think this function can be vectorized. - - //n_orient: number of orientations per gradient scale - int n_orient = this->options["n_orient"]; - //size of HistArr: n_rbin * n_cbin * n_orient . . . (create in caller...) - int n_rbin = (Magnitude.n_rows + downscale - 1) / downscale; - int n_cbin = (Magnitude.n_cols + downscale - 1) / downscale; - double o_range, o; - o_range = arma::datum::pi / n_orient; - - CubeType HistArr(n_rbin, n_cbin, n_orient); - HistArr.zeros(); - - int r, c, o1, o2; - for(size_t i = 0; i < Magnitude.n_rows; ++i) - { - for(size_t j = 0; j < Magnitude.n_cols; ++j) - { - r = i / downscale; - c = j / downscale; - - if( interp != 0) - { - o = Orientation(i, j) / o_range; - o1 = ((int) o) % n_orient; - o2 = (o1 + 1) % n_orient; - HistArr(r, c, o1) += Magnitude(i, j) * (1 + (int)o - o); - HistArr(r, c, o2) += Magnitude(i, j) * (o - (int) o); - } - else - { - o1 = (int) (Orientation(i, j) / o_range + 0.5) % n_orient; - HistArr(r, c, o1) += Magnitude(i, j); - } - } - } - - HistArr = HistArr / downscale; - - for (size_t i = 0; i < HistArr.n_slices; ++i) - HistArr.slice(i) = arma::square(HistArr.slice(i)); - - return HistArr; -} - -template -void StructuredForests:: -GetShrunkChannels(CubeType& InImage, CubeType& reg_ch, CubeType& ss_ch) -{ - CubeType luv = this->RGB2LUV(InImage); - - int shrink = this->options["shrink"]; - int n_orient = this->options["n_orient"]; - int grd_smooth_rad = this->options["grd_smooth_rad"]; - int grd_norm_rad = this->options["grd_norm_rad"]; - int num_channels = 13; - int rsize = luv.n_rows / shrink; - int csize = luv.n_cols / shrink; - CubeType channels(rsize, csize, num_channels); - - - int slice_idx = 0; - - for( slice_idx = 0; slice_idx < luv.n_slices; ++slice_idx) - channels.slice(slice_idx) - = this->bilinearInterpolation(luv.slice(slice_idx), (size_t)rsize, (size_t)csize); - - double scale = 0.5; - - while(scale <= 1.0) - { - CubeType img( (luv.n_rows * scale), - (luv.n_cols * scale), - luv.n_slices ); - - for( slice_idx = 0; slice_idx < luv.n_slices; ++slice_idx) - { - img.slice(slice_idx) = - this->bilinearInterpolation(luv.slice(slice_idx), - (luv.n_rows * scale), - (luv.n_cols * scale) ); - } - - CubeType OutImage = this->ConvTriangle(img, grd_smooth_rad); - - MatType Magnitude(InImage.n_rows, InImage.n_cols), - Orientation(InImage.n_rows, InImage.n_cols); - - this->Gradient(OutImage, Magnitude, Orientation); - - int downscale = std::max(1, (int) (shrink * scale)); - - CubeType Hist = this->Histogram(Magnitude, Orientation, - downscale, 0); - - channels.slice(slice_idx) = - bilinearInterpolation( Magnitude, rsize, csize); - slice_idx++; - for(size_t i = 0; i < InImage.n_slices; ++i) - channels.slice(i + slice_idx) = - bilinearInterpolation( Magnitude, rsize, csize); - slice_idx += 3; - scale += 0.5; - } - - //cout << "size of channels: " << arma::size(channels) << endl; - double reg_smooth_rad, ss_smooth_rad; - reg_smooth_rad = this->options["reg_smooth_rad"] / (double) shrink; - ss_smooth_rad = this->options["ss_smooth_rad"] / (double) shrink; - - - - - if (reg_smooth_rad > 1.0) - reg_ch = this->ConvTriangle(channels, (int) (std::round(reg_smooth_rad)) ); - else - reg_ch = this->ConvTriangle(channels, reg_smooth_rad); - - if (ss_smooth_rad > 1.0) - ss_ch = this->ConvTriangle(channels, (int) (std::round(ss_smooth_rad)) ); - else - ss_ch = this->ConvTriangle(channels, ss_smooth_rad); - -} - -template -CubeType StructuredForests:: -ViewAsWindows(CubeType& channels, arma::umat& loc) -{ - // 500 for pos_loc, and 500 for neg_loc. - // channels = 160, 240, 13. - CubeType features = CubeType(16, 16, 1000 * 13); - int patchSize = 16; - int p = patchSize / 2; - //increase the channel boundary to protect error against image boundaries. - CubeType inc_ch = this->CopyMakeBorder(channels, p, p, p, p); - for (size_t i = 0, channel = 0; i < loc.n_rows; ++i) - { - int x = loc(i, 0); - int y = loc(i, 1); - - /*(x,y) in channels, is ((x+p), (y+p)) in inc_ch*/ - //cout << "(x,y) = " << x << " " << y << endl; - CubeType patch = inc_ch.tube((x + p) - p, (y + p) - p,\ - (x + p) + p - 1, (y + p) + p - 1); - // since each patch has 13 channel we have to increase the index by 13 - - //cout <<"patch size = " << arma::size(patch) << endl; - - features.slices(channel, channel + 12) = patch; - //cout << "sahi hai " << endl; - channel += 13; - - } - //cout << "successfully returned. . ." << endl; - return features; -} - -template -CubeType StructuredForests:: -Rearrange(CubeType& channels) -{ - //we do (16,16,13*1000) to 256, 1000, 13, in vectorized code. - CubeType ch = CubeType(256, 1000, 13); - for(size_t i = 0; i < 1000; i++) - { - //MatType m(256, 13); - for(size_t j = 0; j < 13; ++j) - { - int sl = (i * j) / 1000; - //cout << "(i,j) = " << i << ", " << j << endl; - ch.slice(sl).col(i) = arma::vectorise(channels.slice(i * j)); - } - } - return ch; -} - -// returns 256 * 1000 * 13 dimension features. -template -CubeType StructuredForests:: -GetRegFtr(CubeType& channels, arma::umat& loc) -{ - int shrink = this->options["shrink"]; - int p_size = this->options["p_size"] / shrink; - CubeType wind = this->ViewAsWindows(channels, loc); - return this->Rearrange(wind); -} - -template -CubeType StructuredForests:: -PDist(CubeType& features, arma::uvec& grid_pos) -{ - // size of DestArr: - // InImage.n_rows * (InImage.n_rows - 1)/2 * InImage.n_slices - //find nC2 differences, for locations in the grid_pos. - //python: input: (716, 256, 13) --->(716, 25, 13) ; output: (716, 300, 13). - //input features : 256,1000,13; output: 300, 1000, 13 - - CubeType output(300, 1000, 13); - for(size_t k = 0; k < features.n_slices; ++k) - { - size_t r_idx = 0; - for(size_t i = 0; i < grid_pos.n_elem; ++i) //loop length : 25 - { - for(size_t j = i + 1; j < grid_pos.n_elem; ++j) //loop length : 25 - { - output.slice(k).row(r_idx) = features.slice(k).row(grid_pos(i)) - - features.slice(k).row(grid_pos(j)); - ++r_idx; - } - } - } - return output; -} - -//returns 300,1000,13 dimension features. -template -CubeType StructuredForests:: -GetSSFtr(CubeType& channels, arma::umat& loc) -{ - int shrink = this->options["shrink"]; - int p_size = this->options["p_size"] / shrink; - - //n_cell: number of self similarity cells - int n_cell = this->options["n_cell"]; - int half_cell_size = (int) round(p_size / (2.0 * n_cell)); - - arma::uvec g_pos(n_cell); - for(size_t i = 0; i < n_cell; ++i) - { - g_pos(i) = (int)round( (i + 1) * (p_size + 2 * half_cell_size \ - - 1) / (n_cell + 1.0) - half_cell_size); - } - arma::uvec grid_pos(n_cell * n_cell); - size_t k = 0; - for(size_t i = 0; i < n_cell; ++i) - { - for(size_t j = 0; j < n_cell; ++j) - { - grid_pos(k) = g_pos(i) * p_size + g_pos(j); - ++k; - } - } - - CubeType wind = this->ViewAsWindows(channels, loc); - CubeType re_wind = this->Rearrange(wind); - - return this->PDist(re_wind, grid_pos); -} - -template -arma::field StructuredForests:: -GetFeatures(MatType& image, arma::umat& loc) -{ - int row_size = this->options["row_size"]; - int col_size = this->options["col_size"]; - int bottom, right; - bottom = (4 - (image.n_rows / 3) % 4) % 4; - right = (4 - image.n_cols % 4) % 4; - //cout << "Botttom = " << bottom << " right = " << right << endl; - - CubeType InImage(image.n_rows / 3, image.n_cols, 3); - - for(size_t i = 0; i < 3; ++i) - { - InImage.slice(i) = image.submat(i * row_size, 0, \ - (i + 1) * row_size - 1, col_size - 1); - } - - CubeType OutImage = this->CopyMakeBorder(InImage, 0, 0, bottom, right); - - int num_channels = 13; - int shrink = this->options["shrink"]; - int rsize = OutImage.n_rows / shrink; - int csize = OutImage.n_cols / shrink; - - /* this part gives double free or corruption out error - when executed for a second time */ - CubeType reg_ch = CubeType(rsize, csize, num_channels); - CubeType ss_ch = CubeType(rsize, csize, num_channels); - this->GetShrunkChannels(InImage, reg_ch, ss_ch); - - loc = loc / shrink; - - CubeType reg_ftr = this->GetRegFtr(reg_ch, loc); - CubeType ss_ftr = this->GetSSFtr(ss_ch, loc); - arma::field F(2,1); - F(0,0) = reg_ftr; - F(1,0) = ss_ftr; - return F; - //delete reg_ch; - //free(reg_ch); - //free(ss_ch); -} - -template -void StructuredForests:: -PrepareData(MatType& InputData) -{ - int num_images = this->options["num_images"]; - int n_tree = this->options["n_tree"]; - int n_pos = this->options["n_pos"]; - int n_neg = this->options["n_neg"]; - double fraction = 0.25; - int p_size = this->options["p_size"]; - int g_size = this->options["g_size"]; - int shrink = this->options["shrink"]; - int row_size = this->options["row_size"]; - int col_size = this->options["col_size"]; - // p_rad = radius of image patches. - // g_rad = radius of ground truth patches. - int p_rad = p_size / 2, g_rad = g_size / 2; - - arma::vec FtrDim = this->GetFeatureDimension(); - int n_ftr_dim = FtrDim(0) + FtrDim(1); - int n_smp_ftr_dim = int(n_ftr_dim * fraction); - - for(size_t i = 0; i < n_tree; ++i) - { - //implement the logic for if data already exists. - MatType ftrs = arma::zeros(n_pos + n_neg, n_smp_ftr_dim); - - //effectively a 3d array. . . - MatType lbls = arma::zeros( (n_pos + n_neg ) * g_size, g_size); - - - int loop_iter = num_images * 5; - for(size_t j = 0; j < loop_iter; j += 5) - { - MatType img, bnds, segs; - img = InputData.submat(j * row_size, 0, (j + 3) * row_size - 1, col_size - 1); - bnds = InputData.submat( (j + 3) * row_size, 0, \ - (j + 4) * row_size - 1, col_size - 1 ); - segs = InputData.submat( (j + 4) * row_size, 0, \ - (j + 5) * row_size - 1, col_size - 1 ); - - MatType mask = arma::zeros(row_size, col_size); - for(size_t b = 0; b < mask.n_cols; b = b + shrink) - for(size_t a = 0; a < mask.n_rows; a = a + shrink) - mask(a, b) = 1; - mask.col(p_rad - 1).fill(0); - mask.row( (mask.n_rows - 1) - (p_rad - 1) ).fill(0); - mask.submat(0, 0, mask.n_rows - 1, p_rad - 1).fill(0); - mask.submat(0, mask.n_cols - p_rad, mask.n_rows - 1, - mask.n_cols - 1).fill(0); - - // number of positive or negative patches per ground truth. - //int n_patches_per_gt = (int) (ceil( (float)n_pos / num_images )); - int n_patches_per_gt = 500; - //cout << "n_patches_per_gt = " << n_patches_per_gt << endl; - MatType dis = arma::sqrt( this->dt_image(bnds, 1) ); - MatType dis2 = dis; - //dis.transform( [](double val, const int& g_rad) { return (double)(val < g_rad); } ); - //dis2.transform( [](double val, const int& g_rad) { return (double)(val >= g_rad); } ); - //dis.elem( arma::find(dis >= g_rad) ).zeros(); - //dis2.elem( arma::find(dis < g_rad) ).zeros(); - - - arma::uvec pos_loc = arma::find( (dis < g_rad) % mask ); - arma::uvec neg_loc = arma::find( (dis >= g_rad) % mask ); - - pos_loc = arma::shuffle(pos_loc); - neg_loc = arma::shuffle(neg_loc); - - arma::umat loc(n_patches_per_gt * 2, 2); - //cout << "pos_loc size: " << arma::size(pos_loc) << " neg_loc size: " << arma::size(neg_loc) << endl; - //cout << "n_patches_per_gt = " << n_patches_per_gt << endl; - for(size_t i = 0; i < n_patches_per_gt; ++i) - { - loc.row(i) = arma::ind2sub(arma::size(dis.n_rows, dis.n_cols), pos_loc(i) ).t(); - //cout << "pos_loc: " << loc(i, 0) << ", " << loc(i, 1) << endl; - } - - for(size_t i = n_patches_per_gt; i < 2 * n_patches_per_gt; ++i) - { - loc.row(i) = arma::ind2sub(arma::size(dis.n_rows, dis.n_cols), neg_loc(i) ).t(); - //cout << "neg_loc: " << loc(i, 0) << ", " << loc(i, 1) << endl; - } - - // cout << "num patches = " << n_patches_per_gt << " num elements + = " << pos_loc.n_elem\ - // << " num elements - = " << neg_loc.n_elem << " dis.size " << dis.n_elem << endl; - - //Field F contains reg_ftr and ss_ftr. - arma::field F = this->GetFeatures(img, loc); - //randomly sample 70 values each from reg_ftr and ss_ftr. - /* - CubeType ftr(140, 1000, 13); - arma::uvec r = (0, 255, 256); - arma::uvec s = (0, 299, 300); - arma::uvec rs = r.shuffle(); - arma::uvec ss = s.shuffle(); - */ - CubeType lbl(g_size, g_size, 1000); - CubeType s(segs.n_rows, segs.n_cols, 1); - s.slice(0) = segs; - CubeType in_segs = this->CopyMakeBorder(s, g_rad, - g_rad, g_rad, g_rad); - for(size_t i = 0; i < loc.n_rows; ++i) - { - int x = loc(i, 0); int y = loc(i, 1); - //cout << "x, y = " << x << " " << y << endl; - lbl.slice(i) = in_segs.slice(0).submat((x + g_rad) - g_rad, (y + g_rad) - g_rad, - (x + g_rad) + g_rad - 1, (y + g_rad) + g_rad - 1); - } - } - } -} - - -} // namespace structured_tree -} // namespace mlpack -#endif - From 1dfb208c7f4d652a2d194d8e5159e816cf629cb8 Mon Sep 17 00:00:00 2001 From: nilayjain Date: Mon, 6 Jun 2016 21:09:53 +0000 Subject: [PATCH 10/34] backported sub2ind & ind2sub --- src/mlpack/core/arma_extend/fn_ind2sub.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mlpack/core/arma_extend/fn_ind2sub.hpp b/src/mlpack/core/arma_extend/fn_ind2sub.hpp index b4bbfe7077..7991b9dc58 100644 --- a/src/mlpack/core/arma_extend/fn_ind2sub.hpp +++ b/src/mlpack/core/arma_extend/fn_ind2sub.hpp @@ -1,5 +1,6 @@ - #if (ARMA_VERSION_MAJOR < 6 && ARMA_VERSION_MINOR < 399) + #if (ARMA_VERSION_MAJOR < 6 || \ + (ARMA_VERSION_MAJOR == 6 && ARMA_VERSION_MINOR < 399)) inline uvec ind2sub(const SizeMat& s, const uword i) From 81079bc9e142595adeb066df8906b35d3f370455 Mon Sep 17 00:00:00 2001 From: Keon Kim Date: Wed, 8 Jun 2016 02:27:45 +0900 Subject: [PATCH 11/34] fix doc tutorial --- doc/tutorials/README.md | 48 +++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/doc/tutorials/README.md b/doc/tutorials/README.md index f4114ac40f..c75f5ab10b 100644 --- a/doc/tutorials/README.md +++ b/doc/tutorials/README.md @@ -1,20 +1,40 @@ ## Tutorials -Tutorials for mlpack can be found [here : mlpack tutorials](http://www.mlpack.org/tutorial.html). +Tutorials for mlpack can be found [here : mlpack tutorials](http://www.mlpack.org/tutorials.html). -### Method-specific tutorials -* [NeighborSearch tutorial (mlpack_knn / mlpack_kfn)](http://www.mlpack.org/doxygen.php?doc=nstutorial.html) -* [RangeSearch tutorial (mlpack_range_search)](http://www.mlpack.org/doxygen.php?doc=rstutorial.html) -* [LinearRegression tutorial (mlpack_linear_regression)](http://www.mlpack.org/doxygen.php?doc=lrtutorial.html) -* [Density Estimation Trees tutorial (mlpack_det)](http://www.mlpack.org/doxygen.php?doc=dettutorial.html) -* [Euclidean Minimum Spanning Trees tutorial (mlpack_emst)](http://www.mlpack.org/doxygen.php?doc=emst_tutorial.html) -* [K-Means tutorial (mlpack_kmeans)](http://www.mlpack.org/doxygen.php?doc=kmtutorial.html) -* [FastMKS tutorial (mlpack_fastmks)](http://www.mlpack.org/doxygen.php?doc=fmkstutorial.html) ### General mlpack tutorials -* [Building mlpack from source](http://www.mlpack.org/doxygen.php?doc=build.html) -* [mlpack input and output](http://www.mlpack.org/doxygen.php?doc=iodoc.html) -* [Matrices in mlpack](http://www.mlpack.org/doxygen.php?doc=matrices.html) -* [Simple sample mlpack programs](http://www.mlpack.org/doxygen.php?doc=sample.html) -* [mlpack timers](http://www.mlpack.org/doxygen.php?doc=timer.html) + +These tutorials introduce the basic concepts of working with mlpack, aimed at developers who want to use and contribute to mlpack but are not sure where to start. + +* [Building mlpack from source](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=build.html) +* [File Formats in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=formatdoc.html) +* [Matrices in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=matrices.html) +* [mlpack input and output](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=iodoc.html) +* [mlpack timers](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=timer.html) +* [Simple sample mlpack programs](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=sample.html) + + +### Method-specific tutorials + +These tutorials introduce the various methods mlpack offers, aimed at users who want to get started quickly. These tutorials start with simple examples and progress to complex, extensible uses. + +* [NeighborSearch tutorial (mlpack_knn / mlpack_kfn)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=nstutorial.html) +* [LinearRegression tutorial (mlpack_linear_regression)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=lrtutorial.html) +* [RangeSearch tutorial (mlpack_range_search)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=rstutorial.html) +* [Density Estimation Trees tutorial (mlpack_det)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=dettutorial.html) +* [K-Means tutorial (mlpack_kmeans)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=kmtutorial.html) +* [FastMKS tutorial (mlpack_fastmks)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=fmkstutorial.html) +* [Euclidean Minimum Spanning Trees tutorial (mlpack_emst)](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=emst_tutorial.html) +* [Alternating Matrix Factorization Tutorial](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=amftutorial.html) +* [Collaborative Filtering Tutorial](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=cftutorial.html) + + +### Policy Class Documentation + +mlpack uses templates to achieve its genericity and flexibility. Some of the template types used by mlpack are common across multiple machine learning algorithms. The links below provide documentation for some of these common types. + +[The MetricType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=metrics.html) +[The KernelType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=kernels.html) +[The TreeType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=trees.html) \ No newline at end of file From 14aca3718be01738fe2e541a48058491a7e59cd6 Mon Sep 17 00:00:00 2001 From: Marcus Edel Date: Tue, 7 Jun 2016 19:32:35 +0200 Subject: [PATCH 12/34] Use appveyor cache (nuget and armadillo). --- .appveyor.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 20610299e3..3dff67faae 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,8 +18,8 @@ build_script: - ps: cp C:\projects\mlpack\boost_random-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\ - ps: cp C:\projects\mlpack\boost_serialization-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\ - ps: cp C:\projects\mlpack\boost_unit_test_framework-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\ - - appveyor DownloadFile http://sourceforge.net/projects/arma/files/armadillo-6.500.5.tar.gz - - 7z x armadillo-6.500.5.tar.gz -so | 7z x -si -ttar > nul + - if not exist armadillo.tar.gz appveyor DownloadFile "http://sourceforge.net/projects/arma/files/armadillo-6.500.5.tar.gz" -FileName armadillo.tar.gz + - 7z x armadillo.tar.gz -so | 7z x -si -ttar > nul - cd armadillo-6.500.5 && mkdir build && cd build - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo" -DBUILD_SHARED_LIBS=OFF .. - '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\armadillo-6.500.5\build\armadillo.sln" /m /verbosity:quiet /p:Configuration=Release;Platform=x64' @@ -40,6 +40,10 @@ notifications: on_build_failure: true on_build_status_changed: true +cache: + - packages -> **\packages.config + - armadillo.tar.gz -> appveyor.yaml + # All plans have maximum build job execution time of 60 minutes. But right, now # the machine takes 30 minutes to build the code and at least 50 minutes to run # all tests. From 60ffbd238b2beaeb854fa9331d11572f46e939fd Mon Sep 17 00:00:00 2001 From: Keon Kim Date: Wed, 8 Jun 2016 03:03:14 +0900 Subject: [PATCH 13/34] fix typo --- doc/tutorials/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorials/README.md b/doc/tutorials/README.md index c75f5ab10b..81a382b619 100644 --- a/doc/tutorials/README.md +++ b/doc/tutorials/README.md @@ -35,6 +35,6 @@ These tutorials introduce the various methods mlpack offers, aimed at users who mlpack uses templates to achieve its genericity and flexibility. Some of the template types used by mlpack are common across multiple machine learning algorithms. The links below provide documentation for some of these common types. -[The MetricType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=metrics.html) -[The KernelType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=kernels.html) -[The TreeType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=trees.html) \ No newline at end of file +* [The MetricType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=metrics.html) +* [The KernelType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=kernels.html) +* [The TreeType policy in mlpack](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=trees.html) From 05fcf0bc43f6ee18cbccca8c5558197b2c50f511 Mon Sep 17 00:00:00 2001 From: nilayjain Date: Tue, 7 Jun 2016 20:00:53 +0000 Subject: [PATCH 14/34] added test for ind2sub and sub2ind --- src/mlpack/tests/ind2sub_test.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/mlpack/tests/ind2sub_test.cpp b/src/mlpack/tests/ind2sub_test.cpp index 14baeba9bf..aaf1e78abd 100644 --- a/src/mlpack/tests/ind2sub_test.cpp +++ b/src/mlpack/tests/ind2sub_test.cpp @@ -1,19 +1,23 @@ #include -//#include - #include #include "old_boost_test_definitions.hpp" BOOST_AUTO_TEST_SUITE(ind2sub_test); /** - * This tests handles the case wherein only one class exists in the input - * labels. It checks whether the only class supplied was the only class - * predicted. + * This test checks whether ind2sub and sub2ind are + * compiled successfully and that they function properly. */ BOOST_AUTO_TEST_CASE(ind2sub_test) { - arma::mat A = arma::randu(5,5); - arma::uvec u = arma::ind2sub(arma::size(A), 3); - u.print(); + arma::mat A = arma::randu(4,5); + size_t index = 13; + arma::uvec u = arma::ind2sub(arma::size(A), index); + + BOOST_REQUIRE_EQUAL(u(0), index % A.n_rows); + BOOST_REQUIRE_EQUAL(u(1), index / A.n_rows); + + index = arma::sub2ind(arma::size(A), u(0), u(1)); + BOOST_REQUIRE_EQUAL(index, u(0) + u(1) * A.n_rows); } BOOST_AUTO_TEST_SUITE_END(); + From 83d5850e687b4d785e6542ecf7002c460bc2e682 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 7 Jun 2016 16:52:17 -0400 Subject: [PATCH 15/34] Minor style fixes for ind2sub() test. --- src/mlpack/tests/ind2sub_test.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/mlpack/tests/ind2sub_test.cpp b/src/mlpack/tests/ind2sub_test.cpp index aaf1e78abd..ef1014be0c 100644 --- a/src/mlpack/tests/ind2sub_test.cpp +++ b/src/mlpack/tests/ind2sub_test.cpp @@ -1,7 +1,14 @@ +/** + * @file ind2sub_test.cpp + * @author Nilay Jain + * + * Test the backported Armadillo ind2sub() and sub2ind() functions. + */ #include #include #include "old_boost_test_definitions.hpp" -BOOST_AUTO_TEST_SUITE(ind2sub_test); + +BOOST_AUTO_TEST_SUITE(ind2subTest); /** * This test checks whether ind2sub and sub2ind are @@ -12,12 +19,12 @@ BOOST_AUTO_TEST_CASE(ind2sub_test) arma::mat A = arma::randu(4,5); size_t index = 13; arma::uvec u = arma::ind2sub(arma::size(A), index); - + BOOST_REQUIRE_EQUAL(u(0), index % A.n_rows); BOOST_REQUIRE_EQUAL(u(1), index / A.n_rows); index = arma::sub2ind(arma::size(A), u(0), u(1)); BOOST_REQUIRE_EQUAL(index, u(0) + u(1) * A.n_rows); } -BOOST_AUTO_TEST_SUITE_END(); +BOOST_AUTO_TEST_SUITE_END(); From 3be8ddcb227d6f2d63447063b61dc8a141b9c0d0 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 7 Jun 2016 17:01:20 -0400 Subject: [PATCH 16/34] Add new contributors. --- COPYRIGHT.txt | 4 ++++ src/mlpack/core.hpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index d04af2c29c..10a3ee2c6e 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -55,6 +55,10 @@ Copyright: Copyright 2016, Palash Ahuja Copyright 2016, Yannis Mentekidis Copyright 2016, Ranjan Mondal + Copyright 2016, Mikhail Lozhnikov + Copyright 2016, Marcos Pividori + Copyright 2016, Keon Kim + Copyright 2016, Nilay Jain License: BSD-3-clause All rights reserved. . diff --git a/src/mlpack/core.hpp b/src/mlpack/core.hpp index 9df2947692..c0cbeea1ab 100644 --- a/src/mlpack/core.hpp +++ b/src/mlpack/core.hpp @@ -189,6 +189,10 @@ * - Palash Ahuja * - Yannis Mentekidis * - Ranjan Mondal + * - Mikhail Lozhnikov + * - Marcos Pividori + * - Keon Kim + * - Nilay Jain */ // First, include all of the prerequisites. From 8551a21f9821399ded164d8dbb11e453bcb33c45 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 7 Jun 2016 18:49:42 -0400 Subject: [PATCH 17/34] Try debugging symbols for AppVeyor build to see if it is faster. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3dff67faae..9c9fcbbea7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -24,7 +24,7 @@ build_script: - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo" -DBUILD_SHARED_LIBS=OFF .. - '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\armadillo-6.500.5\build\armadillo.sln" /m /verbosity:quiet /p:Configuration=Release;Platform=x64' - cd C:\projects\mlpack && mkdir build && cd build - - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-6.500.5/include" -DARMADILLO_LIBRARY:FILEPATH="C:\projects\mlpack\armadillo-6.500.5\build\Debug\armadillo.lib" -DBOOST_INCLUDEDIR:PATH="C:\projects\mlpack\boost.1.60.0.0\lib\native\include" -DBOOST_LIBRARYDIR:PATH="C:\projects\mlpack\boost_libs" -DDEBUG=OFF -DPROFILE=OFF .. + - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-6.500.5/include" -DARMADILLO_LIBRARY:FILEPATH="C:\projects\mlpack\armadillo-6.500.5\build\Debug\armadillo.lib" -DBOOST_INCLUDEDIR:PATH="C:\projects\mlpack\boost.1.60.0.0\lib\native\include" -DBOOST_LIBRARYDIR:PATH="C:\projects\mlpack\boost_libs" -DDEBUG=ON -DPROFILE=ON .. - '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\build\mlpack.sln" /m /verbosity:normal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /nologo /p:BuildInParallel=true /p:Configuration=Release;Platform=x64' - 7z a mlpack-windows-no-libs.zip "%APPVEYOR_BUILD_FOLDER%\build\Release\*.exe" - 7z a mlpack-windows.zip "%APPVEYOR_BUILD_FOLDER%\build\Release\*.*" "%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/*.*" From 428191f27084f76f58c3a4f7a284cd4cd1188906 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 2 May 2016 21:30:56 +0000 Subject: [PATCH 18/34] Handle empty centroids _only_ in EmptyClusterPolicy. --- src/mlpack/methods/kmeans/CMakeLists.txt | 1 + .../methods/kmeans/allow_empty_clusters.hpp | 14 ++-- .../methods/kmeans/kill_empty_clusters.hpp | 65 +++++++++++++++++++ 3 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 src/mlpack/methods/kmeans/kill_empty_clusters.hpp diff --git a/src/mlpack/methods/kmeans/CMakeLists.txt b/src/mlpack/methods/kmeans/CMakeLists.txt index 2c3eafc666..df295c26b2 100644 --- a/src/mlpack/methods/kmeans/CMakeLists.txt +++ b/src/mlpack/methods/kmeans/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES elkan_kmeans_impl.hpp hamerly_kmeans.hpp hamerly_kmeans_impl.hpp + kill_empty_clusters.hpp kmeans.hpp kmeans_impl.hpp max_variance_new_cluster.hpp diff --git a/src/mlpack/methods/kmeans/allow_empty_clusters.hpp b/src/mlpack/methods/kmeans/allow_empty_clusters.hpp index d8388dd548..7aacdf7943 100644 --- a/src/mlpack/methods/kmeans/allow_empty_clusters.hpp +++ b/src/mlpack/methods/kmeans/allow_empty_clusters.hpp @@ -24,8 +24,8 @@ class AllowEmptyClusters AllowEmptyClusters() { } /** - * This function does nothing. It is called by K-Means when K-Means detects - * an empty cluster. + * This function allows empty clusters to persist simply by leaving the empty + * cluster in its last position. * * @tparam MatType Type of data (arma::mat or arma::spmat). * @param data Dataset on which clustering is being performed. @@ -43,15 +43,15 @@ class AllowEmptyClusters template static inline force_inline size_t EmptyCluster( const MatType& /* data */, - const size_t /* emptyCluster */, - const arma::mat& /* oldCentroids */, - arma::mat& /* newCentroids */, + const size_t emptyCluster, + const arma::mat& oldCentroids, + arma::mat& newCentroids, arma::Col& /* clusterCounts */, MetricType& /* metric */, const size_t /* iteration */) { - // Empty clusters are okay! Do nothing. - return 0; + // Take the last iteration's centroid. + newCentroids.col(emptyCluster) = oldCentroids.col(emptyCluster); } //! Serialize the empty cluster policy (nothing to do). diff --git a/src/mlpack/methods/kmeans/kill_empty_clusters.hpp b/src/mlpack/methods/kmeans/kill_empty_clusters.hpp new file mode 100644 index 0000000000..9b0038e351 --- /dev/null +++ b/src/mlpack/methods/kmeans/kill_empty_clusters.hpp @@ -0,0 +1,65 @@ +/** + * @file allow_empty_clusters.hpp + * @author Ryan Curtin + * + * This very simple policy is used when K-Means is allowed to return empty + * clusters. + */ +#ifndef __MLPACK_METHODS_KMEANS_KILL_EMPTY_CLUSTERS_HPP +#define __MLPACK_METHODS_KMEANS_KILL_EMPTY_CLUSTERS_HPP + +#include + +namespace mlpack { +namespace kmeans { + +/** + * Policy which allows K-Means to "kill" empty clusters without any error being + * reported. This means the centroids will be filled with DBL_MAX. + */ +class KillEmptyClusters +{ + public: + //! Default constructor required by EmptyClusterPolicy policy. + AllowEmptyClusters() { } + + /** + * This function sets an empty cluster found during k-means to all DBL_MAX + * (i.e. an invalid "dead" cluster). + * + * @tparam MatType Type of data (arma::mat or arma::spmat). + * @param data Dataset on which clustering is being performed. + * @param emptyCluster Index of cluster which is empty. + * @param oldCentroids Centroids of each cluster (one per column) at the start + * of the iteration. + * @param newCentroids Centroids of each cluster (one per column) at the end + * of the iteration. + * @param clusterCounts Number of points in each cluster. + * @param assignments Cluster assignments of each point. + * @param iteration Number of iteration. + * + * @return Number of points changed (0). + */ + template + static inline force_inline size_t EmptyCluster( + const MatType& /* data */, + const size_t emptyCluster, + const arma::mat& /* oldCentroids */, + arma::mat& newCentroids, + arma::Col& /* clusterCounts */, + MetricType& /* metric */, + const size_t /* iteration */) + { + // Kill the empty cluster. + newCentroids.col(emptyCluster).fill(DBL_MAX); + } + + //! Serialize the empty cluster policy (nothing to do). + template + void Serialize(Archive& /* ar */, const unsigned int /* version */) { } +}; + +} // namespace kmeans +} // namespace mlpack + +#endif From 1c87ed95c5f7a00425b5c0e1bf36f8c991c97916 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 2 May 2016 21:31:37 +0000 Subject: [PATCH 19/34] Remove any handling of empty clusters from LloydStepTypes. --- src/mlpack/methods/kmeans/dual_tree_kmeans_impl.hpp | 1 - src/mlpack/methods/kmeans/elkan_kmeans_impl.hpp | 2 -- src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp | 2 -- src/mlpack/methods/kmeans/naive_kmeans.hpp | 5 ++++- src/mlpack/methods/kmeans/naive_kmeans_impl.hpp | 2 -- src/mlpack/methods/kmeans/pelleg_moore_kmeans_impl.hpp | 6 +----- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/mlpack/methods/kmeans/dual_tree_kmeans_impl.hpp b/src/mlpack/methods/kmeans/dual_tree_kmeans_impl.hpp index 9bcf464c62..ed21c4c288 100644 --- a/src/mlpack/methods/kmeans/dual_tree_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/dual_tree_kmeans_impl.hpp @@ -179,7 +179,6 @@ double DualTreeKMeans::Iterate( { if (counts[c] == 0) { - newCentroids.col(c).fill(DBL_MAX); clusterDistances[c] = 0; } else diff --git a/src/mlpack/methods/kmeans/elkan_kmeans_impl.hpp b/src/mlpack/methods/kmeans/elkan_kmeans_impl.hpp index 90659a50e8..27751d864b 100644 --- a/src/mlpack/methods/kmeans/elkan_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/elkan_kmeans_impl.hpp @@ -155,8 +155,6 @@ double ElkanKMeans::Iterate(const arma::mat& centroids, { if (counts[c] > 0) newCentroids.col(c) /= counts[c]; - else - newCentroids.col(c).fill(DBL_MAX); // Fill with invalid value. moveDistances(c) = metric.Evaluate(newCentroids.col(c), centroids.col(c)); cNorm += std::pow(moveDistances(c), 2.0); diff --git a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp index 244faaa484..1c3ac79492 100644 --- a/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/hamerly_kmeans_impl.hpp @@ -132,8 +132,6 @@ double HamerlyKMeans::Iterate(const arma::mat& centroids, { if (counts(c) > 0) newCentroids.col(c) /= counts(c); - else - newCentroids.col(c).fill(DBL_MAX); // Empty cluster. // Calculate movement. const double movement = metric.Evaluate(centroids.col(c), diff --git a/src/mlpack/methods/kmeans/naive_kmeans.hpp b/src/mlpack/methods/kmeans/naive_kmeans.hpp index ee4f2fc416..abb56556b0 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans.hpp @@ -35,10 +35,13 @@ class NaiveKMeans /** * Run a single iteration of the Lloyd algorithm, updating the given centroids - * into the newCentroids matrix. + * into the newCentroids matrix. If any cluster is empty (that is, if any + * cluster has no points assigned to it), then the centroid associated with + * that cluster may be filled with invalid data (it will be corrected later). * * @param centroids Current cluster centroids. * @param newCentroids New cluster centroids. + * @param counts Number of points in each cluster at the end of the iteration. */ double Iterate(const arma::mat& centroids, arma::mat& newCentroids, diff --git a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp index 2457a5929f..239169c548 100644 --- a/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/naive_kmeans_impl.hpp @@ -61,8 +61,6 @@ double NaiveKMeans::Iterate(const arma::mat& centroids, for (size_t i = 0; i < centroids.n_cols; ++i) if (counts(i) != 0) newCentroids.col(i) /= counts(i); - else - newCentroids.col(i).fill(DBL_MAX); // Invalid value. distanceCalculations += centroids.n_cols * dataset.n_cols; diff --git a/src/mlpack/methods/kmeans/pelleg_moore_kmeans_impl.hpp b/src/mlpack/methods/kmeans/pelleg_moore_kmeans_impl.hpp index daca5ea424..8403b07ecc 100644 --- a/src/mlpack/methods/kmeans/pelleg_moore_kmeans_impl.hpp +++ b/src/mlpack/methods/kmeans/pelleg_moore_kmeans_impl.hpp @@ -61,11 +61,7 @@ double PellegMooreKMeans::Iterate( double residual = 0.0; for (size_t c = 0; c < centroids.n_cols; ++c) { - if (counts[c] == 0) - { - newCentroids.col(c).fill(DBL_MAX); // Should have happened anyway I think. - } - else + if (counts[c] > 0) { newCentroids.col(c) /= counts(c); residual += std::pow(metric.Evaluate(centroids.col(c), From 76c0fc62838e6db45df638475467ffaab2a2aa41 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 2 May 2016 21:31:51 +0000 Subject: [PATCH 20/34] Update tutorial to discuss empty clusters and LloydStepType. --- doc/tutorials/kmeans/kmeans.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/kmeans/kmeans.txt b/doc/tutorials/kmeans/kmeans.txt index 1f54f96b2e..c9f7e80e6b 100644 --- a/doc/tutorials/kmeans/kmeans.txt +++ b/doc/tutorials/kmeans/kmeans.txt @@ -653,11 +653,13 @@ The \c LloydStepType policy also mandates three functions: @code /** * Run a single iteration of the Lloyd algorithm, updating the given centroids - * into the newCentroids matrix. + * into the newCentroids matrix. If any cluster is empty (that is, if any + * cluster has no points assigned to it), then the centroid associated with + * that cluster may be filled with invalid data (it will be corrected later). * * @param centroids Current cluster centroids. * @param newCentroids New cluster centroids. - * @param counts Counts of the number of points in each cluster. + * @param counts Number of points in each cluster at the end of the iteration. */ double Iterate(const arma::mat& centroids, arma::mat& newCentroids, @@ -670,6 +672,10 @@ double Iterate(const arma::mat& centroids, size_t DistanceCalculations() const { return distanceCalculations; } @endcode +Note that \c Iterate() does not need to return valid centroids if the cluster is +empty. This is because \c EmptyClusterPolicy will handle the empty centroid. +This behavior can be used to avoid small amounts of computation. + For examples, see the five aforementioned implementations of classes that satisfy the \c LloydStepType policy. From c5b7186ab8e8e8ec30ffec8c4c8ad572f684715f Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sun, 5 Jun 2016 20:29:53 +0000 Subject: [PATCH 21/34] Add --kill_empty_clusters and documentation for it. --- src/mlpack/methods/kmeans/kmeans_main.cpp | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/kmeans/kmeans_main.cpp b/src/mlpack/methods/kmeans/kmeans_main.cpp index 110774e17a..a197f77c2d 100644 --- a/src/mlpack/methods/kmeans/kmeans_main.cpp +++ b/src/mlpack/methods/kmeans/kmeans_main.cpp @@ -8,6 +8,7 @@ #include "kmeans.hpp" #include "allow_empty_clusters.hpp" +#include "kill_empty_clusters.hpp" #include "refined_start.hpp" #include "elkan_kmeans.hpp" #include "hamerly_kmeans.hpp" @@ -42,8 +43,19 @@ PROGRAM_INFO("K-Means Clustering", "This program performs K-Means clustering " "('hamerly'), the dual-tree k-means algorithm ('dualtree'), and the " "dual-tree k-means algorithm using the cover tree ('dualtree-covertree')." "\n\n" + "The behavior for when an empty cluster is encountered can be modified with" + " the --allow_empty_clusters (-e) option. When this option is specified " + "and there is a cluster owning no points at the end of an iteration, that " + "cluster's centroid will simply remain in its position from the previous " + "iteration. If the --kill_empty_clusters (-E) option is specified, then " + "when a cluster owns no points at the end of an iteration, the cluster " + "centroid is simply filled with DBL_MAX, killing it and effectively " + "reducing k for the rest of the computation. Note that the default option " + "when neither empty cluster option is specified can be time-consuming to " + "calculate; therefore, specifying -e or -E will often accelerate runtime." + "\n\n" "As of October 2014, the --overclustering option has been removed. If you " - "want this support back, let us know -- file a bug at " + "want this support back, let us know---file a bug at " "https://github.com/mlpack/mlpack/ or get in touch through another means."); // Required options. @@ -61,7 +73,9 @@ PARAM_STRING("centroid_file", "If specified, the centroids of each cluster will" " be written to the given file.", "C", ""); // k-means configuration options. -PARAM_FLAG("allow_empty_clusters", "Allow empty clusters to be created.", "e"); +PARAM_FLAG("allow_empty_clusters", "Allow empty clusters to be persist.", "e"); +PARAM_FLAG("kill_empty_clusters", "Remove empty clusters when they occur.", + "E"); PARAM_FLAG("labels_only", "Only output labels into output file.", "l"); PARAM_INT("max_iterations", "Maximum number of iterations before K-Means " "terminates.", "m", 1000); @@ -135,8 +149,14 @@ int main(int argc, char** argv) template void FindEmptyClusterPolicy(const InitialPartitionPolicy& ipp) { - if (CLI::HasParam("allow_empty_clusters")) + if (CLI::HasParam("allow_empty_clusters") && + CLI::HasParam("kill_empty_clusters")) + Log::Fatal << "Only one of --allow_empty_clusters (-e) or " + << "--kill_empty_clusters (-E) may be specified!" << endl; + else if (CLI::HasParam("allow_empty_clusters")) FindLloydStepType(ipp); + else if (CLI::HasParam("kill_empty_clusters")) + FindLloydStepType(ipp); else FindLloydStepType(ipp); } From bc3916a37e8e344df4af0669ca1787c3f77d4fab Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 8 Jun 2016 14:04:32 +0000 Subject: [PATCH 22/34] Fix return values. --- src/mlpack/methods/kmeans/allow_empty_clusters.hpp | 1 + src/mlpack/methods/kmeans/kill_empty_clusters.hpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mlpack/methods/kmeans/allow_empty_clusters.hpp b/src/mlpack/methods/kmeans/allow_empty_clusters.hpp index 7aacdf7943..5d6be13e25 100644 --- a/src/mlpack/methods/kmeans/allow_empty_clusters.hpp +++ b/src/mlpack/methods/kmeans/allow_empty_clusters.hpp @@ -52,6 +52,7 @@ class AllowEmptyClusters { // Take the last iteration's centroid. newCentroids.col(emptyCluster) = oldCentroids.col(emptyCluster); + return 0; // No points were changed. } //! Serialize the empty cluster policy (nothing to do). diff --git a/src/mlpack/methods/kmeans/kill_empty_clusters.hpp b/src/mlpack/methods/kmeans/kill_empty_clusters.hpp index 9b0038e351..d8aa97eda5 100644 --- a/src/mlpack/methods/kmeans/kill_empty_clusters.hpp +++ b/src/mlpack/methods/kmeans/kill_empty_clusters.hpp @@ -21,7 +21,7 @@ class KillEmptyClusters { public: //! Default constructor required by EmptyClusterPolicy policy. - AllowEmptyClusters() { } + KillEmptyClusters() { } /** * This function sets an empty cluster found during k-means to all DBL_MAX @@ -52,6 +52,7 @@ class KillEmptyClusters { // Kill the empty cluster. newCentroids.col(emptyCluster).fill(DBL_MAX); + return 0; // No points were changed. } //! Serialize the empty cluster policy (nothing to do). From c566aaf3549a3863acab39f874d9cbd492ea07b0 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 8 Jun 2016 14:04:53 +0000 Subject: [PATCH 23/34] Generalize metrics to arbitrary types. --- src/mlpack/core/metrics/ip_metric.hpp | 2 +- src/mlpack/core/metrics/ip_metric_impl.hpp | 10 ++-- src/mlpack/core/metrics/lmetric.hpp | 3 +- src/mlpack/core/metrics/lmetric_impl.hpp | 57 ++++++++++++++-------- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/mlpack/core/metrics/ip_metric.hpp b/src/mlpack/core/metrics/ip_metric.hpp index d8caf2f65d..b55ace8ce8 100644 --- a/src/mlpack/core/metrics/ip_metric.hpp +++ b/src/mlpack/core/metrics/ip_metric.hpp @@ -46,7 +46,7 @@ class IPMetric * @return Distance between the two points in kernel space. */ template - double Evaluate(const VecTypeA& a, const VecTypeB& b); + typename VecTypeA::elem_type Evaluate(const VecTypeA& a, const VecTypeB& b); //! Get the kernel. const KernelType& Kernel() const { return *kernel; } diff --git a/src/mlpack/core/metrics/ip_metric_impl.hpp b/src/mlpack/core/metrics/ip_metric_impl.hpp index 4297c94310..0d9cc1c4f1 100644 --- a/src/mlpack/core/metrics/ip_metric_impl.hpp +++ b/src/mlpack/core/metrics/ip_metric_impl.hpp @@ -44,8 +44,9 @@ IPMetric::~IPMetric() template template -inline double IPMetric::Evaluate(const Vec1Type& a, - const Vec2Type& b) +inline typename Vec1Type::elem_type IPMetric::Evaluate( + const Vec1Type& a, + const Vec2Type& b) { // This is the metric induced by the kernel function. // Maybe we can do better by caching some of this? @@ -71,8 +72,9 @@ void IPMetric::Serialize(Archive& ar, // the Euclidean distance. template<> template -inline double IPMetric::Evaluate(const Vec1Type& a, - const Vec2Type& b) +inline typename Vec1Type::elem_type IPMetric::Evaluate( + const Vec1Type& a, + const Vec2Type& b) { return metric::LMetric<2, true>::Evaluate(a, b); } diff --git a/src/mlpack/core/metrics/lmetric.hpp b/src/mlpack/core/metrics/lmetric.hpp index 5d599ab06d..240ae2ab76 100644 --- a/src/mlpack/core/metrics/lmetric.hpp +++ b/src/mlpack/core/metrics/lmetric.hpp @@ -75,7 +75,8 @@ class LMetric * @return Distance between vectors a and b. */ template - static double Evaluate(const VecTypeA& a, const VecTypeB& b); + static typename VecTypeA::elem_type Evaluate(const VecTypeA& a, + const VecTypeB& b); //! Serialize the metric (nothing to do). template diff --git a/src/mlpack/core/metrics/lmetric_impl.hpp b/src/mlpack/core/metrics/lmetric_impl.hpp index c87b7e7069..5e1c886a05 100644 --- a/src/mlpack/core/metrics/lmetric_impl.hpp +++ b/src/mlpack/core/metrics/lmetric_impl.hpp @@ -16,74 +16,89 @@ namespace metric { // Unspecialized implementation. This should almost never be used... template template -double LMetric::Evaluate(const VecTypeA& a, - const VecTypeB& b) +typename VecTypeA::elem_type LMetric::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - double sum = 0; + typename VecTypeA::elem_type sum = 0; for (size_t i = 0; i < a.n_elem; i++) - sum += pow(fabs(a[i] - b[i]), Power); + sum += std::pow(fabs(a[i] - b[i]), Power); if (!TakeRoot) // The compiler should optimize this correctly at compile-time. return sum; - return pow(sum, (1.0 / Power)); + return std::pow(sum, (1.0 / Power)); } // L1-metric specializations; the root doesn't matter. template<> template -double LMetric<1, true>::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric<1, true>::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - return accu(abs(a - b)); + return arma::accu(abs(a - b)); } template<> template -double LMetric<1, false>::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric<1, false>::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - return accu(abs(a - b)); + return arma::accu(abs(a - b)); } // L2-metric specializations. template<> template -double LMetric<2, true>::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric<2, true>::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - return sqrt(accu(square(a - b))); + return sqrt(arma::accu(square(a - b))); } template<> template -double LMetric<2, false>::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric<2, false>::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - return accu(square(a - b)); + return accu(arma::square(a - b)); } // L3-metric specialization (not very likely to be used, but just in case). template<> template -double LMetric<3, true>::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric<3, true>::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - double sum = 0; + typename VecTypeA::elem_type sum = 0; for (size_t i = 0; i < a.n_elem; i++) - sum += pow(fabs(a[i] - b[i]), 3.0); + sum += std::pow(fabs(a[i] - b[i]), 3.0); - return pow(accu(pow(abs(a - b), 3.0)), 1.0 / 3.0); + return std::pow(arma::accu(arma::pow(arma::abs(a - b), 3.0)), 1.0 / 3.0); } template<> template -double LMetric<3, false>::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric<3, false>::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - return accu(pow(abs(a - b), 3.0)); + return arma::accu(arma::pow(arma::abs(a - b), 3.0)); } // L-infinity (Chebyshev distance) specialization template<> template -double LMetric::Evaluate(const VecTypeA& a, const VecTypeB& b) +typename VecTypeA::elem_type LMetric::Evaluate( + const VecTypeA& a, + const VecTypeB& b) { - return arma::as_scalar(max(abs(a - b))); + return arma::as_scalar(arma::max(arma::abs(a - b))); } } // namespace metric From bd65c877e6b62bdcbc1f1fb46100587e54e74cec Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 8 Jun 2016 14:33:12 -0400 Subject: [PATCH 24/34] Switch secondHashTable to vector>. This should provide a good amount of speedup, and also save RAM. --- src/mlpack/methods/lsh/lsh_search.hpp | 8 +- src/mlpack/methods/lsh/lsh_search_impl.hpp | 144 +++++++++++++++------ src/mlpack/tests/serialization_test.cpp | 12 +- 3 files changed, 116 insertions(+), 48 deletions(-) diff --git a/src/mlpack/methods/lsh/lsh_search.hpp b/src/mlpack/methods/lsh/lsh_search.hpp index a755a9981f..7cbe1e6a28 100644 --- a/src/mlpack/methods/lsh/lsh_search.hpp +++ b/src/mlpack/methods/lsh/lsh_search.hpp @@ -197,7 +197,8 @@ class LSHSearch size_t BucketSize() const { return bucketSize; } //! Get the second hash table. - const arma::Mat& SecondHashTable() const { return secondHashTable; } + const std::vector>& SecondHashTable() const + { return secondHashTable; } //! Get the projection tables. const arma::cube& Projections() { return projections; } @@ -314,8 +315,9 @@ class LSHSearch //! The bucket size of the second hash. size_t bucketSize; - //! The final hash table; should be (< secondHashSize) x bucketSize. - arma::Mat secondHashTable; + //! The final hash table; should be (< secondHashSize) vectors each with + //! (<= bucketSize) elements. + std::vector> secondHashTable; //! The number of elements present in each hash bucket; should be //! secondHashSize. diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index a141aa2c04..bd022c8234 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -131,21 +131,6 @@ void LSHSearch::Train(const arma::mat& referenceSet, secondHashWeights = arma::floor(arma::randu(numProj) * (double) secondHashSize); - // The 'secondHashTable' is initially an empty matrix of size - // ('secondHashSize' x 'bucketSize'). But by only filling the buckets as - // points land in them allows us to shrink the size of the 'secondHashTable' - // at the end of the hashing. - - // Fill the second hash table n = referenceSet.n_cols. This is because no - // point has index 'n' so the presence of this in the bucket denotes that - // there are no more points in this bucket. - secondHashTable.set_size(secondHashSize, bucketSize); - secondHashTable.fill(referenceSet.n_cols); - - // Keep track of the size of each bucket in the hash. At the end of hashing - // most buckets will be empty. - bucketContentSize.zeros(secondHashSize); - // Instead of putting the points in the row corresponding to the bucket, we // chose the next empty row and keep track of the row in which the bucket // lies. This allows us to stack together and slice out the empty buckets at @@ -219,10 +204,13 @@ void LSHSearch::Train(const arma::mat& referenceSet, for (size_t i = 0; i < secondHashVectors.n_elem; ++i) secondHashBinCounts[secondHashVectors[i]]++; + // Enforce the maximum bucket size. + secondHashBinCounts.transform([bucketSize](size_t val) + { return std::min(val, bucketSize); }); + const size_t numRowsInTable = arma::accu(secondHashBinCounts > 0); - const size_t maxBucketSize = std::min(arma::max(secondHashBinCounts), - bucketSize); - secondHashTable.resize(numRowsInTable, maxBucketSize); + bucketContentSize.zeros(numRowsInTable); + secondHashTable.resize(numRowsInTable); // Next we must assign each point in each table to the right second hash // table. @@ -239,27 +227,26 @@ void LSHSearch::Train(const arma::mat& referenceSet, // If this is currently an empty bucket, start a new row keep track of // which row corresponds to the bucket. - if (bucketContentSize[hashInd] == 0) + const size_t maxSize = secondHashBinCounts[hashInd]; + if (bucketRowInHashTable[hashInd] == secondHashSize) { - // Start a new row for hash. bucketRowInHashTable[hashInd] = currentRow; - bucketContentSize[hashInd] = 1; - secondHashTable(currentRow, 0) = j; + secondHashTable[currentRow].set_size(maxSize); currentRow++; } - else if (bucketContentSize[hashInd] < maxBucketSize) - { - // If bucket is already present in the 'secondHashTable', find the - // corresponding row and insert the point ID in this row unless the - // bucket is full (in which case we are not inside this else if). - secondHashTable(bucketRowInHashTable[hashInd], - bucketContentSize[hashInd]++) = j; - } + + // If this vector in the hash table is not full, add the point. + const size_t index = bucketRowInHashTable[hashInd]; + if (bucketContentSize[index] < maxSize) + secondHashTable[index](bucketContentSize[index]++) = j; + } // Loop over all points in the reference set. } // Loop over tables. - Log::Info << "Final hash table size: " << numRowsInTable << " x " - << maxBucketSize << "." << std::endl; + Log::Info << "Final hash table size: " << numRowsInTable << " rows, with a " + << "maximum length of " << arma::max(secondHashBinCounts) << ", " + << "totaling " << arma::accu(secondHashBinCounts) << " elements." + << std::endl; } template @@ -388,17 +375,14 @@ void LSHSearch::ReturnIndicesFromTable( for (size_t i = 0; i < hashVec.n_elem; i++) // For all tables. { - size_t hashInd = (size_t) hashVec[i]; + const size_t hashInd = (size_t) hashVec[i]; + const size_t tableRow = bucketRowInHashTable[hashInd]; - if (bucketContentSize[hashInd] > 0) + if ((tableRow != secondHashSize) && (bucketContentSize[tableRow] > 0)) { // Pick the indices in the bucket corresponding to 'hashInd'. - size_t tableRow = bucketRowInHashTable[hashInd]; - assert(tableRow < secondHashSize); - assert(tableRow < secondHashTable.n_rows); - - for (size_t j = 0; j < bucketContentSize[hashInd]; j++) - refPointsConsidered[secondHashTable(tableRow, j)]++; + for (size_t j = 0; j < bucketContentSize[tableRow]; j++) + refPointsConsidered[secondHashTable[tableRow](j)]++; } } @@ -540,7 +524,7 @@ void LSHSearch::Serialize(Archive& ar, if (Archive::is_loading::value) projections.reset(); - // Backward compatibility: older version of LSHSearch stored the projection + // Backward compatibility: older versions of LSHSearch stored the projection // tables in a std::vector. if (version == 0) { @@ -561,8 +545,82 @@ void LSHSearch::Serialize(Archive& ar, ar & CreateNVP(secondHashSize, "secondHashSize"); ar & CreateNVP(secondHashWeights, "secondHashWeights"); ar & CreateNVP(bucketSize, "bucketSize"); - ar & CreateNVP(secondHashTable, "secondHashTable"); - ar & CreateNVP(bucketContentSize, "bucketContentSize"); + // needs specific handling for new version + + // Backward compatibility: in older versions of LSHSearch, the secondHashTable + // was stored as an arma::Mat. So we need to properly load that, then + // prune it down to size. + if (version == 0) + { + arma::Mat tmpSecondHashTable; + ar & CreateNVP(tmpSecondHashTable, "secondHashTable"); + + secondHashTable.resize(tmpSecondHashTable.n_cols); + for (size_t i = 0; i < tmpSecondHashTable.n_cols; ++i) + { + // Find length of each column. We know we are at the end of the list when + // the value referenceSet->n_cols is seen. + size_t len = 0; + for ( ; len < tmpSecondHashTable.n_rows; ++len) + if (tmpSecondHashTable(len, i) == referenceSet->n_cols) + break; + + // Set the size of the new column correctly. + secondHashTable[i].set_size(len); + for (size_t j = 0; j < len; ++j) + secondHashTable[i](j) = tmpSecondHashTable(j, i); + } + } + else + { + size_t tables; + if (Archive::is_saving::value) + tables = secondHashTable.size(); + ar & CreateNVP(tables, "numSecondHashTables"); + + // Set size of second hash table if needed. + if (Archive::is_loading::value) + { + secondHashTable.clear(); + secondHashTable.resize(tables); + } + + for (size_t i = 0; i < secondHashTable.size(); ++i) + { + std::ostringstream oss; + oss << "secondHashTable" << i; + ar & CreateNVP(secondHashTable[i], oss.str()); + } + } + + // Backward compatibility: old versions of LSHSearch held bucketContentSize + // for all possible buckets (of size secondHashSize), but now we hold a + // compressed representation. + if (version == 0) + { + // The vector was stored in the old uncompressed form. So we need to shrink + // it. + arma::Col tmpBucketContentSize; + ar & CreateNVP(tmpBucketContentSize, "bucketContentSize"); + + // Compress into a smaller vector by just dropping all of the zeros. + bucketContentSize.set_size(secondHashTable.size()); + size_t loc = 0; + for (size_t i = 0; i < tmpBucketContentSize.n_elem; ++i) + { + if (tmpBucketContentSize[i] > 0) + bucketContentSize[loc++] = tmpBucketContentSize[i]; + + // Terminate early, if we can. + if (loc == bucketContentSize.n_elem) + break; + } + } + else + { + ar & CreateNVP(bucketContentSize, "bucketContentSize"); + } + ar & CreateNVP(bucketRowInHashTable, "bucketRowInHashTable"); ar & CreateNVP(distanceEvaluations, "distanceEvaluations"); } diff --git a/src/mlpack/tests/serialization_test.cpp b/src/mlpack/tests/serialization_test.cpp index 7b6beec96e..5dbb9aaf40 100644 --- a/src/mlpack/tests/serialization_test.cpp +++ b/src/mlpack/tests/serialization_test.cpp @@ -1225,8 +1225,16 @@ BOOST_AUTO_TEST_CASE(LSHTest) BOOST_REQUIRE_EQUAL(lsh.BucketSize(), textLsh.BucketSize()); BOOST_REQUIRE_EQUAL(lsh.BucketSize(), binaryLsh.BucketSize()); - CheckMatrices(lsh.SecondHashTable(), xmlLsh.SecondHashTable(), - textLsh.SecondHashTable(), binaryLsh.SecondHashTable()); + BOOST_REQUIRE_EQUAL(lsh.SecondHashTable().size(), + xmlLsh.SecondHashTable().size()); + BOOST_REQUIRE_EQUAL(lsh.SecondHashTable().size(), + textLsh.SecondHashTable().size()); + BOOST_REQUIRE_EQUAL(lsh.SecondHashTable().size(), + binaryLsh.SecondHashTable().size()); + + for (size_t i = 0; i < lsh.SecondHashTable().size(); ++i) + CheckMatrices(lsh.SecondHashTable()[i], xmlLsh.SecondHashTable()[i], + textLsh.SecondHashTable()[i], binaryLsh.SecondHashTable()[i]); } // Make sure serialization works for the decision stump. From e8e2ff17da5978cacf3c9a45d4aa572a4bf008e5 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 8 Jun 2016 18:39:11 +0000 Subject: [PATCH 25/34] Fix type in test because LMetric supports arbitrary types now. --- src/mlpack/tests/metric_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/tests/metric_test.cpp b/src/mlpack/tests/metric_test.cpp index 6430d032fe..7eff0bbe90 100644 --- a/src/mlpack/tests/metric_test.cpp +++ b/src/mlpack/tests/metric_test.cpp @@ -44,10 +44,10 @@ BOOST_AUTO_TEST_CASE(L2MetricTest) arma::vec b1(5); b1.randn(); - arma::Col a2(5); + arma::vec a2(5); a2 << 1 << 2 << 1 << 0 << 5; - arma::Col b2(5); + arma::vec b2(5); b2 << 2 << 5 << 2 << 0 << 1; EuclideanDistance lMetric; From 596a3e3260631a080dbc7026b7b043e94b3bd580 Mon Sep 17 00:00:00 2001 From: Keon Kim Date: Thu, 9 Jun 2016 04:52:00 +0900 Subject: [PATCH 26/34] delete unused string_util --- .../binary_space_tree_impl.hpp | 1 - .../core/tree/cover_tree/cover_tree_impl.hpp | 1 - .../rectangle_tree/rectangle_tree_impl.hpp | 1 - src/mlpack/core/util/CMakeLists.txt | 2 - src/mlpack/core/util/prefixedoutstream.hpp | 1 - src/mlpack/core/util/string_util.cpp | 47 ------------------- src/mlpack/core/util/string_util.hpp | 23 --------- 7 files changed, 76 deletions(-) delete mode 100644 src/mlpack/core/util/string_util.cpp delete mode 100644 src/mlpack/core/util/string_util.hpp diff --git a/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp b/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp index ee2f20b643..227129b0ec 100644 --- a/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp +++ b/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp @@ -11,7 +11,6 @@ #include #include -#include #include namespace mlpack { diff --git a/src/mlpack/core/tree/cover_tree/cover_tree_impl.hpp b/src/mlpack/core/tree/cover_tree/cover_tree_impl.hpp index cc1049dd04..3d3b0127a7 100644 --- a/src/mlpack/core/tree/cover_tree/cover_tree_impl.hpp +++ b/src/mlpack/core/tree/cover_tree/cover_tree_impl.hpp @@ -10,7 +10,6 @@ // In case it hasn't already been included. #include "cover_tree.hpp" -#include #include namespace mlpack { diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp index d993a45090..5e354438ee 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -12,7 +12,6 @@ #include #include -#include namespace mlpack { namespace tree { diff --git a/src/mlpack/core/util/CMakeLists.txt b/src/mlpack/core/util/CMakeLists.txt index 39b4010102..e75b2cb18a 100644 --- a/src/mlpack/core/util/CMakeLists.txt +++ b/src/mlpack/core/util/CMakeLists.txt @@ -20,8 +20,6 @@ set(SOURCES prefixedoutstream.cpp prefixedoutstream_impl.hpp sfinae_utility.hpp - string_util.hpp - string_util.cpp timers.hpp timers.cpp version.hpp diff --git a/src/mlpack/core/util/prefixedoutstream.hpp b/src/mlpack/core/util/prefixedoutstream.hpp index 13ead9ee5c..e2370caa57 100644 --- a/src/mlpack/core/util/prefixedoutstream.hpp +++ b/src/mlpack/core/util/prefixedoutstream.hpp @@ -19,7 +19,6 @@ #include #include -#include namespace mlpack { namespace util { diff --git a/src/mlpack/core/util/string_util.cpp b/src/mlpack/core/util/string_util.cpp deleted file mode 100644 index 1dc54b27b1..0000000000 --- a/src/mlpack/core/util/string_util.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file string_util.cpp - * @author Trironk Kiatkungwanglai - * @author Ryan Birmingham - * - * Defines methods useful for formatting output. - */ -#include "string_util.hpp" - -using namespace mlpack; -using namespace mlpack::util; -using namespace std; - -//! A utility function that replaces all all newlines with a number of spaces -//! depending on the indentation level. -string mlpack::util::Indent(string input, const size_t howManyTabs) -{ - // For each declared... - string standardTab = " "; - string bigTab = ""; - for (size_t ind = 0; ind < howManyTabs; ind++) - { - // Increase amount tabbed on later lines. - bigTab += standardTab; - - // Add indentation to first line. - input.insert(0, 1, ' '); - input.insert(0, 1, ' '); - } - - // Create the character sequence to replace all newline characters. - std::string tabbedNewline("\n" + bigTab); - - // Replace all newline characters with the precomputed character sequence. - size_t startPos = 0; - while ((startPos = input.find("\n", startPos)) != string::npos) - { - // Don't replace the last newline. - if (startPos == input.length() - 1) - break; - - input.replace(startPos, 1, tabbedNewline); - startPos += tabbedNewline.length(); - } - - return input; -} diff --git a/src/mlpack/core/util/string_util.hpp b/src/mlpack/core/util/string_util.hpp deleted file mode 100644 index 7fa9080711..0000000000 --- a/src/mlpack/core/util/string_util.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file string_util.hpp - * @author Trironk Kiatkungwanglai - * @author Ryan Birmingham - * - * Declares methods that are useful for writing formatting output. - */ -#ifndef MLPACK_CORE_STRING_UTIL_HPP -#define MLPACK_CORE_STRING_UTIL_HPP - -#include - -namespace mlpack { -namespace util { - -//! A utility function that replaces all all newlines with a number of spaces -//! depending on the indentation level. -std::string Indent(std::string input, const size_t howManyTabs = 1); - -} // namespace util -} // namespace mlpack - -#endif From 74cdbc8dce10b4d2dfb259cd2b6cecdb1af49c1b Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 8 Jun 2016 17:42:43 -0400 Subject: [PATCH 27/34] Fix serialization. --- src/mlpack/methods/lsh/lsh_search_impl.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index bd022c8234..ac65a86a6c 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -555,11 +555,16 @@ void LSHSearch::Serialize(Archive& ar, arma::Mat tmpSecondHashTable; ar & CreateNVP(tmpSecondHashTable, "secondHashTable"); + // The old secondHashTable was stored in row-major format, so we transpose + // it. + tmpSecondHashTable = tmpSecondHashTable.t(); + secondHashTable.resize(tmpSecondHashTable.n_cols); for (size_t i = 0; i < tmpSecondHashTable.n_cols; ++i) { // Find length of each column. We know we are at the end of the list when // the value referenceSet->n_cols is seen. + size_t len = 0; for ( ; len < tmpSecondHashTable.n_rows; ++len) if (tmpSecondHashTable(len, i) == referenceSet->n_cols) @@ -599,29 +604,24 @@ void LSHSearch::Serialize(Archive& ar, if (version == 0) { // The vector was stored in the old uncompressed form. So we need to shrink - // it. + // it. But we can't do that until we have bucketRowInHashTable, so we also + // have to load that. arma::Col tmpBucketContentSize; ar & CreateNVP(tmpBucketContentSize, "bucketContentSize"); + ar & CreateNVP(bucketRowInHashTable, "bucketRowInHashTable"); // Compress into a smaller vector by just dropping all of the zeros. bucketContentSize.set_size(secondHashTable.size()); - size_t loc = 0; for (size_t i = 0; i < tmpBucketContentSize.n_elem; ++i) - { if (tmpBucketContentSize[i] > 0) - bucketContentSize[loc++] = tmpBucketContentSize[i]; - - // Terminate early, if we can. - if (loc == bucketContentSize.n_elem) - break; - } + bucketContentSize[bucketRowInHashTable[i]] = tmpBucketContentSize[i]; } else { ar & CreateNVP(bucketContentSize, "bucketContentSize"); + ar & CreateNVP(bucketRowInHashTable, "bucketRowInHashTable"); } - ar & CreateNVP(bucketRowInHashTable, "bucketRowInHashTable"); ar & CreateNVP(distanceEvaluations, "distanceEvaluations"); } From 29d43319f1a3ace534a95e966be9e903f06b07e1 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 9 Jun 2016 09:56:32 -0400 Subject: [PATCH 28/34] Remove unused Score() functions. --- src/mlpack/methods/emst/dtb_rules.hpp | 28 ------------- src/mlpack/methods/emst/dtb_rules_impl.hpp | 46 ---------------------- 2 files changed, 74 deletions(-) diff --git a/src/mlpack/methods/emst/dtb_rules.hpp b/src/mlpack/methods/emst/dtb_rules.hpp index 7f275cfbcf..05fd058a00 100644 --- a/src/mlpack/methods/emst/dtb_rules.hpp +++ b/src/mlpack/methods/emst/dtb_rules.hpp @@ -37,20 +37,6 @@ class DTBRules */ double Score(const size_t queryIndex, TreeType& referenceNode); - /** - * Get the score for recursion order, passing the base case result (in the - * situation where it may be needed to calculate the recursion order). A low - * score indicates priority for recursion, while DBL_MAX indicates that the - * node should not be recursed into at all (it should be pruned). - * - * @param queryIndex Index of query point. - * @param referenceNode Candidate node to be recursed into. - * @param baseCaseResult Result of BaseCase(queryIndex, referenceNode). - */ - double Score(const size_t queryIndex, - TreeType& referenceNode, - const double baseCaseResult); - /** * Re-evaluate the score for recursion order. A low score indicates priority * for recursion, while DBL_MAX indicates that the node should not be recursed @@ -76,20 +62,6 @@ class DTBRules */ double Score(TreeType& queryNode, TreeType& referenceNode); - /** - * Get the score for recursion order, passing the base case result (in the - * situation where it may be needed to calculate the recursion order). A low - * score indicates priority for recursion, while DBL_MAX indicates that the - * node should not be recursed into at all (it should be pruned). - * - * @param queryNode Candidate query node to recurse into. - * @param referenceNode Candidate reference node to recurse into. - * @param baseCaseResult Result of BaseCase(queryIndex, referenceNode). - */ - double Score(TreeType& queryNode, - TreeType& referenceNode, - const double baseCaseResult); - /** * Re-evaluate the score for recursion order. A low score indicates priority * for recursion, while DBL_MAX indicates that the node should not be recursed diff --git a/src/mlpack/methods/emst/dtb_rules_impl.hpp b/src/mlpack/methods/emst/dtb_rules_impl.hpp index 7fd410fc18..f60513b71f 100644 --- a/src/mlpack/methods/emst/dtb_rules_impl.hpp +++ b/src/mlpack/methods/emst/dtb_rules_impl.hpp @@ -92,31 +92,6 @@ double DTBRules::Score(const size_t queryIndex, ? DBL_MAX : distance; } -template -double DTBRules::Score(const size_t queryIndex, - TreeType& referenceNode, - const double baseCaseResult) -{ - // I don't really understand the last argument here - // It just gets passed in the distance call, otherwise this function - // is the same as the one above. - size_t queryComponentIndex = connections.Find(queryIndex); - - // If the query belongs to the same component as all of the references, - // then prune. - if (queryComponentIndex == referenceNode.Stat().ComponentMembership()) - return DBL_MAX; - - const arma::vec queryPoint = dataSet.unsafe_col(queryIndex); - const double distance = referenceNode.MinDistance(queryPoint, - baseCaseResult); - - // If all the points in the reference node are farther than the candidate - // nearest neighbor for the query's component, we prune. - return (neighborsDistances[queryComponentIndex] < distance) ? DBL_MAX : - distance; -} - template double DTBRules::Rescore(const size_t queryIndex, TreeType& /* referenceNode */, @@ -148,27 +123,6 @@ double DTBRules::Score(TreeType& queryNode, return (bound < distance) ? DBL_MAX : distance; } -template -double DTBRules::Score(TreeType& queryNode, - TreeType& referenceNode, - const double baseCaseResult) -{ - // If all the queries belong to the same component as all the references - // then we prune. - if ((queryNode.Stat().ComponentMembership() >= 0) && - (queryNode.Stat().ComponentMembership() == - referenceNode.Stat().ComponentMembership())) - return DBL_MAX; - - ++scores; - const double distance = queryNode.MinDistance(referenceNode, baseCaseResult); - const double bound = CalculateBound(queryNode); - - // If all the points in the reference node are farther than the candidate - // nearest neighbor for all queries in the node, we prune. - return (bound < distance) ? DBL_MAX : distance; -} - template double DTBRules::Rescore(TreeType& queryNode, TreeType& /* referenceNode */, From a93d0221ad4fbc1ede677777d6af7e10748a17bc Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 10 Jun 2016 07:00:33 -0700 Subject: [PATCH 29/34] Minor style changes for R tree variants. --- .../tree/rectangle_tree/r_star_tree_split.hpp | 12 ++--- .../rectangle_tree/r_star_tree_split_impl.hpp | 25 ++------- .../core/tree/rectangle_tree/r_tree_split.hpp | 18 +++---- .../tree/rectangle_tree/r_tree_split_impl.hpp | 51 ++++++++----------- .../tree/rectangle_tree/rectangle_tree.hpp | 2 +- .../rectangle_tree/rectangle_tree_impl.hpp | 5 +- .../core/tree/rectangle_tree/x_tree_split.hpp | 11 ++-- .../tree/rectangle_tree/x_tree_split_impl.hpp | 26 +++++----- 8 files changed, 62 insertions(+), 88 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp index d17abf64f4..e63269021f 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp @@ -22,27 +22,27 @@ template class RStarTreeSplit { public: - //! Default constructor - RStarTreeSplit(); + //! Default constructor. + RStarTreeSplit() { } //! Construct this with the specified node. - RStarTreeSplit(const TreeType *node); + RStarTreeSplit(const TreeType* /* node */) { } //! Create a copy of the other.split. - RStarTreeSplit(const TreeType &other); + RStarTreeSplit(const TreeType& /* other */) { } /** * Split a leaf node using the algorithm described in "The R*-tree: An * Efficient and Robust Access method for Points and Rectangles." If * necessary, this split will propagate upwards through the tree. */ - void SplitLeafNode(TreeType *tree,std::vector& relevels); + void SplitLeafNode(TreeType* tree, std::vector& relevels); /** * Split a non-leaf node using the "default" algorithm. If this is a root * node, the tree increases in depth. */ - bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + bool SplitNonLeafNode(TreeType* tree, std::vector& relevels); private: /** diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp index 44dbf95fe7..cb02190617 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp @@ -15,25 +15,6 @@ namespace mlpack { namespace tree { -template -RStarTreeSplit::RStarTreeSplit() -{ - -} - -template -RStarTreeSplit::RStarTreeSplit(const TreeType *) -{ - -} - -template -RStarTreeSplit::RStarTreeSplit(const TreeType &) -{ - -} - - /** * We call GetPointSeeds to get the two points which will be the initial points * in the new nodes We then call AssignPointDestNode to assign the remaining @@ -288,7 +269,8 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& r * higher up the tree because they were already updated if necessary. */ template -bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) +bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, + std::vector& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -691,7 +673,8 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector * numberOfChildren. */ template -void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) +void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, + TreeType* srcNode) { destTree->Bound() |= srcNode->Bound(); destTree->Children()[destTree->NumChildren()++] = srcNode; diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp index a77308a9a7..389b2d2b47 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp @@ -22,37 +22,37 @@ template class RTreeSplit { public: - //! Default constructor - RTreeSplit(); + //! Default constructor. + RTreeSplit() { } //! Construct this with the specified node. - RTreeSplit(const TreeType *node); + RTreeSplit(const TreeType* /* node */) { } - //! Create a copy of the other.split. - RTreeSplit(const TreeType &other); + //! Create a copy of the other split. + RTreeSplit(const TreeType& /* other */) { } /** * Split a leaf node using the "default" algorithm. If necessary, this split * will propagate upwards through the tree. */ - void SplitLeafNode(TreeType *tree,std::vector& relevels); + void SplitLeafNode(TreeType* tree, std::vector& relevels); /** * Split a non-leaf node using the "default" algorithm. If this is a root * node, the tree increases in depth. */ - bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + bool SplitNonLeafNode(TreeType* tree, std::vector& relevels); private: /** * Get the seeds for splitting a leaf node. */ - static void GetPointSeeds(const TreeType *tree,int& i, int& j); + static void GetPointSeeds(const TreeType* tree, int& i, int& j); /** * Get the seeds for splitting a non-leaf node. */ - static void GetBoundSeeds(const TreeType *tree,int& i, int& j); + static void GetBoundSeeds(const TreeType* tree, int& i, int& j); /** * Assign points to the two new nodes. diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp index 69bf041d5a..26e4120914 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp @@ -14,24 +14,6 @@ namespace mlpack { namespace tree { -template -RTreeSplit::RTreeSplit() -{ - -} - -template -RTreeSplit::RTreeSplit(const TreeType *) -{ - -} - -template -RTreeSplit::RTreeSplit(const TreeType &) -{ - -} - /** * We call GetPointSeeds to get the two points which will be the initial points * in the new nodes We then call AssignPointDestNode to assign the remaining @@ -39,7 +21,8 @@ RTreeSplit::RTreeSplit(const TreeType &) * new nodes into the tree, spliting the parent if necessary. */ template -void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) +void RTreeSplit::SplitLeafNode(TreeType* tree, + std::vector& relevels) { // If we are splitting the root node, we need will do things differently so // that the constructor and other methods don't confuse the end user by giving @@ -103,7 +86,8 @@ void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev * higher up the tree because they were already updated if necessary. */ template -bool RTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) +bool RTreeSplit::SplitNonLeafNode(TreeType* tree, + std::vector& relevels) { // If we are splitting the root node, we need will do things differently so // that the constructor and other methods don't confuse the end user by giving @@ -175,7 +159,9 @@ bool RTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re * The indices of these points will be stored in iRet and jRet. */ template -void RTreeSplit::GetPointSeeds(const TreeType *tree,int& iRet, int& jRet) +void RTreeSplit::GetPointSeeds(const TreeType* tree, + int& iRet, + int& jRet) { // Here we want to find the pair of points that it is worst to place in the // same node. Because we are just using points, we will simply choose the two @@ -203,7 +189,9 @@ void RTreeSplit::GetPointSeeds(const TreeType *tree,int& iRet, int& jR * indices of the bounds will be stored in iRet and jRet. */ template -void RTreeSplit::GetBoundSeeds(const TreeType *tree,int& iRet, int& jRet) +void RTreeSplit::GetBoundSeeds(const TreeType* tree, + int& iRet, + int& jRet) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -235,10 +223,10 @@ void RTreeSplit::GetBoundSeeds(const TreeType *tree,int& iRet, int& jR template void RTreeSplit::AssignPointDestNode(TreeType* oldTree, - TreeType* treeOne, - TreeType* treeTwo, - const int intI, - const int intJ) + TreeType* treeOne, + TreeType* treeTwo, + const int intI, + const int intJ) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -376,10 +364,10 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, template void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, - TreeType* treeOne, - TreeType* treeTwo, - const int intI, - const int intJ) + TreeType* treeOne, + TreeType* treeTwo, + const int intI, + const int intJ) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -540,7 +528,8 @@ void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, * numberOfChildren. */ template -void RTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) +void RTreeSplit::InsertNodeIntoTree(TreeType* destTree, + TreeType* srcNode) { destTree->Bound() |= srcNode->Bound(); destTree->Children()[destTree->NumChildren()++] = srcNode; diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 8432f44233..490f33c9df 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -154,7 +154,7 @@ class RectangleTree * @param numMaxChildren The max number of child nodes (used in x-trees). */ explicit RectangleTree(RectangleTree* parentNode, - const size_t numMaxChildren = 0); + const size_t numMaxChildren = 0); /** * Create a rectangle tree by copying the other tree. Be careful! This can diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp index 5e354438ee..dc6d997815 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -104,8 +104,9 @@ template:: RectangleTree( RectangleTree* - parentNode,const size_t numMaxChildren) : - maxNumChildren(numMaxChildren > 0 ? numMaxChildren : parentNode->MaxNumChildren()), + parentNode, const size_t numMaxChildren) : + maxNumChildren(numMaxChildren > 0 ? numMaxChildren : + parentNode->MaxNumChildren()), minNumChildren(parentNode->MinNumChildren()), numChildren(0), children(maxNumChildren + 1), diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp index 7b120a9086..7b4f868af6 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp @@ -36,23 +36,23 @@ class XTreeSplit XTreeSplit(); //! Construct this with the specified node. - XTreeSplit(const TreeType *node); + XTreeSplit(const TreeType* node); //! Create a copy of the other.split. - XTreeSplit(const TreeType &other); + XTreeSplit(const TreeType& other); /** * Split a leaf node using the algorithm described in "The R*-tree: An * Efficient and Robust Access method for Points and Rectangles." If * necessary, this split will propagate upwards through the tree. */ - void SplitLeafNode(TreeType *tree,std::vector& relevels); + void SplitLeafNode(TreeType* tree, std::vector& relevels); /** * Split a non-leaf node using the "default" algorithm. If this is a root * node, the tree increases in depth. */ - bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + bool SplitNonLeafNode(TreeType* tree, std::vector& relevels); /** * The X tree requires that the tree records it's "split history". To make @@ -98,7 +98,7 @@ class XTreeSplit * Comparator for sorting with sortStruct. */ template - static bool structComp(const sortStruct& s1, + static bool structComp(const sortStruct& s1, const sortStruct& s2) { return s1.d < s2.d; @@ -119,7 +119,6 @@ class XTreeSplit //! Modify the split history of the node assosiated with this object. SplitHistoryStruct& SplitHistory() { return splitHistory; } - /** * Serialize the split. */ diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp index 0b43454200..d3e48edcae 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp @@ -23,10 +23,10 @@ XTreeSplit::XTreeSplit() : } template -XTreeSplit::XTreeSplit(const TreeType *node) : - normalNodeMaxNumChildren(node->Parent() ? - node->Parent()->Split().NormalNodeMaxNumChildren() : - node->MaxNumChildren()), +XTreeSplit::XTreeSplit(const TreeType*node) : + normalNodeMaxNumChildren(node->Parent() ? + node->Parent()->Split().NormalNodeMaxNumChildren() : + node->MaxNumChildren()), splitHistory(node->Bound().Dim()) { @@ -40,7 +40,6 @@ XTreeSplit::XTreeSplit(const TreeType &other) : } - /** * We call GetPointSeeds to get the two points which will be the initial points * in the new nodes We then call AssignPointDestNode to assign the remaining @@ -48,7 +47,8 @@ XTreeSplit::XTreeSplit(const TreeType &other) : * new nodes into the tree, spliting the parent if necessary. */ template -void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) +void XTreeSplit::SplitLeafNode(TreeType* tree, + std::vector& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -245,8 +245,8 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev std::sort(sorted.begin(), sorted.end(), structComp); - TreeType* treeOne = new TreeType(tree->Parent(),NormalNodeMaxNumChildren()); - TreeType* treeTwo = new TreeType(tree->Parent(),NormalNodeMaxNumChildren()); + TreeType* treeOne = new TreeType(tree->Parent(), NormalNodeMaxNumChildren()); + TreeType* treeTwo = new TreeType(tree->Parent(), NormalNodeMaxNumChildren()); // The leaf nodes should never have any overlap introduced by the above method // since a split axis is chosen and then points are assigned based on their @@ -319,7 +319,8 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev * higher up the tree because they were already updated if necessary. */ template -bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) +bool XTreeSplit::SplitNonLeafNode(TreeType* tree, + std::vector& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -684,8 +685,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re std::sort(sorted.begin(), sorted.end(), structComp); - TreeType* treeOne = new TreeType(tree->Parent(),tree->MaxNumChildren()); - TreeType* treeTwo = new TreeType(tree->Parent(),tree->MaxNumChildren()); + TreeType* treeOne = new TreeType(tree->Parent(), tree->MaxNumChildren()); + TreeType* treeTwo = new TreeType(tree->Parent(), tree->MaxNumChildren()); // Now as per the X-tree paper, we ensure that this split was good enough. bool useMinOverlapSplit = false; @@ -770,7 +771,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re (tree->Parent()->NumChildren() == 1)) { // We make the root a supernode instead. - tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() + NormalNodeMaxNumChildren(); + tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() + + NormalNodeMaxNumChildren(); tree->Parent()->Children().resize(tree->Parent()->MaxNumChildren() + 1); tree->Parent()->NumChildren() = tree->NumChildren(); for (size_t i = 0; i < tree->NumChildren(); i++) From 8d8ede7911b37f83d4873b38dda14e887cdad8aa Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 10 Jun 2016 07:00:59 -0700 Subject: [PATCH 30/34] Remove extra line. --- src/mlpack/tests/rectangle_tree_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 9e5b2036bc..f9278c554a 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -614,7 +614,6 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) } } - // Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low // to allow us to test by hand without adding hundreds of points. BOOST_AUTO_TEST_CASE(RTreeSplitTest) From ca391aa35391a2b056bb362f1653218797a4674f Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Fri, 10 Jun 2016 07:10:39 -0700 Subject: [PATCH 31/34] Minor style issues, use BOOST_REQUIRE_* instead of BOOST_REQUIRE. No functionality changes, just pedantry. --- src/mlpack/tests/lsh_test.cpp | 142 +++++++++++++++------------------- 1 file changed, 64 insertions(+), 78 deletions(-) diff --git a/src/mlpack/tests/lsh_test.cpp b/src/mlpack/tests/lsh_test.cpp index 85881b78ce..da489ab2a5 100644 --- a/src/mlpack/tests/lsh_test.cpp +++ b/src/mlpack/tests/lsh_test.cpp @@ -16,7 +16,7 @@ using namespace mlpack; using namespace mlpack::neighbor; /** - * Computes Recall (percent of neighbors found correctly) + * Computes Recall (percent of neighbors found correctly). */ double ComputeRecall( const arma::Mat& lshNeighbors, @@ -31,63 +31,57 @@ double ComputeRecall( /** * Generates a point set of four clusters around (0.5, 0.5), - * (3.5, 0.5), (0.5, 3.5), (3.5, 3.5) + * (3.5, 0.5), (0.5, 3.5), (3.5, 3.5). */ void GetPointset(const size_t N, arma::mat& rdata) { const size_t d = 2; - // Create four clusters of points - arma::mat C1(d, N / 4, arma::fill::randu); - arma::mat C2(d, N / 4, arma::fill::randu); - arma::mat C3(d, N / 4, arma::fill::randu); - arma::mat C4(d, N / 4, arma::fill::randu); + // Create four clusters of points. + arma::mat c1(d, N / 4, arma::fill::randu); + arma::mat c2(d, N / 4, arma::fill::randu); + arma::mat c3(d, N / 4, arma::fill::randu); + arma::mat c4(d, N / 4, arma::fill::randu); arma::colvec offset1; - offset1 - <<0< lshTest(rdata, projections, - hashWidth, secondHashSize, bucketSize); + LSHSearch<> lshTest(rdata, projections, hashWidth, secondHashSize, + bucketSize); arma::Mat neighbors; arma::mat distances; lshTest.Search(qdata, k, neighbors, distances); - // test query 1 + // Test query 1. size_t q; - for (size_t j = 0; j < k; ++j) //for each neighbor + for (size_t j = 0; j < k; ++j) // For each neighbor. { - if (neighbors(j, 0) == N || neighbors(j, 1) == N) //neighbor not found, ignore + // If the neighbor is not found, ignore the point. + if (neighbors(j, 0) == N || neighbors(j, 1) == N) continue; - //query 1 is in cluster 3, which under this projection was merged with - //cluster 4. Clusters 3 and 4 have points 20:39, so only neighbors among - //those should be found + // Query 1 is in cluster 3, which under this projection was merged with + // cluster 4. Clusters 3 and 4 have points 20:39, so only neighbors among + //those should be found. q = 0; - BOOST_REQUIRE(neighbors(j, q) >= N / 2); - - //query 2 is in cluster 2, which under this projection was merged with - //cluster 1. Clusters 1 and 2 have points 0:19, so only neighbors among - //those should be found - q = 1; - BOOST_REQUIRE(neighbors(j, q) < N / 2); + BOOST_REQUIRE_GE(neighbors(j, q), N / 2); + // Query 2 is in cluster 2, which under this projection was merged with + // cluster 1. Clusters 1 and 2 have points 0:19, so only neighbors among + // those should be found. + q = 1; + BOOST_REQUIRE_LT(neighbors(j, q), N / 2); } } - /** - * Test: This is a deterministic test that projects 2-di points to the plane. + * Test: This is a deterministic test that projects 2-d points to the plane. * The reference set contains 4 well-separated clusters that should not merge. * * We create two queries, each one belonging in one cluster (q1 in cluster 3 * located around (0, 0) and q2 in cluster 2 located around (3, 3). The test is - * a success if, after the projection, q1 should have neighbors in C3 and q2 - * in C2. + * a success if, after the projection, q1 should have neighbors in c3 and q2 + * in c2. */ BOOST_AUTO_TEST_CASE(DeterministicNoMerge) { @@ -448,51 +440,45 @@ BOOST_AUTO_TEST_CASE(DeterministicNoMerge) GetPointset(N, rdata); GetQueries(qdata); - const int k = N / 2; const double hashWidth = 1; const int secondHashSize = 99901; const int bucketSize = 500; - //1 table, with one projection to axis 1 + // 1 table, with one projection to axis 1. arma::cube projections(2, 2, 1); projections(0, 0, 0) = 0; projections(1, 0, 0) = 1; projections(0, 1, 0) = 1; projections(1, 1, 0) = 0; - LSHSearch<> lshTest(rdata, projections, - hashWidth, secondHashSize, bucketSize); + LSHSearch<> lshTest(rdata, projections, hashWidth, secondHashSize, + bucketSize); arma::Mat neighbors; arma::mat distances; lshTest.Search(qdata, k, neighbors, distances); - // test query 1 + // Test query 1. size_t q; - for (size_t j = 0; j < k; ++j) //for each neighbor + for (size_t j = 0; j < k; ++j) // For each neighbor. { - - //neighbor not found, ignore + // If the neighbor is not found, ignore the point. if (neighbors(j, 0) == N || neighbors(j, 1) == N) continue; - //query 1 is in cluster 3, which is points 20:29 + // Query 1 is in cluster 3, which is points 20:29. q = 0; - BOOST_REQUIRE( - neighbors(j, q) < 3 * N / 4 && - neighbors(j, q) >= N / 2 - ); + BOOST_REQUIRE_LT(neighbors(j, q), 3 * N / 4); + BOOST_REQUIRE_GE(neighbors(j, q), N / 2); - //query 2 is in cluster 2, which is points 10:19 + // Query 2 is in cluster 2, which is points 10:19. q = 1; - BOOST_REQUIRE( - neighbors(j, q) < N / 2 && - neighbors(j, q) >= N / 4 - ); + BOOST_REQUIRE_LT(neighbors(j, q), N / 2); + BOOST_REQUIRE_GE(neighbors(j, q), N / 4); } - } + BOOST_AUTO_TEST_CASE(LSHTrainTest) { // This is a not very good test that simply checks that the re-trained LSH From 8d7e5db0bed8fc236407bdc5dee00d716d72a5ab Mon Sep 17 00:00:00 2001 From: Marcus Edel Date: Tue, 14 Jun 2016 16:19:52 +0200 Subject: [PATCH 32/34] Specify the nuget boost package version. --- .appveyor.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 9c9fcbbea7..abbfb47be6 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,12 +4,12 @@ environment: configuration: Release os: Visual Studio 2015 install: - - ps: nuget install boost -o "${env:APPVEYOR_BUILD_FOLDER}" - - ps: nuget install boost_unit_test_framework-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" - - ps: nuget install boost_program_options-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" - - ps: nuget install boost_random-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" - - ps: nuget install boost_serialization-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" - - ps: nuget install boost_math_c99-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" + - ps: nuget install boost -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0 + - ps: nuget install boost_unit_test_framework-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0 + - ps: nuget install boost_program_options-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0 + - ps: nuget install boost_random-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0 + - ps: nuget install boost_serialization-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0 + - ps: nuget install boost_math_c99-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0 - ps: nuget install OpenBLAS -o "${env:APPVEYOR_BUILD_FOLDER}" build_script: - mkdir boost_libs From 9c28c08ea7255721caef85927d74fe27aa1d47a3 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 14 Jun 2016 17:52:19 -0400 Subject: [PATCH 33/34] Allow 0 as a bucketSize option. --- src/mlpack/methods/lsh/lsh_main.cpp | 3 +- src/mlpack/methods/lsh/lsh_search.hpp | 49 +++++++++++++++------- src/mlpack/methods/lsh/lsh_search_impl.hpp | 5 ++- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp index 2894411cec..74126190f4 100644 --- a/src/mlpack/methods/lsh/lsh_main.cpp +++ b/src/mlpack/methods/lsh/lsh_main.cpp @@ -65,7 +65,8 @@ PARAM_DOUBLE("hash_width", "The hash width for the first-level hashing in the " "hash width for its use.", "H", 0.0); PARAM_INT("second_hash_size", "The size of the second level hash table.", "S", 99901); -PARAM_INT("bucket_size", "The size of a bucket in the second level hash.", "B", +PARAM_INT("bucket_size", "The maximum size of a bucket in the second level " + "hash; 0 indicates no limit (so the table can be arbitrarily large!).", "B", 500); PARAM_INT("seed", "Random seed. If 0, 'std::time(NULL)' is used.", "s", 0); diff --git a/src/mlpack/methods/lsh/lsh_search.hpp b/src/mlpack/methods/lsh/lsh_search.hpp index 7cbe1e6a28..ad285eab48 100644 --- a/src/mlpack/methods/lsh/lsh_search.hpp +++ b/src/mlpack/methods/lsh/lsh_search.hpp @@ -50,10 +50,9 @@ class LSHSearch * performing the hashing for details on how the hashing is done. * * @param referenceSet Set of reference points and the set of queries. - * @param numProj Number of projections in each hash table (anything between - * 10-50 might be a decent choice). - * @param numTables Total number of hash tables (anything between 10-20 - * should suffice). + * @param projections Cube of projection tables. For a cube of size (a, b, c) + * we set numProj = a, numTables = c. b is the reference set + * dimensionality. * @param hashWidth The width of hash for every table. If 0 (the default) is * provided, then the hash width is automatically obtained by computing * the average pairwise distance of 25 pairs. This should be a reasonable @@ -61,8 +60,9 @@ class LSHSearch * @param secondHashSize The size of the second hash table. This should be a * large prime number. * @param bucketSize The size of the bucket in the second hash table. This is - * the maximum number of points that can be hashed into single bucket. - * Default values are already provided here. + * the maximum number of points that can be hashed into single bucket. A + * value of 0 indicates that there is no limit (so the second hash table + * can be arbitrarily large---be careful!). */ LSHSearch(const arma::mat& referenceSet, const arma::cube& projections, @@ -76,9 +76,10 @@ class LSHSearch * performing the hashing for details on how the hashing is done. * * @param referenceSet Set of reference points and the set of queries. - * @param projections Cube of projection tables. For a cube of size (a, b, c) - * we set numProj = a, numTables = c. b is the reference set - * dimensionality. + * @param numProj Number of projections in each hash table (anything between + * 10-50 might be a decent choice). + * @param numTables Total number of hash tables (anything between 10-20 + * should suffice). * @param hashWidth The width of hash for every table. If 0 (the default) is * provided, then the hash width is automatically obtained by computing * the average pairwise distance of 25 pairs. This should be a reasonable @@ -86,8 +87,9 @@ class LSHSearch * @param secondHashSize The size of the second hash table. This should be a * large prime number. * @param bucketSize The size of the bucket in the second hash table. This is - * the maximum number of points that can be hashed into single bucket. - * Default values are already provided here. + * the maximum number of points that can be hashed into single bucket. A + * value of 0 indicates that there is no limit (so the second hash table + * can be arbitrarily large---be careful!). */ LSHSearch(const arma::mat& referenceSet, const size_t numProj, @@ -108,9 +110,28 @@ class LSHSearch ~LSHSearch(); /** - * Train the LSH model on the given dataset. If a correct vector is not - * provided, this means building new hash tables. Otherwise, we use the ones - * provided by the user. + * Train the LSH model on the given dataset. If a correctly-sized projection + * cube is not provided, this means building new hash tables. Otherwise, we + * use the projections provided by the user. + * + * @param referenceSet Set of reference points and the set of queries. + * @param numProj Number of projections in each hash table (anything between + * 10-50 might be a decent choice). + * @param numTables Total number of hash tables (anything between 10-20 + * should suffice). + * @param hashWidth The width of hash for every table. If 0 (the default) is + * provided, then the hash width is automatically obtained by computing + * the average pairwise distance of 25 pairs. This should be a reasonable + * upper bound on the nearest-neighbor distance in general. + * @param secondHashSize The size of the second hash table. This should be a + * large prime number. + * @param bucketSize The size of the bucket in the second hash table. This is + * the maximum number of points that can be hashed into single bucket. A + * value of 0 indicates that there is no limit (so the second hash table + * can be arbitrarily large---be careful!). + * @param projections Cube of projection tables. For a cube of size (a, b, c) + * we set numProj = a, numTables = c. b is the reference set + * dimensionality. */ void Train(const arma::mat& referenceSet, const size_t numProj, diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index ac65a86a6c..98acad15da 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -205,8 +205,9 @@ void LSHSearch::Train(const arma::mat& referenceSet, secondHashBinCounts[secondHashVectors[i]]++; // Enforce the maximum bucket size. - secondHashBinCounts.transform([bucketSize](size_t val) - { return std::min(val, bucketSize); }); + const size_t effectiveBucketSize = (bucketSize == 0) ? SIZE_MAX : bucketSize; + secondHashBinCounts.transform([effectiveBucketSize](size_t val) + { return std::min(val, effectiveBucketSize); }); const size_t numRowsInTable = arma::accu(secondHashBinCounts > 0); bucketContentSize.zeros(numRowsInTable); From 4fa39b6ab0baa1428116d0406264b5452e716d06 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 14 Jun 2016 19:44:12 -0400 Subject: [PATCH 34/34] Move back to port 80. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e713dba57..76cff88a99 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ learning researchers. **Download [current stable version (2.0.1)](http://www.mlpack.org/files/mlpack-2.0.1.tar.gz).** -[![Build Status](http://big.mlpack.org:7780/job/mlpack%20-%20git%20commit%20test/badge/icon)](http://big.mlpack.org:7780/job/mlpack%20-%20git%20commit%20test/) Build status +[![Build Status](http://big.mlpack.org/job/mlpack%20-%20git%20commit%20test/badge/icon)](http://big.mlpack.org/job/mlpack%20-%20git%20commit%20test/) Build status 0. Contents -----------